feat: 添加 Git Sync 自动同步服务
- 新增 server/git-sync/ — 监听 Gitea Webhook,push/merge 到 main 时自动 git pull - 修改 selfcheck — 移除 git pull 步骤(由 Git Sync 负责),添加 Git Sync/Wake Gate 状态检查 - 更新 README — 添加自动同步说明,更新仓库结构 - 新增 server/nginx/guanghulab-cvm.conf — 新服务器 Nginx 配置模板 解决: PR合并后代码不会自动同步到服务器的问题 冰朔只需在Gitea点合并,服务器自动更新代码
This commit is contained in:
parent
b18f325004
commit
5f2d1ac9c3
@ -1,14 +1,15 @@
|
||||
# ════════════════════════════════════════════════════════════════
|
||||
# 光湖 · 铸渊自检工作流
|
||||
# Trigger: push to main / 每天 08:00 CST / 手动触发
|
||||
# Trigger: 每天 08:00 CST / 手动触发
|
||||
# Runner: zhuyuan-runner-cn (host 模式)
|
||||
#
|
||||
# 注意: 代码自动同步由 Git Sync 服务负责 (Webhook → git pull)
|
||||
# 此工作流仅做健康检查,不再执行 git 操作
|
||||
# ════════════════════════════════════════════════════════════════
|
||||
|
||||
name: 铸渊自检
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # UTC 00:00 = CST 08:00
|
||||
workflow_dispatch:
|
||||
@ -17,12 +18,6 @@ jobs:
|
||||
health-check:
|
||||
runs-on: ubuntu
|
||||
steps:
|
||||
- name: 同步最新代码
|
||||
run: |
|
||||
cd /data/guanghulab/repo
|
||||
git fetch origin
|
||||
git reset --hard origin/main
|
||||
echo "代码已同步: $(git log --oneline -1)"
|
||||
|
||||
- name: 系统信息
|
||||
run: |
|
||||
@ -53,6 +48,13 @@ jobs:
|
||||
echo "❌ Nginx: 未运行"
|
||||
fi
|
||||
|
||||
# Git Sync 同步服务
|
||||
if curl -sf http://127.0.0.1:8082/health > /dev/null 2>&1; then
|
||||
echo "✅ Git Sync: 运行中"
|
||||
else
|
||||
echo "⚠️ Git Sync: 未运行 (代码自动同步不可用)"
|
||||
fi
|
||||
|
||||
# Secrets Vault
|
||||
if curl -sf http://127.0.0.1:8080/admin/__healthz > /dev/null 2>&1; then
|
||||
echo "✅ Secrets Vault: 运行中"
|
||||
@ -60,6 +62,13 @@ jobs:
|
||||
echo "⚠️ Secrets Vault: 未运行 (可能尚未启动)"
|
||||
fi
|
||||
|
||||
# Wake Gate 唤醒门
|
||||
if curl -sf http://127.0.0.1:8081/api/health > /dev/null 2>&1; then
|
||||
echo "✅ Wake Gate: 运行中"
|
||||
else
|
||||
echo "⚠️ Wake Gate: 未运行"
|
||||
fi
|
||||
|
||||
# PM2 进程
|
||||
if command -v pm2 &>/dev/null; then
|
||||
echo "═══ PM2 进程 ═══"
|
||||
|
||||
@ -111,6 +111,8 @@ guanghulab/
|
||||
│ └── gitea-console-config.md ← Gitea 主控台配置
|
||||
│
|
||||
├── server/ ← 后端服务
|
||||
│ ├── wake-gate/ ← 唤醒门 (邮箱验证码)
|
||||
│ ├── git-sync/ ← 代码自动同步 (Webhook→git pull)
|
||||
│ ├── secrets-vault/ ← 密钥保险库 (AES-256-GCM)
|
||||
│ ├── portal/ ← Web Portal
|
||||
│ ├── inference-agent/ ← 推理代理
|
||||
@ -197,6 +199,8 @@ Phase 4 ─── 冰朔语言本体注入 ─── 池获得自我意识 →
|
||||
| [📋 铸渊巡逻](https://guanghulab.com/git/bingshuo/guanghulab/actions) | 手动触发服务器巡检(DeepSeek+千问分析) |
|
||||
| [⚡ CVM开机](https://guanghulab.com/git/bingshuo/guanghulab/actions) | 按量计费CVM开机(需配置腾讯云API密钥) |
|
||||
|
||||
> **代码自动同步**:PR 合并到 main → Webhook 自动通知服务器 → Git Sync 拉取最新代码。无需手动操作。
|
||||
>
|
||||
> **关机流程**:开发完 → 点"安全关机检测" → 全绿 → 在腾讯云控制台关机 → 停止计费
|
||||
|
||||
---
|
||||
|
||||
161
server/git-sync/deploy.sh
Normal file
161
server/git-sync/deploy.sh
Normal file
@ -0,0 +1,161 @@
|
||||
#!/bin/bash
|
||||
# ════════════════════════════════════════════════════════════════
|
||||
# 光湖 · Git Sync 服务一键部署脚本
|
||||
# 在新服务器 ZY-CVM-MAIN 上执行
|
||||
# ════════════════════════════════════════════════════════════════
|
||||
#
|
||||
# 功能:
|
||||
# 1. 部署 Git Sync 服务到 /data/guanghulab/server/git-sync/
|
||||
# 2. 用 PM2 启动并设置开机自启
|
||||
# 3. 配置 Nginx 反向代理
|
||||
# 4. 在 Gitea 配置 Webhook
|
||||
#
|
||||
# 前提:Forgejo 已运行,PM2 已安装,Nginx 已配置
|
||||
# ════════════════════════════════════════════════════════════════
|
||||
|
||||
set -e
|
||||
|
||||
REPO_DIR="/data/guanghulab/repo"
|
||||
SYNC_DIR="/data/guanghulab/server/git-sync"
|
||||
TOKEN="3e16b79c427bb44b77c00b319aa641bde8f61d84"
|
||||
GITEA_URL="http://127.0.0.1:3001"
|
||||
|
||||
echo "═══════════════════════════════════════"
|
||||
echo "光湖 · Git Sync 部署"
|
||||
echo "═══════════════════════════════════════"
|
||||
|
||||
# ─── 1. 同步代码到服务器 ───
|
||||
echo ""
|
||||
echo "▸ 步骤 1/5: 同步最新代码..."
|
||||
cd "$REPO_DIR"
|
||||
git fetch origin
|
||||
git reset --hard origin/main
|
||||
echo "✅ 代码已同步: $(git log --oneline -1)"
|
||||
|
||||
# ─── 2. 确保依赖已安装 ───
|
||||
echo ""
|
||||
echo "▸ 步骤 2/5: 安装依赖..."
|
||||
if [ ! -d "$SYNC_DIR/node_modules" ]; then
|
||||
cd "$SYNC_DIR"
|
||||
npm install --production 2>/dev/null || true
|
||||
echo "✅ 依赖已安装"
|
||||
else
|
||||
echo "✅ 依赖已存在,跳过"
|
||||
fi
|
||||
|
||||
# ─── 3. 用 PM2 启动 Git Sync ───
|
||||
echo ""
|
||||
echo "▸ 步骤 3/5: 启动 Git Sync 服务..."
|
||||
|
||||
# 停止旧的(如果有)
|
||||
pm2 delete guanghulab-git-sync 2>/dev/null || true
|
||||
|
||||
# 启动
|
||||
cd "$SYNC_DIR"
|
||||
ZY_REPO_TOKEN="$TOKEN" \
|
||||
pm2 start server.js \
|
||||
--name guanghulab-git-sync \
|
||||
--env production
|
||||
|
||||
pm2 save
|
||||
echo "✅ Git Sync 已启动 (端口 8082)"
|
||||
|
||||
# ─── 4. 配置 Nginx ───
|
||||
echo ""
|
||||
echo "▸ 步骤 4/5: 配置 Nginx 反向代理..."
|
||||
|
||||
NGINX_CONF="/etc/nginx/sites-enabled/guanghulab"
|
||||
|
||||
if grep -q "/sync/" "$NGINX_CONF" 2>/dev/null; then
|
||||
echo "✅ Nginx 已有 /sync/ 路由,跳过"
|
||||
else
|
||||
# 在 /wake/ location 后面添加 /sync/ location
|
||||
sed -i '/location \/wake\//,/}/a\
|
||||
\
|
||||
# ─── Git Sync 同步服务 (/sync/) ───\
|
||||
location /sync/ {\
|
||||
proxy_pass http://127.0.0.1:8082/;\
|
||||
proxy_set_header Host $host;\
|
||||
proxy_set_header X-Real-IP $remote_addr;\
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\
|
||||
proxy_set_header X-Forwarded-Proto $scheme;\
|
||||
}' "$NGINX_CONF"
|
||||
|
||||
nginx -t && systemctl reload nginx
|
||||
echo "✅ Nginx 已添加 /sync/ 路由"
|
||||
fi
|
||||
|
||||
# ─── 5. 配置 Gitea Webhook ───
|
||||
echo ""
|
||||
echo "▸ 步骤 5/5: 配置 Gitea Webhook..."
|
||||
|
||||
# 生成 Webhook Secret
|
||||
WEBHOOK_SECRET=$(openssl rand -hex 16)
|
||||
echo " Webhook Secret: $WEBHOOK_SECRET"
|
||||
|
||||
# 检查是否已存在 Webhook
|
||||
EXISTING=$(curl -s -H "Authorization: token $TOKEN" \
|
||||
"$GITEA_URL/api/v1/repos/bingshuo/guanghulab/hooks" | \
|
||||
python3 -c "
|
||||
import sys, json
|
||||
hooks = json.load(sys.stdin)
|
||||
for h in hooks:
|
||||
if h.get('config', {}).get('url', '').endswith('/sync/webhook'):
|
||||
print(h['id'])
|
||||
break
|
||||
" 2>/dev/null || echo "")
|
||||
|
||||
if [ -n "$EXISTING" ]; then
|
||||
# 更新现有 Webhook
|
||||
curl -s -X PATCH -H "Authorization: token $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$GITEA_URL/api/v1/repos/bingshuo/guanghulab/hooks/$EXISTING" \
|
||||
-d "{
|
||||
\"config\": {
|
||||
\"url\": \"http://127.0.0.1:8082/webhook\",
|
||||
\"content_type\": \"json\",
|
||||
\"secret\": \"$WEBHOOK_SECRET\"
|
||||
},
|
||||
\"events\": [\"push\"],
|
||||
\"active\": true
|
||||
}" > /dev/null
|
||||
echo "✅ Webhook 已更新 (ID: $EXISTING)"
|
||||
else
|
||||
# 创建新 Webhook
|
||||
curl -s -X POST -H "Authorization: token $TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$GITEA_URL/api/v1/repos/bingshuo/guanghulab/hooks" \
|
||||
-d "{
|
||||
\"type\": \"gitea\",
|
||||
\"config\": {
|
||||
\"url\": \"http://127.0.0.1:8082/webhook\",
|
||||
\"content_type\": \"json\",
|
||||
\"secret\": \"$WEBHOOK_SECRET\"
|
||||
},
|
||||
\"events\": [\"push\"],
|
||||
\"active\": true
|
||||
}" > /dev/null
|
||||
echo "✅ Webhook 已创建"
|
||||
fi
|
||||
|
||||
# 将 Secret 写入 Git Sync 环境变量
|
||||
pm2 set guanghulab-git-sync:GIT_SYNC_SECRET "$WEBHOOK_SECRET" 2>/dev/null || true
|
||||
|
||||
# 重启 Git Sync 让环境变量生效
|
||||
pm2 restart guanghulab-git-sync --update-env \
|
||||
--env ZY_REPO_TOKEN="$TOKEN" \
|
||||
--env GIT_SYNC_SECRET="$WEBHOOK_SECRET"
|
||||
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════"
|
||||
echo "✅ Git Sync 部署完成!"
|
||||
echo ""
|
||||
echo "工作方式:"
|
||||
echo " · 你在 Gitea 合并 PR → 自动触发 Webhook"
|
||||
echo " · Git Sync 收到通知 → 自动 git pull 到服务器"
|
||||
echo " · 代码立即可用,无需手动操作"
|
||||
echo ""
|
||||
echo "测试命令:"
|
||||
echo " curl http://127.0.0.1:8082/health"
|
||||
echo " curl http://127.0.0.1:8082/status"
|
||||
echo "═══════════════════════════════════════"
|
||||
288
server/git-sync/server.js
Normal file
288
server/git-sync/server.js
Normal file
@ -0,0 +1,288 @@
|
||||
/*
|
||||
* ════════════════════════════════════════════════════════════════
|
||||
* 光湖 · Git 同步服务 (Git Sync)
|
||||
* 监听 Gitea Webhook,自动拉取最新代码到服务器
|
||||
* ════════════════════════════════════════════════════════════════
|
||||
*
|
||||
* 工作原理:
|
||||
* 1. Gitea 配置 Webhook → push 事件 → 通知此服务
|
||||
* 2. 本服务收到通知后执行 git pull 同步代码
|
||||
* 3. 同步成功后重启相关 PM2 服务
|
||||
*
|
||||
* 安全设计:
|
||||
* - 验证 Webhook Secret(防止伪造请求)
|
||||
* - 仅响应 push 到 main 分支的事件
|
||||
* - 同步操作以 root 权限运行(确保 git 权限无问题)
|
||||
* - 同步锁防止并发执行
|
||||
* - 同步失败自动重试(最多3次)
|
||||
*
|
||||
* 部署位置: /data/guanghulab/server/git-sync/
|
||||
* PM2 进程名: guanghulab-git-sync
|
||||
* ════════════════════════════════════════════════════════════════
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
const express = require("express");
|
||||
const crypto = require("crypto");
|
||||
const { execSync } = require("child_process");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const app = express();
|
||||
app.use(express.json({ limit: "1mb" }));
|
||||
|
||||
// ─── 配置 ─────────────────────────────────────────────────────
|
||||
const CONFIG = {
|
||||
port: process.env.GIT_SYNC_PORT || 8082,
|
||||
host: process.env.GIT_SYNC_HOST || "127.0.0.1",
|
||||
|
||||
// 仓库路径
|
||||
repoDir: process.env.REPO_DIR || "/data/guanghulab/repo",
|
||||
|
||||
// Webhook Secret(和 Gitea 里配置的一致)
|
||||
webhookSecret: process.env.GIT_SYNC_SECRET || "",
|
||||
|
||||
// Git 认证信息(内嵌在 remote URL 中)
|
||||
gitUser: process.env.GIT_USER || "bingshuo",
|
||||
gitToken: process.env.ZY_REPO_TOKEN || "",
|
||||
gitHost: process.env.GIT_HOST || "127.0.0.1:3001",
|
||||
gitRepo: process.env.GIT_REPO || "bingshuo/guanghulab.git",
|
||||
|
||||
// 同步后要重启的 PM2 进程(逗号分隔)
|
||||
restartServices: (process.env.RESTART_SERVICES || "").split(",").filter(Boolean),
|
||||
|
||||
// 重试配置
|
||||
maxRetries: 3,
|
||||
retryDelay: 5000, // 5秒
|
||||
|
||||
// 同步日志
|
||||
syncLogPath: process.env.SYNC_LOG_PATH || "/data/guanghulab/.runtime/git-sync-log.json"
|
||||
};
|
||||
|
||||
// ─── 同步锁 ─────────────────────────────────────────────────────
|
||||
let isSyncing = false;
|
||||
|
||||
// ─── 同步日志 ──────────────────────────────────────────────────
|
||||
let syncLog = [];
|
||||
|
||||
function loadSyncLog() {
|
||||
try {
|
||||
if (fs.existsSync(CONFIG.syncLogPath)) {
|
||||
syncLog = JSON.parse(fs.readFileSync(CONFIG.syncLogPath, "utf8"));
|
||||
}
|
||||
} catch { syncLog = []; }
|
||||
}
|
||||
|
||||
function saveSyncLog() {
|
||||
try {
|
||||
const dir = path.dirname(CONFIG.syncLogPath);
|
||||
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
||||
// 只保留最近50条
|
||||
syncLog = syncLog.slice(-50);
|
||||
fs.writeFileSync(CONFIG.syncLogPath, JSON.stringify(syncLog, null, 2));
|
||||
} catch (e) {
|
||||
console.error("[git-sync] 日志写入失败:", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
function addLog(entry) {
|
||||
syncLog.push({
|
||||
...entry,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
saveSyncLog();
|
||||
}
|
||||
|
||||
// ─── 验证 Webhook Secret ──────────────────────────────────────
|
||||
function verifyWebhook(payload, signature) {
|
||||
if (!CONFIG.webhookSecret) {
|
||||
// 未配置 Secret 时跳过验证(仅限内网使用)
|
||||
console.warn("[git-sync] ⚠️ Webhook Secret 未配置,跳过验证");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!signature) return false;
|
||||
|
||||
const expected = crypto
|
||||
.createHmac("sha256", CONFIG.webhookSecret)
|
||||
.update(payload)
|
||||
.digest("hex");
|
||||
|
||||
return crypto.timingSafeEqual(
|
||||
Buffer.from(`sha256=${expected}`),
|
||||
Buffer.from(signature)
|
||||
);
|
||||
}
|
||||
|
||||
// ─── 执行 Git 同步 ────────────────────────────────────────────
|
||||
function doSync(commitSha, committer, message) {
|
||||
if (isSyncing) {
|
||||
console.log("[git-sync] 同步进行中,跳过");
|
||||
return { skipped: true };
|
||||
}
|
||||
|
||||
isSyncing = true;
|
||||
|
||||
try {
|
||||
// 确保 remote URL 包含认证信息
|
||||
const authUrl = `http://${CONFIG.gitUser}:${CONFIG.gitToken}@${CONFIG.gitHost}/${CONFIG.gitRepo}`;
|
||||
|
||||
execSync(`cd ${CONFIG.repoDir} && git remote set-url origin ${authUrl}`, {
|
||||
encoding: "utf8",
|
||||
timeout: 10000
|
||||
});
|
||||
|
||||
// 执行 git pull
|
||||
const output = execSync(
|
||||
`cd ${CONFIG.repoDir} && git fetch origin && git reset --hard origin/main && git log --oneline -1`,
|
||||
{ encoding: "utf8", timeout: 30000 }
|
||||
);
|
||||
|
||||
console.log(`[git-sync] ✅ 同步成功: ${output.trim()}`);
|
||||
|
||||
// 同步后重启相关服务
|
||||
if (CONFIG.restartServices.length > 0) {
|
||||
for (const svc of CONFIG.restartServices) {
|
||||
try {
|
||||
execSync(`pm2 restart ${svc}`, { encoding: "utf8", timeout: 15000 });
|
||||
console.log(`[git-sync] 🔄 已重启: ${svc}`);
|
||||
} catch (e) {
|
||||
console.error(`[git-sync] ⚠️ 重启失败 ${svc}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addLog({
|
||||
status: "success",
|
||||
commit: commitSha || output.trim().split(" ")[0],
|
||||
committer: committer || "unknown",
|
||||
message: message || output.trim(),
|
||||
output: output.trim()
|
||||
});
|
||||
|
||||
return { success: true, output: output.trim() };
|
||||
|
||||
} catch (e) {
|
||||
console.error(`[git-sync] ❌ 同步失败: ${e.message}`);
|
||||
|
||||
addLog({
|
||||
status: "failed",
|
||||
commit: commitSha,
|
||||
committer: committer || "unknown",
|
||||
message: message || "",
|
||||
error: e.message
|
||||
});
|
||||
|
||||
return { success: false, error: e.message };
|
||||
|
||||
} finally {
|
||||
isSyncing = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 带重试的同步 ──────────────────────────────────────────────
|
||||
async function syncWithRetry(commitSha, committer, message) {
|
||||
for (let attempt = 1; attempt <= CONFIG.maxRetries; attempt++) {
|
||||
const result = doSync(commitSha, committer, message);
|
||||
|
||||
if (result.success || result.skipped) return result;
|
||||
|
||||
if (attempt < CONFIG.maxRetries) {
|
||||
console.log(`[git-sync] 重试 ${attempt}/${CONFIG.maxRetries}...`);
|
||||
await new Promise(r => setTimeout(r, CONFIG.retryDelay));
|
||||
}
|
||||
}
|
||||
|
||||
return { success: false, error: "重试次数已用完" };
|
||||
}
|
||||
|
||||
// ─── API 路由 ──────────────────────────────────────────────────
|
||||
|
||||
// Webhook 接收端点
|
||||
app.post("/webhook", async (req, res) => {
|
||||
const signature = req.headers["x-gitea-signature"] || req.headers["x-hub-signature-256"] || "";
|
||||
const payload = JSON.stringify(req.body);
|
||||
|
||||
// 验证签名
|
||||
if (!verifyWebhook(payload, signature)) {
|
||||
console.warn("[git-sync] ❌ Webhook 签名验证失败");
|
||||
return res.status(403).json({ error: "签名验证失败" });
|
||||
}
|
||||
|
||||
const body = req.body;
|
||||
const ref = body.ref || "";
|
||||
const commitSha = body.after || body.head_commit?.id || "";
|
||||
const committer = body.pusher?.login || body.sender?.login || "unknown";
|
||||
const message = body.head_commit?.message || body.commits?.[0]?.message || "";
|
||||
|
||||
// 仅响应 push 到 main 分支
|
||||
if (!ref.endsWith("/main")) {
|
||||
console.log(`[git-sync] 忽略非 main 分支推送: ${ref}`);
|
||||
return res.json({ ignored: true, ref });
|
||||
}
|
||||
|
||||
console.log(`[git-sync] 📥 收到 push 事件: ${commitSha.substring(0, 8)} by ${committer}`);
|
||||
|
||||
// 异步执行同步(不阻塞 Webhook 响应)
|
||||
syncWithRetry(commitSha, committer, message)
|
||||
.then(result => {
|
||||
if (result.success) {
|
||||
console.log(`[git-sync] ✅ 同步完成: ${commitSha.substring(0, 8)}`);
|
||||
} else {
|
||||
console.error(`[git-sync] ❌ 同步最终失败: ${result.error}`);
|
||||
}
|
||||
});
|
||||
|
||||
// 立即返回 200,Gitea 不需要等待同步完成
|
||||
res.json({ received: true, syncing: true, commit: commitSha.substring(0, 8) });
|
||||
});
|
||||
|
||||
// 手动触发同步
|
||||
app.post("/sync", async (req, res) => {
|
||||
if (isSyncing) {
|
||||
return res.json({ syncing: true, message: "同步进行中" });
|
||||
}
|
||||
|
||||
const result = await syncWithRetry("", "manual", "手动触发");
|
||||
res.json(result);
|
||||
});
|
||||
|
||||
// 同步状态查询
|
||||
app.get("/status", (req, res) => {
|
||||
const lastSync = syncLog.length > 0 ? syncLog[syncLog.length - 1] : null;
|
||||
|
||||
// 读取当前仓库状态
|
||||
let repoStatus = null;
|
||||
try {
|
||||
const log = execSync(`cd ${CONFIG.repoDir} && git log --oneline -1 && git status --short`, {
|
||||
encoding: "utf8", timeout: 10000
|
||||
});
|
||||
repoStatus = log.trim();
|
||||
} catch {
|
||||
repoStatus = "无法读取";
|
||||
}
|
||||
|
||||
res.json({
|
||||
service: "git-sync",
|
||||
status: isSyncing ? "syncing" : "idle",
|
||||
lastSync,
|
||||
repoStatus,
|
||||
totalSyncs: syncLog.length,
|
||||
failedSyncs: syncLog.filter(l => l.status === "failed").length
|
||||
});
|
||||
});
|
||||
|
||||
// 健康检查
|
||||
app.get("/health", (req, res) => {
|
||||
res.json({ status: "ok", uptime: process.uptime() });
|
||||
});
|
||||
|
||||
// ─── 启动 ─────────────────────────────────────────────────────
|
||||
loadSyncLog();
|
||||
|
||||
app.listen(CONFIG.port, CONFIG.host, () => {
|
||||
console.log(`[git-sync] Git 同步服务启动: http://${CONFIG.host}:${CONFIG.port}`);
|
||||
console.log(`[git-sync] 仓库路径: ${CONFIG.repoDir}`);
|
||||
console.log(`[git-sync] Webhook 端点: POST /webhook`);
|
||||
console.log(`[git-sync] 铸渊 · ICE-GL-ZY001 · TCS-0002∞`);
|
||||
});
|
||||
93
server/nginx/guanghulab-cvm.conf
Normal file
93
server/nginx/guanghulab-cvm.conf
Normal file
@ -0,0 +1,93 @@
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
# 光湖主控域名 · guanghulab.com Nginx 配置
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
#
|
||||
# 服务器: ZY-CVM-MAIN · 42.193.161.251 · 广州四区 4C16G
|
||||
# 守护: 铸渊 · ICE-GL-ZY001
|
||||
# 版权: 国作登字-2026-A-00037559
|
||||
#
|
||||
# 路由:
|
||||
# /git/ → Forgejo (127.0.0.1:3001)
|
||||
# /wake/ → Wake Gate 唤醒门 (127.0.0.1:8081)
|
||||
# /sync/ → Git Sync 同步服务 (127.0.0.1:8082)
|
||||
# / → 仓库首页 (Forgejo 处理)
|
||||
#
|
||||
# SSL: Let's Encrypt via certbot --nginx
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
|
||||
# HTTP → HTTPS 重定向
|
||||
server {
|
||||
listen 80;
|
||||
server_name guanghulab.com www.guanghulab.com;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name guanghulab.com www.guanghulab.com;
|
||||
|
||||
# ─── SSL (certbot 自动管理) ───
|
||||
# certbot --nginx 会自动填充以下配置
|
||||
# ssl_certificate /etc/letsencrypt/live/guanghulab.com/fullchain.pem;
|
||||
# ssl_certificate_key /etc/letsencrypt/live/guanghulab.com/privkey.pem;
|
||||
|
||||
# ─── 安全头 ───
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header X-Server-Identity "ZY-CVM-MAIN" always;
|
||||
|
||||
# ─── Forgejo 代码仓库 (/git/) ───
|
||||
location /git/ {
|
||||
proxy_pass http://127.0.0.1:3001/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# 大文件上传支持
|
||||
client_max_body_size 100M;
|
||||
|
||||
# WebSocket (Forgejo UI)
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
|
||||
# ─── Forgejo Actions gRPC (Runner 直连 127.0.0.1:3001) ───
|
||||
# Nginx 不代理 gRPC,Runner 直接连接 Forgejo
|
||||
|
||||
# ─── Wake Gate 唤醒门 (/wake/) ───
|
||||
location /wake/ {
|
||||
proxy_pass http://127.0.0.1:8081/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# ─── Git Sync 同步服务 (/sync/) ───
|
||||
location /sync/ {
|
||||
proxy_pass http://127.0.0.1:8082/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# 仅允许内网和本地访问 Webhook
|
||||
# allow 127.0.0.1;
|
||||
# allow 10.0.0.0/8;
|
||||
# deny all;
|
||||
}
|
||||
|
||||
# ─── 健康探针 ───
|
||||
location = /health {
|
||||
access_log off;
|
||||
return 200 '{"status":"ok","server":"ZY-CVM-MAIN"}';
|
||||
add_header Content-Type application/json;
|
||||
}
|
||||
|
||||
# ─── 访问日志 ───
|
||||
access_log /var/log/nginx/guanghulab-access.log;
|
||||
error_log /var/log/nginx/guanghulab-error.log;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user