实现 LightOps 运维面板基础功能

This commit is contained in:
2026-05-25 01:13:03 +08:00
commit d3bb9f45a6
84 changed files with 23505 additions and 0 deletions

108
scripts/install-agent.sh Executable file
View File

@@ -0,0 +1,108 @@
#!/usr/bin/env sh
set -eu
SERVER=""
TOKEN=""
BIN_URL=""
VERSION=""
KEEP_CONFIG="0"
SHA256=""
while [ "$#" -gt 0 ]; do
case "$1" in
--server) SERVER="$2"; shift 2 ;;
--token) TOKEN="$2"; shift 2 ;;
--bin-url) BIN_URL="$2"; shift 2 ;;
--version) VERSION="$2"; shift 2 ;;
--sha256) SHA256="$2"; shift 2 ;;
--keep-config) KEEP_CONFIG="1"; shift 1 ;;
*) echo "未知参数:$1" >&2; exit 1 ;;
esac
done
if [ -z "$SERVER" ] || [ -z "$TOKEN" ]; then
echo "用法install-agent.sh --server https://panel.example.com --token TOKEN [--bin-url URL]" >&2
exit 1
fi
if [ "$(id -u)" -ne 0 ]; then
echo "请使用 root 用户运行" >&2
exit 1
fi
install -d -m 0755 /etc/lightops /usr/local/bin
if command -v systemctl >/dev/null 2>&1; then
systemctl stop lightops-agent 2>/dev/null || true
fi
if [ -z "$BIN_URL" ]; then
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
ARCH="$(uname -m)"
SUFFIX=""
if [ -n "$VERSION" ]; then
SUFFIX="-$VERSION"
fi
BIN_URL="$SERVER/downloads/lightops-agent-$OS-$ARCH$SUFFIX"
fi
if [ -n "$BIN_URL" ]; then
TMP_BIN="$(mktemp)"
echo "正在下载 Agent$BIN_URL"
curl -fL --retry 3 --connect-timeout 10 "$BIN_URL" -o "$TMP_BIN"
if [ -n "$SHA256" ]; then
echo "$SHA256 $TMP_BIN" | sha256sum -c -
fi
chmod +x "$TMP_BIN"
if /usr/local/bin/lightops-agent --version >/dev/null 2>&1; then
cp /usr/local/bin/lightops-agent "/usr/local/bin/lightops-agent.bak.$(date +%Y%m%d%H%M%S)" || true
fi
mv "$TMP_BIN" /usr/local/bin/lightops-agent
elif [ ! -x /usr/local/bin/lightops-agent ]; then
echo "未在 /usr/local/bin/lightops-agent 找到 lightops-agent 二进制文件"
echo "请先复制二进制文件,或传入 --bin-url https://.../lightops-agent"
exit 1
fi
if [ "$KEEP_CONFIG" != "1" ] || [ ! -f /etc/lightops/agent.toml ]; then
cat >/etc/lightops/agent.toml <<EOF
server_url = "$SERVER"
token = "$TOKEN"
heartbeat_interval = 30
EOF
chmod 0600 /etc/lightops/agent.toml
fi
cat >/etc/systemd/system/lightops-agent.service <<'EOF'
[Unit]
Description=LightOps Agent
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=300
StartLimitBurst=10
[Service]
Type=simple
ExecStart=/usr/local/bin/lightops-agent --config /etc/lightops/agent.toml
Restart=always
RestartSec=5
KillSignal=SIGINT
TimeoutStopSec=20
User=root
NoNewPrivileges=false
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now lightops-agent
sleep 2
if ! systemctl is-active --quiet lightops-agent; then
echo "Agent 启动失败,最近日志如下:" >&2
journalctl -u lightops-agent -n 80 --no-pager >&2 || true
exit 1
fi
echo "LightOps Agent 已安装并运行"
systemctl status lightops-agent --no-pager

59
scripts/install-server.sh Executable file
View File

@@ -0,0 +1,59 @@
#!/usr/bin/env sh
set -eu
if [ "$(id -u)" -ne 0 ]; then
echo "请使用 root 用户运行" >&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
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"
registration_token_ttl_minutes = 30
task_timeout_secs = 20
EOF
fi
cat >/etc/systemd/system/lightops-server.service <<'EOF'
[Unit]
Description=LightOps Server
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=300
StartLimitBurst=10
[Service]
Type=simple
WorkingDirectory=/opt/lightops
ExecStart=/opt/lightops/lightops-server --config /etc/lightops/server.toml
Restart=always
RestartSec=5
KillSignal=SIGINT
TimeoutStopSec=20
User=root
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now lightops-server
sleep 2
if ! systemctl is-active --quiet lightops-server; then
echo "Server 启动失败,最近日志如下:" >&2
journalctl -u lightops-server -n 80 --no-pager >&2 || true
exit 1
fi
echo "LightOps Server 已安装并运行"

100
scripts/run-local-smoke.ps1 Normal file
View File

@@ -0,0 +1,100 @@
param(
[int]$Port = 18083
)
$ErrorActionPreference = "Stop"
$Root = Resolve-Path (Join-Path $PSScriptRoot "..")
$RunDir = Join-Path $Root "target\run"
New-Item -ItemType Directory -Force -Path $RunDir | Out-Null
$ExistingListener = Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction SilentlyContinue | Select-Object -First 1
if ($ExistingListener) {
$Process = Get-Process -Id $ExistingListener.OwningProcess -ErrorAction SilentlyContinue
$Name = if ($Process) { $Process.ProcessName } else { "未知进程" }
throw "端口 $Port 已被 PID $($ExistingListener.OwningProcess) ($Name) 占用。请换一个端口,例如 -Port $($Port + 1),或先停止该进程。"
}
$Config = Join-Path $RunDir "server-$Port.toml"
$Db = (Join-Path $RunDir "lightops-$Port.db").Replace("\", "/")
@"
bind = "127.0.0.1:$Port"
database_url = "sqlite://${Db}?mode=rwc"
jwt_secret = "local-dev-lightops-secret-please-change"
public_url = "http://127.0.0.1:$Port"
static_dir = "web/dist"
registration_token_ttl_minutes = 30
task_timeout_secs = 20
"@ | Set-Content -Encoding UTF8 -Path $Config
$ServerOut = Join-Path $RunDir "server-$Port.out"
$ServerErr = Join-Path $RunDir "server-$Port.err"
$AgentOut = Join-Path $RunDir "agent-$Port.out"
$AgentErr = Join-Path $RunDir "agent-$Port.err"
$Server = Start-Process `
-FilePath (Join-Path $Root "target\debug\lightops-server.exe") `
-ArgumentList @("--config", $Config) `
-WorkingDirectory $Root `
-PassThru `
-WindowStyle Hidden `
-RedirectStandardOutput $ServerOut `
-RedirectStandardError $ServerErr
Set-Content -Path (Join-Path $RunDir "server-$Port.pid") -Value $Server.Id
Start-Sleep -Seconds 2
if ($Server.HasExited) {
throw "Server 提前退出,退出码 $($Server.ExitCode)。请查看 $ServerOut$ServerErr"
}
$BaseUrl = "http://127.0.0.1:$Port"
$Page = Invoke-WebRequest -UseBasicParsing "$BaseUrl/" -TimeoutSec 10
$InitBody = @{ username = "admin"; password = "LightOps@123456" } | ConvertTo-Json
$Init = Invoke-RestMethod -Method Post -Uri "$BaseUrl/api/auth/init" -ContentType "application/json" -Body $InitBody -TimeoutSec 10
$Jwt = $Init.data.token
$Headers = @{ Authorization = "Bearer $Jwt" }
$TokenBody = @{ name = "本机测试节点"; ttl_minutes = 30 } | ConvertTo-Json
$AgentTokenResp = Invoke-RestMethod -Method Post -Uri "$BaseUrl/api/agent-tokens" -Headers $Headers -ContentType "application/json" -Body $TokenBody -TimeoutSec 10
$AgentToken = $AgentTokenResp.data.token
$AgentConfig = Join-Path $RunDir "agent-$Port.toml"
$Agent = Start-Process `
-FilePath (Join-Path $Root "target\debug\lightops-agent.exe") `
-ArgumentList @("--server", $BaseUrl, "--token", $AgentToken, "--config", $AgentConfig, "--name", "本机测试节点") `
-WorkingDirectory $Root `
-PassThru `
-WindowStyle Hidden `
-RedirectStandardOutput $AgentOut `
-RedirectStandardError $AgentErr
Set-Content -Path (Join-Path $RunDir "agent-$Port.pid") -Value $Agent.Id
Start-Sleep -Seconds 5
if ($Agent.HasExited) {
throw "Agent 提前退出,退出码 $($Agent.ExitCode)。请查看 $AgentOut$AgentErr"
}
$Agents = Invoke-RestMethod -Method Get -Uri "$BaseUrl/api/agents" -Headers $Headers -TimeoutSec 10
$AgentId = $Agents.data[0].id
$Metrics = Invoke-RestMethod -Method Get -Uri "$BaseUrl/api/agents/$AgentId/metrics" -Headers $Headers -TimeoutSec 10
$Files = Invoke-RestMethod -Method Get -Uri "$BaseUrl/api/agents/$AgentId/files?path=." -Headers $Headers -TimeoutSec 10
[pscustomobject]@{
url = $BaseUrl
server_pid = $Server.Id
agent_pid = $Agent.Id
page_status = $Page.StatusCode
init_success = $Init.success
token_created = $AgentTokenResp.success
agent_count = @($Agents.data).Count
agent_id = $AgentId
agent_status = $Agents.data[0].status
metrics_count = @($Metrics.data).Count
file_api_success = $Files.success
file_count = @($Files.data.entries).Count
username = "admin"
password = "LightOps@123456"
} | ConvertTo-Json -Depth 5

View File

@@ -0,0 +1,3 @@
@echo off
python "%~dp0rust_gnu_linker.py" %*
exit /b %errorlevel%

View File

@@ -0,0 +1,44 @@
import os
import shlex
import subprocess
import sys
def expand_args(args):
expanded = []
for arg in args:
if arg.startswith("@") and os.path.exists(arg[1:]):
with open(arg[1:], "r", encoding="utf-8") as file:
content = file.read()
expanded.extend(shlex.split(content, posix=False))
else:
expanded.append(arg)
return expanded
def output_path(args):
for index, arg in enumerate(args):
if arg == "-o" and index + 1 < len(args):
return args[index + 1].strip('"')
return None
def main():
args = sys.argv[1:]
expanded = expand_args(args)
output = output_path(expanded)
result = subprocess.run(["x86_64-w64-mingw32-gcc.exe", *args])
if result.returncode != 0:
return result.returncode
if output and os.path.exists(output):
subprocess.run(["strip.exe", "--strip-debug", output], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
if output.lower().endswith(".exe"):
subprocess.run(["strip.exe", output], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
return 0
if __name__ == "__main__":
raise SystemExit(main())

27
scripts/uninstall-agent.sh Executable file
View File

@@ -0,0 +1,27 @@
#!/usr/bin/env sh
set -eu
if [ "$(id -u)" -ne 0 ]; then
echo "请使用 root 用户运行" >&2
exit 1
fi
PURGE="0"
while [ "$#" -gt 0 ]; do
case "$1" in
--purge) PURGE="1"; shift 1 ;;
*) echo "未知参数:$1" >&2; exit 1 ;;
esac
done
systemctl disable --now lightops-agent 2>/dev/null || true
rm -f /etc/systemd/system/lightops-agent.service
systemctl daemon-reload
rm -f /usr/local/bin/lightops-agent
rm -f /usr/local/bin/lightops-agent.bak.*
if [ "$PURGE" = "1" ]; then
rm -rf /etc/lightops
echo "LightOps Agent 已卸载,配置已清理"
else
echo "LightOps Agent 已卸载,配置保留在 /etc/lightops。需要完全清理请追加 --purge"
fi

131
scripts/update-from-git.sh Executable file
View File

@@ -0,0 +1,131 @@
#!/usr/bin/env bash
set -Eeuo pipefail
REPO_DIR="/opt/lightops"
BRANCH="main"
SERVER_SERVICE="lightops-server"
AGENT_SERVICE="lightops-agent"
LOG_FILE="/var/log/lightops/update.log"
LOCK_FILE="/tmp/lightops-update.lock"
while [[ $# -gt 0 ]]; do
case "$1" in
--repo-dir)
REPO_DIR="${2:?missing --repo-dir value}"
shift 2
;;
--branch)
BRANCH="${2:?missing --branch value}"
shift 2
;;
--server-service)
SERVER_SERVICE="${2:?missing --server-service value}"
shift 2
;;
--agent-service)
AGENT_SERVICE="${2:?missing --agent-service value}"
shift 2
;;
--log-file)
LOG_FILE="${2:?missing --log-file value}"
shift 2
;;
*)
echo "未知参数:$1" >&2
exit 2
;;
esac
done
mkdir -p "$(dirname "$LOG_FILE")"
exec >>"$LOG_FILE" 2>&1
exec 9>"$LOCK_FILE"
if ! flock -n 9; then
echo "[$(date -Is)] 已有更新任务正在执行"
exit 1
fi
if [[ "$(id -u)" -eq 0 ]]; then
SUDO=()
else
SUDO=(sudo -n)
fi
echo "[$(date -Is)] 开始更新 LightOps"
echo "仓库目录:$REPO_DIR"
echo "目标分支:$BRANCH"
cd "$REPO_DIR"
if [[ ! -d .git ]]; then
echo "当前目录不是 Git 仓库"
exit 1
fi
if [[ -n "$(git status --porcelain)" ]]; then
echo "仓库存在未提交改动,为避免覆盖本地修改,已停止更新"
git status --short
exit 1
fi
git fetch origin "$BRANCH"
CURRENT_COMMIT="$(git rev-parse HEAD)"
REMOTE_COMMIT="$(git rev-parse "origin/$BRANCH")"
echo "当前提交:$CURRENT_COMMIT"
echo "远程提交:$REMOTE_COMMIT"
if [[ "$CURRENT_COMMIT" == "$REMOTE_COMMIT" ]]; then
echo "已经是最新版本"
exit 0
fi
git checkout "$BRANCH"
git pull --ff-only origin "$BRANCH"
echo "[$(date -Is)] 构建前端"
cd "$REPO_DIR/web"
if [[ -f package-lock.json ]]; then
npm ci
else
npm install
fi
npm run build
echo "[$(date -Is)] 构建 Rust 二进制"
cd "$REPO_DIR"
cargo build --release -p lightops-server -p lightops-agent
STAMP="$(date +%Y%m%d%H%M%S)"
BACKUP_DIR="$REPO_DIR/target/update-backups/$STAMP"
mkdir -p "$BACKUP_DIR"
backup_if_exists() {
local path="$1"
if [[ -f "$path" ]]; then
cp -a "$path" "$BACKUP_DIR/$(basename "$path")"
fi
}
backup_if_exists "/usr/local/bin/lightops-server"
backup_if_exists "/usr/local/bin/lightops-agent"
backup_if_exists "$REPO_DIR/lightops-server"
backup_if_exists "$REPO_DIR/lightops-agent"
echo "[$(date -Is)] 替换二进制"
"${SUDO[@]}" install -m 755 "$REPO_DIR/target/release/lightops-server" /usr/local/bin/lightops-server
"${SUDO[@]}" install -m 755 "$REPO_DIR/target/release/lightops-agent" /usr/local/bin/lightops-agent
# 兼容旧 service 文件ExecStart=/opt/lightops/lightops-server
install -m 755 "$REPO_DIR/target/release/lightops-server" "$REPO_DIR/lightops-server"
install -m 755 "$REPO_DIR/target/release/lightops-agent" "$REPO_DIR/lightops-agent"
echo "[$(date -Is)] 重启服务"
if systemctl list-unit-files "$AGENT_SERVICE.service" >/dev/null 2>&1; then
"${SUDO[@]}" systemctl restart "$AGENT_SERVICE" || true
fi
if systemctl list-unit-files "$SERVER_SERVICE.service" >/dev/null 2>&1; then
"${SUDO[@]}" systemctl restart "$SERVER_SERVICE"
else
echo "未找到 $SERVER_SERVICE.service请手动重启 Server"
fi
echo "[$(date -Is)] 更新完成"

81
scripts/upgrade-agent.sh Executable file
View File

@@ -0,0 +1,81 @@
#!/usr/bin/env sh
set -eu
SERVER=""
BIN_URL=""
VERSION=""
SHA256=""
while [ "$#" -gt 0 ]; do
case "$1" in
--server) SERVER="$2"; shift 2 ;;
--bin-url) BIN_URL="$2"; shift 2 ;;
--version) VERSION="$2"; shift 2 ;;
--sha256) SHA256="$2"; shift 2 ;;
*) echo "未知参数:$1" >&2; exit 1 ;;
esac
done
if [ "$(id -u)" -ne 0 ]; then
echo "请使用 root 用户运行" >&2
exit 1
fi
if [ -z "$BIN_URL" ]; then
if [ -z "$SERVER" ]; then
if [ -f /etc/lightops/agent.toml ]; then
SERVER="$(awk -F= '/server_url/ { gsub(/[ "]/, "", $2); print $2; exit }' /etc/lightops/agent.toml)"
fi
fi
if [ -z "$SERVER" ]; then
echo "缺少 --server 或 --bin-url" >&2
exit 1
fi
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
ARCH="$(uname -m)"
SUFFIX=""
if [ -n "$VERSION" ]; then
SUFFIX="-$VERSION"
fi
BIN_URL="$SERVER/downloads/lightops-agent-$OS-$ARCH$SUFFIX"
fi
TMP_BIN="$(mktemp)"
echo "正在下载 Agent$BIN_URL"
curl -fL --retry 3 --connect-timeout 10 "$BIN_URL" -o "$TMP_BIN"
if [ -n "$SHA256" ]; then
echo "$SHA256 $TMP_BIN" | sha256sum -c -
fi
chmod +x "$TMP_BIN"
"$TMP_BIN" --version >/dev/null
systemctl stop lightops-agent 2>/dev/null || true
if [ -x /usr/local/bin/lightops-agent ]; then
BACKUP="/usr/local/bin/lightops-agent.bak.$(date +%Y%m%d%H%M%S)"
cp /usr/local/bin/lightops-agent "$BACKUP"
else
BACKUP=""
fi
mv "$TMP_BIN" /usr/local/bin/lightops-agent
if ! systemctl start lightops-agent; then
echo "新版本启动失败,正在回滚" >&2
if [ -n "$BACKUP" ]; then
cp "$BACKUP" /usr/local/bin/lightops-agent
systemctl start lightops-agent || true
fi
exit 1
fi
sleep 2
if ! systemctl is-active --quiet lightops-agent; then
echo "新版本未保持运行,正在回滚" >&2
if [ -n "$BACKUP" ]; then
cp "$BACKUP" /usr/local/bin/lightops-agent
systemctl restart lightops-agent || true
fi
journalctl -u lightops-agent -n 80 --no-pager >&2 || true
exit 1
fi
echo "LightOps Agent 已升级"