211 lines
4.8 KiB
Bash
211 lines
4.8 KiB
Bash
#!/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 <<EOF
|
|
Usage: install.sh [options]
|
|
|
|
Options:
|
|
--install-dir <dir> Install directory, default: ${INSTALL_DIR}
|
|
--service-name <name> systemd service name, default: ${SERVICE_NAME}
|
|
--run-user <user> Runtime user, default: ${RUN_USER}
|
|
--package-dir <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" <<EOF
|
|
[Unit]
|
|
Description=SingBox GoPanel API
|
|
After=network-online.target
|
|
Wants=network-online.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=${RUN_USER}
|
|
WorkingDirectory=${INSTALL_DIR}
|
|
EnvironmentFile=-${INSTALL_DIR}/.env
|
|
ExecStart=${INSTALL_DIR}/${BIN_NAME}
|
|
Restart=always
|
|
RestartSec=5
|
|
LimitNOFILE=1048576
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
systemctl daemon-reload
|
|
systemctl enable "${SERVICE_NAME}"
|
|
systemctl restart "${SERVICE_NAME}"
|
|
}
|
|
|
|
print_summary() {
|
|
cat <<EOF
|
|
|
|
Install complete.
|
|
|
|
Package dir: ${PACKAGE_DIR}
|
|
Install directory: ${INSTALL_DIR}
|
|
Binary: ${INSTALL_DIR}/${BIN_NAME}
|
|
Frontend: ${INSTALL_DIR}/frontend
|
|
Docs: ${INSTALL_DIR}/docs
|
|
Service name: ${SERVICE_NAME}
|
|
|
|
Next steps:
|
|
1. Edit ${INSTALL_DIR}/.env if needed
|
|
2. Check service status: systemctl status ${SERVICE_NAME}
|
|
3. Tail logs: journalctl -u ${SERVICE_NAME} -f
|
|
EOF
|
|
}
|
|
|
|
main() {
|
|
parse_args "$@"
|
|
if [[ ! -f "${PACKAGE_DIR}/${BIN_NAME}" && -f "${SCRIPT_DIR}/../${BIN_NAME}" ]]; then
|
|
PACKAGE_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
fi
|
|
ensure_root
|
|
ensure_run_user
|
|
ensure_package_layout
|
|
prepare_dirs
|
|
copy_runtime_files
|
|
chown -R "${RUN_USER}:${RUN_USER}" "${INSTALL_DIR}" || true
|
|
chmod +x "${INSTALL_DIR}/${BIN_NAME}" || true
|
|
install_service
|
|
print_summary
|
|
}
|
|
|
|
main "$@"
|