From 7d6a06839ccea896e65dace22f949e8bae949063 Mon Sep 17 00:00:00 2001 From: Eeveid <448859157@qq.com> Date: Mon, 25 May 2026 01:28:54 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=B8=BB=E6=8E=A7=E4=B8=80?= =?UTF-8?q?=E9=94=AE=E5=AE=89=E8=A3=85=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 27 +++ scripts/install-server.sh | 337 ++++++++++++++++++++++++++++++++++---- 2 files changed, 335 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 62a82cf..9f20107 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,33 @@ lightops/ - Node.js 20 或更高版本。 - Agent 运行能力优先面向 Linux 主机。 +## 一条命令安装 Server + +在目标 Linux 服务器上使用 root 执行: + +```bash +curl -fsSL https://gitea.kmux.cn/Eeveid/lightOps/raw/branch/main/scripts/install-server.sh | bash +``` + +指定面板访问地址: + +```bash +curl -fsSL https://gitea.kmux.cn/Eeveid/lightOps/raw/branch/main/scripts/install-server.sh | bash -s -- --public-url https://panel.example.com +``` + +安装脚本会自动安装基础依赖、Rust、Node.js,拉取远程仓库,构建前端和 Rust 二进制,写入 `/etc/lightops/server.toml`,注册并启动 `lightops-server` systemd 服务。安装完成后访问 `/init` 初始化管理员。 + +常用参数: + +```bash +--repo Git 仓库地址 +--branch Git 分支,默认 main +--install-dir 安装目录,默认 /opt/lightops +--bind 监听地址,默认 0.0.0.0:8080 +--public-url 面板外部访问地址 +--skip-deps 跳过依赖安装检查 +``` + 构建前端: ```bash diff --git a/scripts/install-server.sh b/scripts/install-server.sh index e092448..4f12348 100755 --- a/scripts/install-server.sh +++ b/scripts/install-server.sh @@ -1,32 +1,282 @@ -#!/usr/bin/env sh -set -eu +#!/usr/bin/env bash +set -Eeuo pipefail -if [ "$(id -u)" -ne 0 ]; then - echo "请使用 root 用户运行" >&2 +REPO_URL="https://gitea.kmux.cn/Eeveid/lightOps.git" +BRANCH="main" +INSTALL_DIR="/opt/lightops" +CONFIG_DIR="/etc/lightops" +BIND_ADDR="0.0.0.0:8080" +PUBLIC_URL="" +SKIP_DEPS="false" +FORCE="false" + +usage() { + cat <<'EOF' +LightOps Server 一键安装脚本 + +用法: + bash install-server.sh [选项] + +选项: + --repo Git 仓库地址,默认 https://gitea.kmux.cn/Eeveid/lightOps.git + --branch Git 分支,默认 main + --install-dir 安装目录,默认 /opt/lightops + --config-dir 配置目录,默认 /etc/lightops + --bind 监听地址,默认 0.0.0.0:8080 + --public-url 面板访问地址,默认根据本机 IP 生成 http://:8080 + --skip-deps 跳过系统依赖、Rust、Node.js 检查安装 + --force 仓库存在未提交改动时仍继续,谨慎使用 + -h, --help 显示帮助 + +示例: + curl -fsSL https://gitea.kmux.cn/Eeveid/lightOps/raw/branch/main/scripts/install-server.sh | bash + curl -fsSL https://gitea.kmux.cn/Eeveid/lightOps/raw/branch/main/scripts/install-server.sh | bash -s -- --public-url https://panel.example.com +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --repo) + REPO_URL="${2:?缺少 --repo 参数值}" + shift 2 + ;; + --branch) + BRANCH="${2:?缺少 --branch 参数值}" + shift 2 + ;; + --install-dir) + INSTALL_DIR="${2:?缺少 --install-dir 参数值}" + shift 2 + ;; + --config-dir) + CONFIG_DIR="${2:?缺少 --config-dir 参数值}" + shift 2 + ;; + --bind) + BIND_ADDR="${2:?缺少 --bind 参数值}" + shift 2 + ;; + --public-url) + PUBLIC_URL="${2:?缺少 --public-url 参数值}" + shift 2 + ;; + --skip-deps) + SKIP_DEPS="true" + shift + ;; + --force) + FORCE="true" + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "未知参数:$1" >&2 + usage >&2 + exit 2 + ;; + esac +done + +if [[ "$(id -u)" -ne 0 ]]; then + echo "请使用 root 用户运行,例如:curl -fsSL ... | bash" >&2 exit 1 fi -install -d /opt/lightops /etc/lightops -chmod 0755 /opt/lightops - -if [ ! -x /opt/lightops/lightops-server ]; then - echo "运行此脚本前,请先将 lightops-server 放到 /opt/lightops/lightops-server" >&2 +if [[ "$(uname -s)" != "Linux" ]]; then + echo "LightOps Server 一键安装当前只支持 Linux 服务器" >&2 exit 1 fi -if [ ! -f /etc/lightops/server.toml ]; then - cat >/etc/lightops/server.toml <<'EOF' -bind = "0.0.0.0:8080" -database_url = "sqlite:///opt/lightops/lightops.db?mode=rwc" -jwt_secret = "请替换为足够长的随机密钥" -public_url = "https://panel.example.com" -static_dir = "/opt/lightops/web" +if ! command -v systemctl >/dev/null 2>&1; then + echo "未检测到 systemd,当前一键安装脚本需要 systemd 管理服务" >&2 + exit 1 +fi + +case "$INSTALL_DIR" in + ""|"/"|"/opt"|"/etc"|"/usr"|"/usr/local") + echo "安装目录过于危险:$INSTALL_DIR" >&2 + exit 1 + ;; +esac + +if [[ "$INSTALL_DIR" == *" "* || "$CONFIG_DIR" == *" "* ]]; then + echo "安装目录和配置目录暂不支持空格" >&2 + exit 1 +fi + +log() { + printf '\033[1;32m[LightOps]\033[0m %s\n' "$*" +} + +warn() { + printf '\033[1;33m[LightOps]\033[0m %s\n' "$*" >&2 +} + +fail() { + printf '\033[1;31m[LightOps]\033[0m %s\n' "$*" >&2 + exit 1 +} + +detect_public_url() { + local port + port="${BIND_ADDR##*:}" + local ip + ip="$(hostname -I 2>/dev/null | awk '{print $1}')" + if [[ -z "$ip" ]]; then + ip="127.0.0.1" + fi + echo "http://${ip}:${port}" +} + +install_packages_apt() { + export DEBIAN_FRONTEND=noninteractive + apt-get update + apt-get install -y ca-certificates curl git build-essential pkg-config sqlite3 +} + +install_packages_generic() { + if command -v apt-get >/dev/null 2>&1; then + install_packages_apt + elif command -v dnf >/dev/null 2>&1; then + dnf install -y ca-certificates curl git gcc gcc-c++ make pkgconf-pkg-config sqlite + elif command -v yum >/dev/null 2>&1; then + yum install -y ca-certificates curl git gcc gcc-c++ make pkgconfig sqlite + elif command -v pacman >/dev/null 2>&1; then + pacman -Sy --noconfirm ca-certificates curl git base-devel pkgconf sqlite + else + fail "未识别系统包管理器,请手动安装 curl、git、C/C++ 构建工具、pkg-config、sqlite3" + fi +} + +node_major() { + node -v 2>/dev/null | sed -E 's/^v([0-9]+).*/\1/' || true +} + +ensure_node() { + local major + major="$(node_major)" + if [[ -n "$major" && "$major" -ge 18 ]] && command -v npm >/dev/null 2>&1; then + log "Node.js 已安装:$(node -v)" + return + fi + if command -v apt-get >/dev/null 2>&1; then + log "安装 Node.js 20" + curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + apt-get install -y nodejs + elif command -v dnf >/dev/null 2>&1; then + dnf install -y nodejs npm + elif command -v yum >/dev/null 2>&1; then + yum install -y nodejs npm + elif command -v pacman >/dev/null 2>&1; then + pacman -Sy --noconfirm nodejs npm + else + fail "无法自动安装 Node.js,请手动安装 Node.js 18 或更高版本" + fi + major="$(node_major)" + if [[ -z "$major" || "$major" -lt 18 ]]; then + fail "Node.js 版本过低,需要 18 或更高版本,当前:$(node -v 2>/dev/null || echo 未安装)" + fi +} + +ensure_rust() { + if command -v cargo >/dev/null 2>&1; then + log "Rust 已安装:$(cargo --version)" + return + fi + log "安装 Rust 稳定版工具链" + curl --proto '=https' --tlsv1.2 -fsSL https://sh.rustup.rs | sh -s -- -y --profile minimal + # shellcheck disable=SC1091 + source "$HOME/.cargo/env" + if ! command -v cargo >/dev/null 2>&1; then + fail "Rust 安装失败,请检查网络或手动安装 rustup" + fi +} + +ensure_deps() { + if [[ "$SKIP_DEPS" == "true" ]]; then + warn "已跳过依赖安装检查" + return + fi + log "安装系统依赖" + install_packages_generic + ensure_node + ensure_rust +} + +prepare_repo() { + log "准备代码仓库:$INSTALL_DIR" + mkdir -p "$(dirname "$INSTALL_DIR")" + if [[ -d "$INSTALL_DIR/.git" ]]; then + cd "$INSTALL_DIR" + if [[ "$FORCE" != "true" && -n "$(git status --porcelain)" ]]; then + fail "安装目录存在未提交改动:$INSTALL_DIR。请先处理改动,或使用 --force" + fi + git remote set-url origin "$REPO_URL" || true + git fetch origin "$BRANCH" + git checkout "$BRANCH" + git pull --ff-only origin "$BRANCH" + elif [[ -e "$INSTALL_DIR" && -n "$(find "$INSTALL_DIR" -mindepth 1 -maxdepth 1 2>/dev/null | head -n 1)" ]]; then + fail "安装目录已存在且不是 Git 仓库:$INSTALL_DIR" + else + rm -rf "$INSTALL_DIR" + git clone --branch "$BRANCH" "$REPO_URL" "$INSTALL_DIR" + fi +} + +build_project() { + log "构建前端" + cd "$INSTALL_DIR/web" + if [[ -f package-lock.json ]]; then + npm ci + else + npm install + fi + npm run build + + log "构建 Server 和 Agent" + cd "$INSTALL_DIR" + cargo build --release -p lightops-server -p lightops-agent + + install -m 0755 "$INSTALL_DIR/target/release/lightops-server" "$INSTALL_DIR/lightops-server" + install -m 0755 "$INSTALL_DIR/target/release/lightops-agent" "$INSTALL_DIR/lightops-agent" + install -m 0755 "$INSTALL_DIR/target/release/lightops-server" /usr/local/bin/lightops-server + install -m 0755 "$INSTALL_DIR/target/release/lightops-agent" /usr/local/bin/lightops-agent +} + +write_config() { + mkdir -p "$CONFIG_DIR" "$INSTALL_DIR" + if [[ -z "$PUBLIC_URL" ]]; then + PUBLIC_URL="$(detect_public_url)" + fi + local jwt_secret + if command -v openssl >/dev/null 2>&1; then + jwt_secret="$(openssl rand -hex 32)" + else + jwt_secret="$(date +%s%N)-$(hostname)-$RANDOM-$RANDOM" + fi + if [[ -f "$CONFIG_DIR/server.toml" ]]; then + log "保留已有配置:$CONFIG_DIR/server.toml" + return + fi + log "生成 Server 配置:$CONFIG_DIR/server.toml" + cat >"$CONFIG_DIR/server.toml" </etc/systemd/system/lightops-server.service <<'EOF' +write_service() { + log "写入 systemd 服务" + cat >/etc/systemd/system/lightops-server.service <&2 - journalctl -u lightops-server -n 80 --no-pager >&2 || true - exit 1 -fi -echo "LightOps Server 已安装并运行" +start_service() { + log "启动 LightOps Server" + systemctl restart lightops-server + sleep 2 + if ! systemctl is-active --quiet lightops-server; then + journalctl -u lightops-server -n 120 --no-pager >&2 || true + fail "LightOps Server 启动失败" + fi +} + +print_result() { + local port + port="${BIND_ADDR##*:}" + cat <