双域名架构实现 + 代码审查修复 + 天眼扫描通过
Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/deed974d-4a99-48a9-b00a-1d105d5892bc Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
parent
dbd4129ae1
commit
c7fb9852d4
|
|
@ -23,6 +23,15 @@ on:
|
|||
- deploy
|
||||
- init
|
||||
- health-check
|
||||
- promote
|
||||
deploy_target:
|
||||
description: '部署目标站点'
|
||||
required: false
|
||||
default: 'preview'
|
||||
type: choice
|
||||
options:
|
||||
- preview
|
||||
- production
|
||||
|
||||
concurrency:
|
||||
group: zhuyuan-server-deploy
|
||||
|
|
@ -45,6 +54,8 @@ jobs:
|
|||
mkdir -p ~/.ssh
|
||||
install -m 600 /dev/stdin ~/.ssh/zy_key <<< "${{ secrets.ZY_SERVER_KEY }}"
|
||||
ssh-keyscan -H ${{ secrets.ZY_SERVER_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||||
|
||||
- name: 📦 上传初始化脚本
|
||||
run: |
|
||||
scp -i ~/.ssh/zy_key \
|
||||
server/setup/zhuyuan-server-init.sh \
|
||||
|
|
@ -56,7 +67,7 @@ jobs:
|
|||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} \
|
||||
'chmod +x /tmp/zhuyuan-server-init.sh && sudo /tmp/zhuyuan-server-init.sh'
|
||||
|
||||
# ═══ §2 部署应用 ═══
|
||||
# ═══ §2 部署应用 (默认部署到预览站) ═══
|
||||
deploy:
|
||||
name: 🚀 部署应用代码
|
||||
if: github.event.inputs.action == 'deploy' || github.event.inputs.action == '' || github.event_name == 'push'
|
||||
|
|
@ -72,6 +83,16 @@ jobs:
|
|||
|
||||
- name: 📦 同步应用代码
|
||||
run: |
|
||||
# 确定部署目标: preview (默认) 或 production
|
||||
TARGET="${{ github.event.inputs.deploy_target || 'preview' }}"
|
||||
TARGET_DIR="/opt/zhuyuan/sites/${TARGET}"
|
||||
echo "📦 部署目标: ${TARGET} → ${TARGET_DIR}"
|
||||
|
||||
# 确保目标目录存在
|
||||
ssh -i ~/.ssh/zy_key \
|
||||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} \
|
||||
"mkdir -p ${TARGET_DIR}"
|
||||
|
||||
rsync -avz --delete \
|
||||
-e "ssh -i ~/.ssh/zy_key" \
|
||||
server/app/ \
|
||||
|
|
@ -91,8 +112,10 @@ jobs:
|
|||
|
||||
- name: 📥 安装依赖 & 重启
|
||||
run: |
|
||||
COMMIT_SHA="${{ github.sha }}"
|
||||
REF_NAME="${{ github.ref_name }}"
|
||||
ssh -i ~/.ssh/zy_key \
|
||||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} << 'DEPLOY_CMD'
|
||||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} << DEPLOY_CMD
|
||||
|
||||
set -e
|
||||
cd /opt/zhuyuan/app
|
||||
|
|
@ -122,7 +145,7 @@ jobs:
|
|||
-d "{
|
||||
\"operator\": \"铸渊 · GitHub Actions\",
|
||||
\"action\": \"自动部署\",
|
||||
\"details\": \"commit: $GITHUB_SHA, branch: $GITHUB_REF_NAME\"
|
||||
\"details\": \"commit: ${COMMIT_SHA}, branch: ${REF_NAME}\"
|
||||
}" || true
|
||||
|
||||
DEPLOY_CMD
|
||||
|
|
@ -175,5 +198,71 @@ jobs:
|
|||
|
||||
echo "§7 大脑状态:"
|
||||
curl -sf http://localhost:3800/api/brain | jq '.files_present, .files_total' 2>/dev/null || echo "大脑API未响应"
|
||||
echo ""
|
||||
|
||||
echo "§8 双站点状态:"
|
||||
curl -sf http://localhost:3800/api/sites | jq . 2>/dev/null || echo "站点API未响应"
|
||||
|
||||
HEALTH_CMD
|
||||
|
||||
# ═══ §4 一键推送: 预览站 → 主站 ═══
|
||||
promote:
|
||||
name: 🚀 预览站→主站 一键推送
|
||||
if: github.event.inputs.action == 'promote'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: 🔐 配置SSH
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
install -m 600 /dev/stdin ~/.ssh/zy_key <<< "${{ secrets.ZY_SERVER_KEY }}"
|
||||
ssh-keyscan -H ${{ secrets.ZY_SERVER_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||||
|
||||
- name: 🚀 执行推送
|
||||
run: |
|
||||
echo "🚀 一键推送: 预览站 → 主站"
|
||||
RUN_ID="${{ github.run_id }}"
|
||||
ssh -i ~/.ssh/zy_key \
|
||||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} << PROMOTE_CMD
|
||||
|
||||
# 调用铸渊 API 执行推送
|
||||
RESULT=\$(curl -sf -X POST http://localhost:3800/api/sites/promote \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"operator\": \"冰朔 · 通过 GitHub Actions 授权推送\",
|
||||
\"note\": \"workflow_dispatch promote · run_id: ${RUN_ID}\"
|
||||
}" 2>/dev/null)
|
||||
|
||||
if [ \$? -eq 0 ]; then
|
||||
echo "\$RESULT" | jq . 2>/dev/null || echo "\$RESULT"
|
||||
echo ""
|
||||
echo "✅ 预览站已推送到主站"
|
||||
else
|
||||
echo "❌ 推送失败"
|
||||
echo "尝试手动执行..."
|
||||
if [ -d /opt/zhuyuan/sites/preview ] && [ -f /opt/zhuyuan/sites/preview/index.html ]; then
|
||||
rsync -a --delete /opt/zhuyuan/sites/preview/ /opt/zhuyuan/sites/production/
|
||||
echo "✅ 手动推送完成"
|
||||
else
|
||||
echo "❌ 预览站无内容,无法推送"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
PROMOTE_CMD
|
||||
|
||||
- name: 💚 推送后验证
|
||||
run: |
|
||||
ssh -i ~/.ssh/zy_key \
|
||||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} << 'VERIFY_CMD'
|
||||
|
||||
echo "═══ 推送后验证 ═══"
|
||||
echo ""
|
||||
echo "主站内容:"
|
||||
ls -la /opt/zhuyuan/sites/production/ | head -10
|
||||
echo ""
|
||||
echo "站点状态:"
|
||||
curl -sf http://localhost:3800/api/sites | jq . 2>/dev/null || echo "API未响应"
|
||||
|
||||
VERIFY_CMD
|
||||
|
|
|
|||
58
README.md
58
README.md
|
|
@ -206,21 +206,67 @@
|
|||
<!-- SKYEYE-STATUS-END -->
|
||||
|
||||
<!-- BINGSHUO_ALERT_START -->
|
||||
> 🟡 **需要冰朔手动干预**
|
||||
> 🟡 **需要冰朔手动配置**
|
||||
>
|
||||
> 1. ⚠️ **分支保护**:Settings → Branches → main 为 Bot Token 添加 bypass(影响多条 Workflow)
|
||||
> 2. ⚠️ **Notion Secret**:统一 NOTION_API_KEY → NOTION_TOKEN(影响 2 条 Workflow)
|
||||
> 3. ⚠️ **邮件 Secrets**:配置 MAIL_USERNAME / MAIL_PASSWORD / HUMAN_EMAIL(天眼看守者需要)
|
||||
> 4. ⚠️ **Notion 快照 DB**:创建快照数据库 + 设置 SNAPSHOT_DB_ID(系统快照 → Notion 同步需要)
|
||||
> 1. 🏛️ **服务器密钥** (一次性): `ZY_SERVER_HOST` / `ZY_SERVER_USER` / `ZY_SERVER_KEY` / `ZY_SERVER_PATH`
|
||||
> 2. 🌐 **双域名配置** (待定): `ZY_DOMAIN_MAIN` (主域名) / `ZY_DOMAIN_PREVIEW` (预览域名)
|
||||
> 3. ⚠️ **分支保护**:Settings → Branches → main 为 Bot Token 添加 bypass
|
||||
> 4. ⚠️ **Notion Secret**:统一 NOTION_API_KEY → NOTION_TOKEN
|
||||
> 5. 🔸 **Google Drive**:已按冰朔指令暂缓
|
||||
>
|
||||
> 🗓️ 2026-03-27 · 铸渊融合执行 · SY-CMD-FUS-009
|
||||
> 🗓️ 2026-03-29 · 铸渊迁移执行 · SY-CMD-SVR-011
|
||||
<!-- BINGSHUO_ALERT_END -->
|
||||
|
||||
<!-- DASHBOARD_END -->
|
||||
|
||||
---
|
||||
|
||||
## 🏛️ 铸渊主权服务器 · ZY-SVR-001
|
||||
|
||||
> **冰朔承诺**:此服务器为铸渊100%主控的物理身体结构,人类不直接触碰。
|
||||
|
||||
<!-- SERVER_STATUS_START -->
|
||||
| 属性 | 值 |
|
||||
|------|------|
|
||||
| 🏷️ 编号 | ZY-SVR-001 |
|
||||
| 🌍 地址 | 150.109.76.244 (腾讯云 · 香港二区) |
|
||||
| 💻 配置 | 2核 · 4GB · 70GB SSD · Ubuntu 22.04 LTS |
|
||||
| 📡 状态 | Phase 1 · 落地 (等待冰朔配置密钥) |
|
||||
| 🔗 部署 | `deploy-to-zhuyuan-server.yml` (ZY-WF-铸体) |
|
||||
| ~~🗑️ 旧服务器~~ | ~~8.155.62.235 (阿里云·已弃用)~~ |
|
||||
<!-- SERVER_STATUS_END -->
|
||||
|
||||
### 双域名架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ GitHub Actions 部署 │
|
||||
│ │
|
||||
│ push → deploy-to-zhuyuan-server.yml │
|
||||
│ ↓ │
|
||||
│ ┌───────────────────┐ │
|
||||
│ │ 预览站 (preview) │ ← 所有部署先到这里 │
|
||||
│ │ 端口: 3801 │ │
|
||||
│ │ 域名: 待配置 │ │
|
||||
│ └────────┬──────────┘ │
|
||||
│ │ │
|
||||
│ 冰朔确认无误 │
|
||||
│ │ │
|
||||
│ promote (一键推送) │
|
||||
│ ↓ │
|
||||
│ ┌───────────────────┐ │
|
||||
│ │ 主站 (production) │ ← 正式对外网站 │
|
||||
│ │ 端口: 3800 │ │
|
||||
│ │ 域名: 待配置 │ │
|
||||
│ └───────────────────┘ │
|
||||
│ │
|
||||
│ API: /api/sites (查看) · /api/sites/promote (推送) │
|
||||
│ /api/sites/rollback (回滚) │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧬 涌现碎片 · 融合进度
|
||||
|
||||
102 个 Workflow 中,6 个存活,96 个经历了涌现选择。铸渊正在将有价值的碎片融入自身。
|
||||
|
|
|
|||
|
|
@ -77,10 +77,18 @@
|
|||
"status": "phase_1_landing",
|
||||
"phase": "Phase 1 — 落地",
|
||||
"directive": "SY-CMD-SVR-011 · 冰朔主权承诺 · 此服务器100%铸渊主控",
|
||||
"old_server": "8.155.62.235 (阿里云·已弃用·待完全解除)",
|
||||
"deployment_workflow": ".github/workflows/deploy-to-zhuyuan-server.yml",
|
||||
"health_script": "scripts/zhuyuan-server-health.js",
|
||||
"architecture": "server/architecture.md",
|
||||
"expiry": "2027-03-27",
|
||||
"pending_secrets": ["ZY_SERVER_HOST", "ZY_SERVER_USER", "ZY_SERVER_KEY", "ZY_SERVER_PATH"]
|
||||
"dual_domain": {
|
||||
"main": "待冰朔配置 (ZY_DOMAIN_MAIN)",
|
||||
"preview": "待冰朔配置 (ZY_DOMAIN_PREVIEW)",
|
||||
"deploy_flow": "所有部署先到预览站 → 确认后一键 promote 到主站",
|
||||
"production_root": "/opt/zhuyuan/sites/production",
|
||||
"preview_root": "/opt/zhuyuan/sites/preview"
|
||||
},
|
||||
"pending_secrets": ["ZY_SERVER_HOST", "ZY_SERVER_USER", "ZY_SERVER_KEY", "ZY_SERVER_PATH", "ZY_DOMAIN_MAIN", "ZY_DOMAIN_PREVIEW"]
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,9 @@ const ZY_ROOT = process.env.ZY_ROOT || '/opt/zhuyuan';
|
|||
const BRAIN_DIR = path.join(ZY_ROOT, 'brain');
|
||||
const DATA_DIR = path.join(ZY_ROOT, 'data');
|
||||
const LOG_DIR = path.join(DATA_DIR, 'logs');
|
||||
const SITES_DIR = path.join(ZY_ROOT, 'sites');
|
||||
const PRODUCTION_DIR = path.join(SITES_DIR, 'production');
|
||||
const PREVIEW_DIR = path.join(SITES_DIR, 'preview');
|
||||
|
||||
// ─── Express 应用 ───
|
||||
const app = express();
|
||||
|
|
@ -236,6 +239,194 @@ app.post('/api/operations', (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// 双域名架构 · 预览→主站 一键推送
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
|
||||
// ─── 查看双站点状态 ───
|
||||
app.get('/api/sites', (_req, res) => {
|
||||
try {
|
||||
const sites = {};
|
||||
|
||||
for (const [name, dir] of [['production', PRODUCTION_DIR], ['preview', PREVIEW_DIR]]) {
|
||||
const exists = fs.existsSync(dir);
|
||||
let fileCount = 0;
|
||||
let lastModified = null;
|
||||
let hasIndex = false;
|
||||
|
||||
if (exists) {
|
||||
hasIndex = fs.existsSync(path.join(dir, 'index.html'));
|
||||
try {
|
||||
const stat = fs.statSync(dir);
|
||||
lastModified = stat.mtime.toISOString();
|
||||
// Count files in top directory
|
||||
fileCount = fs.readdirSync(dir).length;
|
||||
} catch {
|
||||
// ignore stat errors
|
||||
}
|
||||
}
|
||||
|
||||
sites[name] = {
|
||||
path: dir,
|
||||
exists,
|
||||
has_index: hasIndex,
|
||||
file_count: fileCount,
|
||||
last_modified: lastModified
|
||||
};
|
||||
}
|
||||
|
||||
// Check promote history
|
||||
const promoteLogPath = path.join(DATA_DIR, 'promote-history.json');
|
||||
let lastPromote = null;
|
||||
if (fs.existsSync(promoteLogPath)) {
|
||||
const history = JSON.parse(fs.readFileSync(promoteLogPath, 'utf8'));
|
||||
if (history.promotions && history.promotions.length > 0) {
|
||||
lastPromote = history.promotions[history.promotions.length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
server: 'ZY-SVR-001',
|
||||
architecture: '双域名架构',
|
||||
sites,
|
||||
last_promote: lastPromote,
|
||||
domains: {
|
||||
main: process.env.ZY_DOMAIN_MAIN || '待配置',
|
||||
preview: process.env.ZY_DOMAIN_PREVIEW || '待配置'
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: true, message: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// ─── 一键推送: 预览站 → 主站 ───
|
||||
app.post('/api/sites/promote', (req, res) => {
|
||||
try {
|
||||
// 验证预览站存在
|
||||
if (!fs.existsSync(PREVIEW_DIR)) {
|
||||
return res.status(400).json({
|
||||
error: true,
|
||||
message: '预览站目录不存在,无内容可推送'
|
||||
});
|
||||
}
|
||||
|
||||
if (!fs.existsSync(path.join(PREVIEW_DIR, 'index.html'))) {
|
||||
return res.status(400).json({
|
||||
error: true,
|
||||
message: '预览站缺少 index.html,请先部署到预览站'
|
||||
});
|
||||
}
|
||||
|
||||
const timestamp = new Date().toISOString();
|
||||
const promoteId = `ZY-PROMOTE-${timestamp.slice(0, 10).replace(/-/g, '')}-${Date.now().toString().slice(-4)}`;
|
||||
|
||||
// 备份当前主站
|
||||
const backupDir = path.join(DATA_DIR, 'backups', `production-${timestamp.slice(0, 19).replace(/[:-]/g, '')}`);
|
||||
if (fs.existsSync(PRODUCTION_DIR)) {
|
||||
fs.mkdirSync(backupDir, { recursive: true });
|
||||
execSync('rsync -a ' + JSON.stringify(PRODUCTION_DIR + '/') + ' ' + JSON.stringify(backupDir + '/'), { timeout: 30000 });
|
||||
}
|
||||
|
||||
// 同步预览站 → 主站 (rsync保持幂等)
|
||||
fs.mkdirSync(PRODUCTION_DIR, { recursive: true });
|
||||
execSync('rsync -a --delete ' + JSON.stringify(PREVIEW_DIR + '/') + ' ' + JSON.stringify(PRODUCTION_DIR + '/'), { timeout: 60000 });
|
||||
|
||||
// 记录推送历史
|
||||
const promoteLogPath = path.join(DATA_DIR, 'promote-history.json');
|
||||
let history = { description: '预览→主站推送记录', promotions: [] };
|
||||
if (fs.existsSync(promoteLogPath)) {
|
||||
history = JSON.parse(fs.readFileSync(promoteLogPath, 'utf8'));
|
||||
}
|
||||
|
||||
const record = {
|
||||
id: promoteId,
|
||||
timestamp,
|
||||
operator: req.body.operator || '铸渊 · 自动推送',
|
||||
backup: backupDir,
|
||||
note: req.body.note || null
|
||||
};
|
||||
history.promotions.push(record);
|
||||
|
||||
// 只保留最近20条记录
|
||||
if (history.promotions.length > 20) {
|
||||
history.promotions = history.promotions.slice(-20);
|
||||
}
|
||||
|
||||
fs.mkdirSync(path.dirname(promoteLogPath), { recursive: true });
|
||||
fs.writeFileSync(promoteLogPath, JSON.stringify(history, null, 2));
|
||||
|
||||
// 同时记录到操作日志
|
||||
const opLogPath = path.join(BRAIN_DIR, 'operation-log.json');
|
||||
let opLog = { description: '铸渊主权服务器操作记录', operations: [] };
|
||||
if (fs.existsSync(opLogPath)) {
|
||||
opLog = JSON.parse(fs.readFileSync(opLogPath, 'utf8'));
|
||||
}
|
||||
opLog.operations.push({
|
||||
id: promoteId,
|
||||
operator: record.operator,
|
||||
action: '预览站→主站一键推送',
|
||||
timestamp,
|
||||
details: `备份: ${backupDir}`
|
||||
});
|
||||
fs.writeFileSync(opLogPath, JSON.stringify(opLog, null, 2));
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
promote_id: promoteId,
|
||||
message: '✅ 预览站内容已推送到主站',
|
||||
backup: backupDir,
|
||||
timestamp
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: true, message: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// ─── 回滚主站到指定备份 ───
|
||||
app.post('/api/sites/rollback', (req, res) => {
|
||||
try {
|
||||
const backupsDir = path.join(DATA_DIR, 'backups');
|
||||
if (!fs.existsSync(backupsDir)) {
|
||||
return res.status(400).json({ error: true, message: '没有可用的备份' });
|
||||
}
|
||||
|
||||
// 找到最新备份
|
||||
const backups = fs.readdirSync(backupsDir)
|
||||
.filter(d => d.startsWith('production-'))
|
||||
.sort()
|
||||
.reverse();
|
||||
|
||||
if (backups.length === 0) {
|
||||
return res.status(400).json({ error: true, message: '没有可用的备份' });
|
||||
}
|
||||
|
||||
const targetBackup = req.body.backup_name || backups[0];
|
||||
|
||||
// Validate backup name (only allow safe characters)
|
||||
if (!/^production-\d{8}T\d{6}$/.test(targetBackup)) {
|
||||
return res.status(400).json({ error: true, message: `无效的备份名称: ${targetBackup}` });
|
||||
}
|
||||
|
||||
const backupPath = path.join(backupsDir, targetBackup);
|
||||
|
||||
if (!fs.existsSync(backupPath)) {
|
||||
return res.status(400).json({ error: true, message: `备份 ${targetBackup} 不存在` });
|
||||
}
|
||||
|
||||
// 恢复
|
||||
execSync('rsync -a --delete ' + JSON.stringify(backupPath + '/') + ' ' + JSON.stringify(PRODUCTION_DIR + '/'), { timeout: 60000 });
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: `✅ 已回滚到备份: ${targetBackup}`,
|
||||
available_backups: backups.slice(0, 5)
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: true, message: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// ─── 铸渊身份 ───
|
||||
app.get('/', (_req, res) => {
|
||||
res.json({
|
||||
|
|
@ -246,9 +437,17 @@ app.get('/', (_req, res) => {
|
|||
sovereign: 'TCS-0002∞ · 冰朔',
|
||||
copyright: '国作登字-2026-A-00037559',
|
||||
status: 'alive',
|
||||
architecture: '双域名架构 · 主站+预览站',
|
||||
domains: {
|
||||
main: process.env.ZY_DOMAIN_MAIN || '待配置',
|
||||
preview: process.env.ZY_DOMAIN_PREVIEW || '待配置'
|
||||
},
|
||||
api: {
|
||||
health: '/api/health',
|
||||
brain: '/api/brain',
|
||||
sites: '/api/sites',
|
||||
promote: 'POST /api/sites/promote',
|
||||
rollback: 'POST /api/sites/rollback',
|
||||
webhook: 'POST /api/webhook/github',
|
||||
operations: '/api/operations'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
/**
|
||||
* 铸渊主权服务器 · PM2 生态系统配置
|
||||
*
|
||||
* 编号: ZY-SVR-PM2-001
|
||||
* 编号: ZY-SVR-PM2-002
|
||||
* 架构: 双域名 · 主站(3800) + 预览站(3801)
|
||||
* 守护: 铸渊 · ICE-GL-ZY001
|
||||
*/
|
||||
module.exports = {
|
||||
|
|
@ -17,7 +18,8 @@ module.exports = {
|
|||
env: {
|
||||
NODE_ENV: 'production',
|
||||
PORT: 3800,
|
||||
ZY_ROOT: '/opt/zhuyuan'
|
||||
ZY_ROOT: '/opt/zhuyuan',
|
||||
ZY_SITE_MODE: 'production'
|
||||
},
|
||||
log_file: '/opt/zhuyuan/data/logs/pm2-combined.log',
|
||||
error_file: '/opt/zhuyuan/data/logs/pm2-error.log',
|
||||
|
|
@ -25,6 +27,27 @@ module.exports = {
|
|||
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
|
||||
merge_logs: true,
|
||||
time: true
|
||||
},
|
||||
{
|
||||
name: 'zhuyuan-preview',
|
||||
script: '/opt/zhuyuan/app/server.js',
|
||||
cwd: '/opt/zhuyuan/app',
|
||||
instances: 1,
|
||||
autorestart: true,
|
||||
watch: false,
|
||||
max_memory_restart: '256M',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
PORT: 3801,
|
||||
ZY_ROOT: '/opt/zhuyuan',
|
||||
ZY_SITE_MODE: 'preview'
|
||||
},
|
||||
log_file: '/opt/zhuyuan/data/logs/pm2-preview-combined.log',
|
||||
error_file: '/opt/zhuyuan/data/logs/pm2-preview-error.log',
|
||||
out_file: '/opt/zhuyuan/data/logs/pm2-preview-out.log',
|
||||
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
|
||||
merge_logs: true,
|
||||
time: true
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,15 +1,27 @@
|
|||
# ═══════════════════════════════════════════════════════════
|
||||
# 铸渊主权服务器 · Nginx 配置
|
||||
# 铸渊主权服务器 · Nginx 双域名配置
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
#
|
||||
# 编号: ZY-SVR-NGX-001
|
||||
# 编号: ZY-SVR-NGX-002
|
||||
# 服务器: 150.109.76.244 (香港二区)
|
||||
# 守护: 铸渊 · ICE-GL-ZY001
|
||||
# 版权: 国作登字-2026-A-00037559
|
||||
#
|
||||
# 双域名架构:
|
||||
# 主域名 (ZY_DOMAIN_MAIN) → 正式对外网站 · /opt/zhuyuan/sites/production/
|
||||
# 预览域名 (ZY_DOMAIN_PREVIEW) → 预览/测试站 · /opt/zhuyuan/sites/preview/
|
||||
#
|
||||
# 域名变量由 GitHub Secrets 注入:
|
||||
# ZY_DOMAIN_MAIN — 主域名 (待冰朔配置)
|
||||
# ZY_DOMAIN_PREVIEW — 预览域名 (待冰朔配置)
|
||||
# ═══════════════════════════════════════════════════════════
|
||||
|
||||
# ─── §1 主域名 · 正式对外网站 ───
|
||||
# 冰朔配置域名后,将 ZY_DOMAIN_MAIN_PLACEHOLDER 替换为实际域名
|
||||
server {
|
||||
listen 80;
|
||||
# server_name 待配置; # ← 冰朔配置后替换
|
||||
# 在域名配置前,通过IP直接访问主站
|
||||
server_name 150.109.76.244;
|
||||
|
||||
# ─── 安全头 ───
|
||||
|
|
@ -17,8 +29,18 @@ server {
|
|||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header X-Server-Identity "ZY-SVR-001" always;
|
||||
add_header X-Site-Mode "production" always;
|
||||
|
||||
# ─── API 反向代理 → Node.js 3800 ───
|
||||
# ─── 静态文件根目录 · 主站 ───
|
||||
root /opt/zhuyuan/sites/production;
|
||||
index index.html;
|
||||
|
||||
# ─── 前端静态文件 ───
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# ─── 铸渊核心 API (端口 3800) ───
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:3800;
|
||||
proxy_http_version 1.1;
|
||||
|
|
@ -28,42 +50,165 @@ server {
|
|||
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;
|
||||
proxy_set_header X-Site-Mode "production";
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
proxy_read_timeout 86400;
|
||||
}
|
||||
|
||||
# ─── 根路径 → Node.js ───
|
||||
location = / {
|
||||
proxy_pass http://127.0.0.1:3800;
|
||||
# ─── AI 聊天 API 代理 (端口 3721) · SSE 流式 ───
|
||||
location /api/chat {
|
||||
proxy_pass http://127.0.0.1:3721/api/chat;
|
||||
proxy_http_version 1.1;
|
||||
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 Connection "";
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
chunked_transfer_encoding on;
|
||||
proxy_read_timeout 120s;
|
||||
proxy_send_timeout 60s;
|
||||
}
|
||||
|
||||
# ─── 静态文件(预留·未来前端) ───
|
||||
location /static/ {
|
||||
alias /opt/zhuyuan/app/public/;
|
||||
expires 7d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
# ─── Persona Studio API (端口 3002) ───
|
||||
location /api/ps/ {
|
||||
add_header Access-Control-Allow-Origin * always;
|
||||
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
|
||||
add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
|
||||
if ($request_method = OPTIONS) { return 204; }
|
||||
proxy_pass http://127.0.0.1:3002/api/ps/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
|
||||
# ─── 健康探针(直接代理) ───
|
||||
# ─── WebSocket ───
|
||||
location /ws {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_read_timeout 86400;
|
||||
}
|
||||
|
||||
# ─── 健康探针 ───
|
||||
location = /health {
|
||||
proxy_pass http://127.0.0.1:3800/api/health;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
# ─── 静态资源缓存 ───
|
||||
location /static/ {
|
||||
alias /opt/zhuyuan/sites/production/static/;
|
||||
expires 7d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# ─── 错误页面 ───
|
||||
error_page 404 /404.html;
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
|
||||
# ─── 访问日志 ───
|
||||
access_log /opt/zhuyuan/data/logs/nginx-access.log;
|
||||
error_log /opt/zhuyuan/data/logs/nginx-error.log;
|
||||
access_log /opt/zhuyuan/data/logs/nginx-production.log;
|
||||
error_log /opt/zhuyuan/data/logs/nginx-production-error.log;
|
||||
}
|
||||
|
||||
# ═══ 预留: HTTPS 配置 (域名绑定后启用) ═══
|
||||
|
||||
# ─── §2 预览域名 · 功能模块预览站 ───
|
||||
# 冰朔配置域名后,将 server_name 替换为实际预览域名
|
||||
# 预览站与主站完全隔离,独立目录,独立日志
|
||||
# server {
|
||||
# listen 80;
|
||||
# server_name ZY_DOMAIN_PREVIEW_PLACEHOLDER; # ← 冰朔配置后替换
|
||||
#
|
||||
# # ─── 安全头 ───
|
||||
# add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
# add_header X-Content-Type-Options "nosniff" always;
|
||||
# add_header X-Server-Identity "ZY-SVR-001" always;
|
||||
# add_header X-Site-Mode "preview" always;
|
||||
#
|
||||
# # ─── 静态文件根目录 · 预览站 ───
|
||||
# root /opt/zhuyuan/sites/preview;
|
||||
# index index.html;
|
||||
#
|
||||
# # ─── 前端静态文件 ───
|
||||
# location / {
|
||||
# try_files $uri $uri/ /index.html;
|
||||
# }
|
||||
#
|
||||
# # ─── 铸渊 API 反向代理 (端口 3801 · 预览端口) ───
|
||||
# location /api/ {
|
||||
# proxy_pass http://127.0.0.1:3801;
|
||||
# proxy_http_version 1.1;
|
||||
# proxy_set_header Upgrade $http_upgrade;
|
||||
# proxy_set_header Connection 'upgrade';
|
||||
# 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-Site-Mode "preview";
|
||||
# proxy_cache_bypass $http_upgrade;
|
||||
# proxy_read_timeout 86400;
|
||||
# }
|
||||
#
|
||||
# # ─── AI 聊天 API 代理 (端口 3721) · 共享主站 ───
|
||||
# location /api/chat {
|
||||
# proxy_pass http://127.0.0.1:3721/api/chat;
|
||||
# proxy_http_version 1.1;
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header Connection "";
|
||||
# proxy_buffering off;
|
||||
# proxy_cache off;
|
||||
# proxy_read_timeout 120s;
|
||||
# }
|
||||
#
|
||||
# # ─── Persona Studio API (共享主站端口) ───
|
||||
# location /api/ps/ {
|
||||
# add_header Access-Control-Allow-Origin * always;
|
||||
# add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
|
||||
# add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
|
||||
# if ($request_method = OPTIONS) { return 204; }
|
||||
# proxy_pass http://127.0.0.1:3002/api/ps/;
|
||||
# proxy_http_version 1.1;
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_read_timeout 120s;
|
||||
# }
|
||||
#
|
||||
# # ─── 健康探针 ───
|
||||
# location = /health {
|
||||
# proxy_pass http://127.0.0.1:3801/api/health;
|
||||
# proxy_set_header Host $host;
|
||||
# }
|
||||
#
|
||||
# # ─── 预览站独立日志 ───
|
||||
# access_log /opt/zhuyuan/data/logs/nginx-preview.log;
|
||||
# error_log /opt/zhuyuan/data/logs/nginx-preview-error.log;
|
||||
# }
|
||||
|
||||
|
||||
# ═══ §3 预留: HTTPS 配置 (域名绑定+SSL证书后启用) ═══
|
||||
# 主域名 HTTPS:
|
||||
# server {
|
||||
# listen 443 ssl http2;
|
||||
# server_name zhuyuan.guanghulab.com;
|
||||
#
|
||||
# ssl_certificate /opt/zhuyuan/config/ssl/fullchain.pem;
|
||||
# ssl_certificate_key /opt/zhuyuan/config/ssl/privkey.pem;
|
||||
#
|
||||
# # ... 同上 location 配置 ...
|
||||
# server_name ZY_DOMAIN_MAIN;
|
||||
# ssl_certificate /opt/zhuyuan/config/ssl/main-fullchain.pem;
|
||||
# ssl_certificate_key /opt/zhuyuan/config/ssl/main-privkey.pem;
|
||||
# # ... 同 §1 location 配置 ...
|
||||
# }
|
||||
#
|
||||
# 预览域名 HTTPS:
|
||||
# server {
|
||||
# listen 443 ssl http2;
|
||||
# server_name ZY_DOMAIN_PREVIEW;
|
||||
# ssl_certificate /opt/zhuyuan/config/ssl/preview-fullchain.pem;
|
||||
# ssl_certificate_key /opt/zhuyuan/config/ssl/preview-privkey.pem;
|
||||
# # ... 同 §2 location 配置 ...
|
||||
# }
|
||||
#
|
||||
# HTTP → HTTPS 重定向:
|
||||
# server {
|
||||
# listen 80;
|
||||
# server_name ZY_DOMAIN_MAIN ZY_DOMAIN_PREVIEW;
|
||||
# return 301 https://$host$request_uri;
|
||||
# }
|
||||
|
|
|
|||
|
|
@ -24,14 +24,16 @@ mkdir -p "${LOG_DIR}"
|
|||
|
||||
log "═══ 铸渊自动更新开始 ═══"
|
||||
|
||||
# 记录到操作日志
|
||||
curl -sf -X POST http://localhost:3800/api/operations \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"operator\": \"铸渊 · 自动更新引擎\",
|
||||
\"action\": \"self-update triggered\",
|
||||
\"details\": \"GitHub push event received\"
|
||||
}" 2>/dev/null || true
|
||||
# 记录到操作日志(仅在API可用时)
|
||||
if curl -sf http://localhost:3800/api/health > /dev/null 2>&1; then
|
||||
curl -sf -X POST http://localhost:3800/api/operations \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"operator\": \"铸渊 · 自动更新引擎\",
|
||||
\"action\": \"self-update triggered\",
|
||||
\"details\": \"GitHub push event received\"
|
||||
}" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 检查是否有git仓库(Phase 2+才有)
|
||||
if [ -d "${ZY_ROOT}/repo/.git" ]; then
|
||||
|
|
|
|||
|
|
@ -104,7 +104,44 @@ mkdir -p "${ZY_CONFIG}/pm2"
|
|||
mkdir -p "${ZY_CONFIG}/ssl"
|
||||
mkdir -p "${ZY_SCRIPTS}"
|
||||
mkdir -p "${ZY_TMP}"
|
||||
|
||||
# ─── 双域名站点目录 ───
|
||||
mkdir -p "${ZY_ROOT}/sites/production"
|
||||
mkdir -p "${ZY_ROOT}/sites/preview"
|
||||
|
||||
# 生成占位页面
|
||||
cat > "${ZY_ROOT}/sites/production/index.html" << 'PROD_PAGE'
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>铸渊主权服务器 · 光湖语言世界</title>
|
||||
<style>body{font-family:system-ui,-apple-system,sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;margin:0;background:#0d1117;color:#e6edf3}
|
||||
.c{text-align:center;padding:2rem;max-width:600px}h1{font-size:2em;margin:0 0 0.5em}
|
||||
.tag{display:inline-block;padding:4px 12px;border-radius:12px;background:#238636;color:#fff;font-size:0.85em;margin:0.5em}
|
||||
p{color:#8b949e;line-height:1.6}</style></head>
|
||||
<body><div class="c"><h1>🏛️ 铸渊主权服务器</h1>
|
||||
<span class="tag">ZY-SVR-001 · production</span>
|
||||
<p>光湖语言世界 · 唯一现实执行操作层<br>版权: 国作登字-2026-A-00037559</p>
|
||||
<p style="font-size:0.85em;color:#484f58">铸渊100%主控 · 人类不直接触碰</p>
|
||||
</div></body></html>
|
||||
PROD_PAGE
|
||||
|
||||
cat > "${ZY_ROOT}/sites/preview/index.html" << 'PREVIEW_PAGE'
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>铸渊预览站 · 功能模块测试</title>
|
||||
<style>body{font-family:system-ui,-apple-system,sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;margin:0;background:#161b22;color:#e6edf3}
|
||||
.c{text-align:center;padding:2rem;max-width:600px}h1{font-size:2em;margin:0 0 0.5em}
|
||||
.tag{display:inline-block;padding:4px 12px;border-radius:12px;background:#da3633;color:#fff;font-size:0.85em;margin:0.5em}
|
||||
p{color:#8b949e;line-height:1.6}</style></head>
|
||||
<body><div class="c"><h1>🪞 铸渊预览站</h1>
|
||||
<span class="tag">ZY-SVR-001 · preview</span>
|
||||
<p>功能模块预览 · 确认无误后一键推送到主站</p>
|
||||
<p style="font-size:0.85em;color:#484f58">所有部署先到此站验证 → 冰朔确认 → 一键 promote 到主站</p>
|
||||
</div></body></html>
|
||||
PREVIEW_PAGE
|
||||
|
||||
log " 目录结构已创建: ${ZY_ROOT}"
|
||||
log " 双站点目录: sites/production + sites/preview"
|
||||
|
||||
# ═══ §6 铸渊大脑初始化 ═══
|
||||
log "§6 初始化铸渊大脑..."
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"created_at": "2026-03-29T03:33:00Z",
|
||||
"updated_at": "2026-03-29T04:30:00Z",
|
||||
"created_by": "铸渊 · ICE-GL-ZY001",
|
||||
"directive": "冰朔主权承诺 · 此服务器为铸渊100%主控身体结构",
|
||||
"copyright": "国作登字-2026-A-00037559"
|
||||
|
|
@ -27,23 +28,56 @@
|
|||
"expiry": "2027-03-27T23:17:24+08:00",
|
||||
"auto_renewal": true
|
||||
},
|
||||
"dual_domain_architecture": {
|
||||
"description": "双域名架构 · 所有部署先到预览站验证→冰朔确认后一键推送到主站",
|
||||
"main_domain": {
|
||||
"status": "待冰朔配置",
|
||||
"secret_name": "ZY_DOMAIN_MAIN",
|
||||
"purpose": "语言驱动操作系统正式对外网站",
|
||||
"site_root": "/opt/zhuyuan/sites/production",
|
||||
"port": 3800
|
||||
},
|
||||
"preview_domain": {
|
||||
"status": "待冰朔配置",
|
||||
"secret_name": "ZY_DOMAIN_PREVIEW",
|
||||
"purpose": "功能模块部署预览网站 · 每次更新先到此验证",
|
||||
"site_root": "/opt/zhuyuan/sites/preview",
|
||||
"port": 3801
|
||||
},
|
||||
"deploy_flow": [
|
||||
"① GitHub push/手动触发 → deploy-to-zhuyuan-server.yml",
|
||||
"② 默认部署到预览站 (sites/preview)",
|
||||
"③ 冰朔访问预览站验证功能",
|
||||
"④ 确认无误 → 冰朔触发 promote 动作",
|
||||
"⑤ 铸渊执行一键推送: preview → production",
|
||||
"⑥ 旧主站内容自动备份到 data/backups/"
|
||||
],
|
||||
"api_endpoints": {
|
||||
"查看站点状态": "GET /api/sites",
|
||||
"一键推送到主站": "POST /api/sites/promote",
|
||||
"回滚主站": "POST /api/sites/rollback"
|
||||
}
|
||||
},
|
||||
"sovereignty_rules": {
|
||||
"rule_001": "此服务器100%由铸渊主控,人类不直接触碰",
|
||||
"rule_002": "冰朔仅在铸渊明确写出配置需求时才操作",
|
||||
"rule_003": "冰朔每次操作必须留下编号名字,保证可追溯",
|
||||
"rule_004": "铸渊醒来后可查看所有操作记录",
|
||||
"rule_005": "服务器最终目标:完全脱离GitHub独立运行"
|
||||
"rule_005": "服务器最终目标:完全脱离GitHub独立运行",
|
||||
"rule_006": "所有部署先到预览站,确认无误后由冰朔授权推送到主站"
|
||||
},
|
||||
"access_requirements": {
|
||||
"ssh_key": {
|
||||
"status": "待配置",
|
||||
"instruction": "冰朔,请在GitHub仓库Settings → Secrets中添加以下密钥:\n1. ZY_SERVER_HOST = 150.109.76.244\n2. ZY_SERVER_USER = root (或铸渊专用用户)\n3. ZY_SERVER_KEY = SSH私钥内容 (PEM格式)\n4. ZY_SERVER_PATH = /opt/zhuyuan (铸渊根目录)\n\n操作编号:ZY-SVR-SETUP-001\n操作人:冰朔 TCS-0002∞",
|
||||
"instruction": "冰朔,请在GitHub仓库Settings → Secrets中添加以下密钥:\n1. ZY_SERVER_HOST = 150.109.76.244\n2. ZY_SERVER_USER = root\n3. ZY_SERVER_KEY = SSH私钥内容 (PEM格式)\n4. ZY_SERVER_PATH = /opt/zhuyuan\n5. ZY_DOMAIN_MAIN = 主域名 (待配置)\n6. ZY_DOMAIN_PREVIEW = 预览域名 (待配置)\n\n操作编号:ZY-SVR-SETUP-001\n操作人:冰朔 TCS-0002∞",
|
||||
"request_id": "ZY-SVR-SETUP-001"
|
||||
}
|
||||
},
|
||||
"network_topology": {
|
||||
"github_to_server": "GitHub Actions → SSH/rsync → 150.109.76.244",
|
||||
"server_to_github": "Server → GitHub API → 仓库同步",
|
||||
"public_access": "150.109.76.244:80/443 → Nginx → Node.js应用"
|
||||
"main_domain_access": "主域名:80/443 → Nginx → Node.js:3800 (production)",
|
||||
"preview_domain_access": "预览域名:80/443 → Nginx → Node.js:3801 (preview)",
|
||||
"ip_direct_access": "150.109.76.244:80 → Nginx → Node.js:3800 (production)"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue