#!/usr/bin/env bash set -euo pipefail APP_NAME="singbox-gopanel" SERVICE_NAME="singbox-gopanel" INSTALL_DIR="/opt/${APP_NAME}" RUN_USER="${SUDO_USER:-${USER:-root}}" SKIP_SERVICE="false" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PACKAGE_DIR="${SCRIPT_DIR}" BIN_NAME="api" usage() { cat < Install directory, default: ${INSTALL_DIR} --service-name systemd service name, default: ${SERVICE_NAME} --run-user Runtime user, default: ${RUN_USER} --package-dir Package directory, default: ${PACKAGE_DIR} --skip-service Skip systemd service installation -h, --help Show this help Expected package layout: ${BIN_NAME} frontend/ docs/ README.md .env.example (optional) Example: sudo bash ./install.sh --install-dir /opt/singbox-gopanel --run-user root EOF } parse_args() { while [[ $# -gt 0 ]]; do case "$1" in --install-dir) INSTALL_DIR="$2" shift 2 ;; --service-name) SERVICE_NAME="$2" shift 2 ;; --run-user) RUN_USER="$2" shift 2 ;; --package-dir) PACKAGE_DIR="$2" shift 2 ;; --skip-service) SKIP_SERVICE="true" shift ;; -h|--help) usage exit 0 ;; *) echo "error: unknown option: $1" >&2 usage exit 1 ;; esac done } ensure_root() { if [[ "${EUID}" -ne 0 ]]; then echo "error: please run as root or via sudo" >&2 exit 1 fi } ensure_run_user() { if ! id "${RUN_USER}" >/dev/null 2>&1; then echo "error: run user does not exist: ${RUN_USER}" >&2 exit 1 fi } ensure_package_layout() { if [[ ! -f "${PACKAGE_DIR}/${BIN_NAME}" ]]; then echo "error: binary not found: ${PACKAGE_DIR}/${BIN_NAME}" >&2 exit 1 fi if [[ ! -d "${PACKAGE_DIR}/frontend" ]]; then echo "error: frontend directory not found: ${PACKAGE_DIR}/frontend" >&2 exit 1 fi if [[ ! -d "${PACKAGE_DIR}/docs" ]]; then echo "error: docs directory not found: ${PACKAGE_DIR}/docs" >&2 exit 1 fi } prepare_dirs() { mkdir -p "${INSTALL_DIR}" mkdir -p "${INSTALL_DIR}/frontend" mkdir -p "${INSTALL_DIR}/docs" } copy_runtime_files() { echo "copying package files into ${INSTALL_DIR}..." install -m 0755 "${PACKAGE_DIR}/${BIN_NAME}" "${INSTALL_DIR}/${BIN_NAME}" rm -rf "${INSTALL_DIR}/frontend" mkdir -p "${INSTALL_DIR}/frontend" cp -R "${PACKAGE_DIR}/frontend/." "${INSTALL_DIR}/frontend/" rm -rf "${INSTALL_DIR}/docs" mkdir -p "${INSTALL_DIR}/docs" cp -R "${PACKAGE_DIR}/docs/." "${INSTALL_DIR}/docs/" if [[ -f "${PACKAGE_DIR}/README.md" ]]; then cp -f "${PACKAGE_DIR}/README.md" "${INSTALL_DIR}/README.md" fi if [[ -f "${PACKAGE_DIR}/.env.example" ]]; then cp -f "${PACKAGE_DIR}/.env.example" "${INSTALL_DIR}/.env.example" fi if [[ -f "${PACKAGE_DIR}/.env" && ! -f "${INSTALL_DIR}/.env" ]]; then cp -f "${PACKAGE_DIR}/.env" "${INSTALL_DIR}/.env" fi if [[ ! -f "${INSTALL_DIR}/.env" && -f "${INSTALL_DIR}/.env.example" ]]; then cp -f "${INSTALL_DIR}/.env.example" "${INSTALL_DIR}/.env" echo "created ${INSTALL_DIR}/.env from .env.example" fi } install_service() { if [[ "${SKIP_SERVICE}" == "true" ]]; then echo "skip service enabled" return fi if ! command -v systemctl >/dev/null 2>&1; then echo "systemctl not found, skipping service installation" return fi cat >"/etc/systemd/system/${SERVICE_NAME}.service" <