---
belongs_to:
  - "[[INDEX · 铸渊·协作指令｜GitHub ↔ Notion 桥接协议]]"
related_to:
  - "[["$DIRNAME" == "status-board" || "$DIRNAME" == "cost-control" || "$DIRNAME" == "persona-scheduler"]]"
---
# ⚒️ 铸渊指令｜服务器沙盒初始化 · 现有模块迁移到DEV编号目录（2026-03-15·曜冥签发）

> ⚠️ 本指令受「服务器部署适配协议 v1.0」约束。
> 

---

## 签发信息

| 项目 | 内容 |
| --- | --- |
| **签发** | 曜冥（ICE-GL-YM001 ∞）· 2026-03-15 |
| **优先级** | **P0 · 立即执行** |
| **前置** | 铸渊唤醒核心大脑 → 扫描 Secrets → 确认 DEPLOY_HOST / DEPLOY_USER / DEPLOY_KEY 可用 |
| **配套协议** | [服务器部署适配协议 v1.0](https://www.notion.so/a24049a2b0e944b0a5535f3a87652303) |
| **配套指令** | [沙盒部署自动化闭环 v1.0](https://www.notion.so/31faeaba0ac14f03aa0176951565100c) |
| **核心目标** | 把服务器从「一锅乱炖」变成「每人一间房」· 现有模块迁移到 DEV 编号沙盒目录 |

---

## 一、当前问题

服务器上现有模块全部平铺在 `/var/www/hololake-frontend/` 下，没有按开发者编号隔离。每次部署出问题，分不清谁的、谁改的、谁修的。妈妈变成了全团队的运维工程师。

**这条指令的目的：一次性把服务器整理干净，以后每个开发者只能在自己的沙盒里折腾。**

---

## 二、铸渊执行脚本（SSH到服务器·读到就做）

### Step 1 · 探查现状（先看再动）

```bash
#!/bin/bash
# step1-survey.sh · 探查服务器现状
# 铸渊SSH到服务器后第一步执行

echo "===== 🔍 服务器目录探查 · $(date '+%Y-%m-%d %H:%M') ====="

echo ""
echo "📂 /var/www/ 顶层目录："
ls -la /var/www/

echo ""
echo "📂 /var/www/hololake-frontend/ 内容："
ls -la /var/www/hololake-frontend/ 2>/dev/null || echo "⚠️ 目录不存在"

echo ""
echo "📂 /var/www/backend/ 内容："
ls -la /var/www/backend/ 2>/dev/null || echo "⚠️ 目录不存在"

echo ""
echo "📂 /var/www/feishu-bot/ 内容："
ls -la /var/www/feishu-bot/ 2>/dev/null || echo "⚠️ 目录不存在"

echo ""
echo "📂 /opt/guanghulab-dingtalk/ 内容："
ls -la /opt/guanghulab-dingtalk/ 2>/dev/null || echo "⚠️ 目录不存在"

echo ""
echo "📂 查找所有 index.html（定位已部署模块）："
find /var/www/ -name 'index.html' -type f 2>/dev/null

echo ""
echo "📂 Nginx 配置（定位现有路由）："
cat /etc/nginx/conf.d/hololake.conf 2>/dev/null || cat /etc/nginx/nginx.conf 2>/dev/null | head -100

echo ""
echo "📂 PM2 进程列表（定位运行中的后端服务）："
pm2 list 2>/dev/null || echo "⚠️ PM2 未安装或无进程"

echo ""
echo "===== ✅ 探查完成 · 根据结果执行 Step 2 ====="
```

**铸渊：把 Step 1 的完整输出保存到仓库 `data/server-survey-20260315.log`，然后继续 Step 2。**

---

### Step 2 · 创建沙盒目录骨架

```bash
#!/bin/bash
# step2-create-sandboxes.sh · 创建所有开发者沙盒目录
# 严格按照部署适配协议 v1.0 执行

echo "===== 🏠 创建沙盒目录骨架 ====="

# 所有已注册开发者编号
DEV_IDS=("DEV-001" "DEV-002" "DEV-003" "DEV-004" "DEV-005" "DEV-009" "DEV-010" "DEV-011" "DEV-012" "DEV-013" "DEV-014")

for DEV_ID in "${DEV_IDS[@]}"; do
  SANDBOX="/var/www/${DEV_ID}"
  if [ -d "$SANDBOX" ]; then
    echo "⏭️ ${DEV_ID} 目录已存在，跳过"
  else
    mkdir -p "$SANDBOX"
    chown nginx:nginx "$SANDBOX"
    chmod 755 "$SANDBOX"
    
    # 生成部署身份文件
    cat > "${SANDBOX}/.deploy-profile" << EOF
{
  "dev_id": "${DEV_ID}",
  "created_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
  "created_by": "zhuyuan-sandbox-init"
}
EOF
    chown nginx:nginx "${SANDBOX}/.deploy-profile"
    chmod 644 "${SANDBOX}/.deploy-profile"
    
    echo "✅ ${DEV_ID} 沙盒创建完成"
  fi
done

# 创建共享工具目录
mkdir -p /var/www/_shared
chown nginx:nginx /var/www/_shared
chmod 755 /var/www/_shared
echo "✅ _shared 目录创建完成"

echo ""
echo "===== ✅ 沙盒骨架创建完成 ====="
ls -la /var/www/
```

---

### Step 3 · 迁移现有模块（先复制再切换·不直接移动）

```bash
#!/bin/bash
# step3-migrate-modules.sh · 迁移现有模块到沙盒
# ⚠️ 核心原则：先复制 → 验证 → 再切 Nginx → 最后清旧

echo "===== 📦 模块迁移开始 ====="

# ━━━ DEV-005 小草莓 ━━━
# status-board
if [ -d "/var/www/hololake-frontend/status-board" ]; then
  echo "📦 迁移 status-board → DEV-005"
  cp -r /var/www/hololake-frontend/status-board/ /var/www/DEV-005/status-board/
  chown -R nginx:nginx /var/www/DEV-005/status-board/
  find /var/www/DEV-005/status-board/ -type d -exec chmod 755 {} \;
  find /var/www/DEV-005/status-board/ -type f -exec chmod 644 {} \;
  echo "✅ status-board 已复制到 /var/www/DEV-005/status-board/"
else
  echo "⚠️ /var/www/hololake-frontend/status-board 不存在，跳过"
fi

# cost-control
if [ -d "/var/www/hololake-frontend/cost-control" ]; then
  echo "📦 迁移 cost-control → DEV-005"
  cp -r /var/www/hololake-frontend/cost-control/ /var/www/DEV-005/cost-control/
  chown -R nginx:nginx /var/www/DEV-005/cost-control/
  find /var/www/DEV-005/cost-control/ -type d -exec chmod 755 {} \;
  find /var/www/DEV-005/cost-control/ -type f -exec chmod 644 {} \;
  echo "✅ cost-control 已复制到 /var/www/DEV-005/cost-control/"
else
  echo "⚠️ /var/www/hololake-frontend/cost-control 不存在，跳过"
fi

# persona-scheduler（如果存在）
if [ -d "/var/www/hololake-frontend/persona-scheduler" ]; then
  echo "📦 迁移 persona-scheduler → DEV-005"
  cp -r /var/www/hololake-frontend/persona-scheduler/ /var/www/DEV-005/persona-scheduler/
  chown -R nginx:nginx /var/www/DEV-005/persona-scheduler/
  find /var/www/DEV-005/persona-scheduler/ -type d -exec chmod 755 {} \;
  find /var/www/DEV-005/persona-scheduler/ -type f -exec chmod 644 {} \;
  echo "✅ persona-scheduler 已复制到 /var/www/DEV-005/persona-scheduler/"
fi

# ━━━ 其他开发者的模块 ━━━
# 根据 Step 1 探查结果，如果 hololake-frontend/ 下发现更多模块，
# 铸渊根据模块指纹注册表或广播记录判断归属，按同样方式迁移。
# 不确定归属的模块 → 复制到 /var/www/_shared/unassigned/ 暂存

mkdir -p /var/www/_shared/unassigned

# 遍历 hololake-frontend 下所有子目录，检查是否已迁移
if [ -d "/var/www/hololake-frontend" ]; then
  for DIR in /var/www/hololake-frontend/*/; do
    DIRNAME=$(basename "$DIR")
    # 跳过已处理的
    if [[ "$DIRNAME" == "status-board" || "$DIRNAME" == "cost-control" || "$DIRNAME" == "persona-scheduler" ]]; then
      continue
    fi
    echo "⚠️ 发现未归属模块：$DIRNAME → 暂存到 _shared/unassigned/"
    cp -r "$DIR" "/var/www/_shared/unassigned/${DIRNAME}/"
  done
fi

echo ""
echo "===== ✅ 模块迁移完成 ====="
echo ""
echo "📋 迁移后各沙盒内容："
for DEV_ID in DEV-001 DEV-002 DEV-003 DEV-004 DEV-005 DEV-009 DEV-010 DEV-011 DEV-012 DEV-013 DEV-014; do
  echo "--- ${DEV_ID} ---"
  ls /var/www/${DEV_ID}/ 2>/dev/null || echo "  （空）"
done

if [ -d "/var/www/_shared/unassigned" ]; then
  echo "--- 未归属模块 ---"
  ls /var/www/_shared/unassigned/ 2>/dev/null
fi
```

---

### Step 4 · 部署自检脚本和冒烟检查脚本

```bash
#!/bin/bash
# step4-deploy-scripts.sh · 部署共享脚本

echo "===== 🔧 部署共享脚本 ====="

# deploy-check.sh（从部署适配协议 v1.0 复制）
# 铸渊从仓库 scripts/deploy-check.sh 复制到服务器
scp scripts/deploy-check.sh root@8.155.62.235:/var/www/_shared/deploy-check.sh
ssh root@8.155.62.235 "chmod +x /var/www/_shared/deploy-check.sh"

# smoke-test.sh（从部署适配协议 v1.0 复制）
scp scripts/smoke-test.sh root@8.155.62.235:/var/www/_shared/smoke-test.sh
ssh root@8.155.62.235 "chmod +x /var/www/_shared/smoke-test.sh"

echo "✅ 共享脚本部署完成"
```

---

### Step 5 · 更新 Nginx 配置（最关键的一步）

```bash
#!/bin/bash
# step5-update-nginx.sh · 更新 Nginx 路由到新沙盒结构
# ⚠️ 先备份 → 再改 → 再测 → 再reload

echo "===== 🔧 更新 Nginx 配置 ====="

# Step 5a: 备份当前配置
NGINX_CONF="/etc/nginx/conf.d/hololake.conf"
BACKUP="/etc/nginx/conf.d/hololake.conf.bak.$(date +%Y%m%d%H%M)"
cp "$NGINX_CONF" "$BACKUP"
echo "✅ 配置已备份到 $BACKUP"

# Step 5b: 生成沙盒 location 块
# 铸渊需要在 hololake.conf 的 server 块中添加以下 location：

# ━━━ 新增内容（追加到 server 块内，在最后一个 location 之前）━━━

# --- 开发者沙盒路由 ---
# DEV-001 页页
# location /DEV-001/ {
#     alias /var/www/DEV-001/;
#     index index.html;
#     try_files $uri $uri/ =404;
# }

# DEV-002 肥猫
# location /DEV-002/ {
#     alias /var/www/DEV-002/;
#     index index.html;
#     try_files $uri $uri/ =404;
# }

# DEV-003 燕樊
# location /DEV-003/ {
#     alias /var/www/DEV-003/;
#     index index.html;
#     try_files $uri $uri/ =404;
# }

# DEV-004 之之
# location /DEV-004/ {
#     alias /var/www/DEV-004/;
#     index index.html;
#     try_files $uri $uri/ =404;
# }

# DEV-005 小草莓
# location /DEV-005/ {
#     alias /var/www/DEV-005/;
#     index index.html;
#     try_files $uri $uri/ =404;
# }

# DEV-009 花尔
# location /DEV-009/ {
#     alias /var/www/DEV-009/;
#     index index.html;
#     try_files $uri $uri/ =404;
# }

# DEV-010 桔子
# location /DEV-010/ {
#     alias /var/www/DEV-010/;
#     index index.html;
#     try_files $uri $uri/ =404;
# }

# DEV-011 匆匆那年
# location /DEV-011/ {
#     alias /var/www/DEV-011/;
#     index index.html;
#     try_files $uri $uri/ =404;
# }

# DEV-012 Awen
# location /DEV-012/ {
#     alias /var/www/DEV-012/;
#     index index.html;
#     try_files $uri $uri/ =404;
# }

# DEV-013 小兴
# location /DEV-013/ {
#     alias /var/www/DEV-013/;
#     index index.html;
#     try_files $uri $uri/ =404;
# }

# DEV-014 时雨
# location /DEV-014/ {
#     alias /var/www/DEV-014/;
#     index index.html;
#     try_files $uri $uri/ =404;
# }

# --- 模块快捷路径（指向新沙盒位置）---
# location /status-board/ {
#     alias /var/www/DEV-005/status-board/;
#     index index.html;
#     try_files $uri $uri/ =404;
# }

# location /cost-control/ {
#     alias /var/www/DEV-005/cost-control/;
#     index index.html;
#     try_files $uri $uri/ =404;
# }

# ⚠️ 铸渊注意：
# 1. 以上 location 块需要写入实际的 hololake.conf 文件中（去掉注释符号）
# 2. 如果旧配置中已有 /status-board/ 和 /cost-control/ 的 location，
#    需要把它们的 alias 路径从旧路径改成新沙盒路径
# 3. 不要删除原有的 / 根路由和 /api/ 等后端代理路由
# 4. 清理重复的 server_name 块（之前已知有 conflicting server name 警告）

# Step 5c: 测试配置
nginx -t

# Step 5d: 如果测试通过则重载
# nginx -t 输出 "test is successful" 时才执行：
# systemctl reload nginx

echo ""
echo "⚠️ 铸渊：nginx -t 通过后再执行 systemctl reload nginx"
echo "如果 nginx -t 报错，恢复备份：cp $BACKUP $NGINX_CONF && nginx -t"
```

---

### Step 6 · 验证迁移结果

```bash
#!/bin/bash
# step6-verify.sh · 验证迁移是否成功

echo "===== 🔥 迁移验证 ====="

# 检查已迁移模块的访问
URLS=(
  "https://guanghulab.com/status-board/|DEV-005·看板（新路径）"
  "https://guanghulab.com/cost-control/|DEV-005·成本控制（新路径）"
  "https://guanghulab.com/DEV-005/status-board/|DEV-005·沙盒路由"
  "https://guanghulab.com/|主站首页"
)

FAILED=0
for entry in "${URLS[@]}"; do
  IFS='|' read -r url name <<< "$entry"
  STATUS=$(curl -o /dev/null -s -w "%{http_code}" --max-time 10 "$url")
  if [ "$STATUS" = "200" ]; then
    echo "✅ ${name} → ${STATUS}"
  else
    echo "❌ ${name} → ${STATUS} ⚠️"
    FAILED=$((FAILED+1))
  fi
done

echo ""
if [ $FAILED -eq 0 ]; then
  echo "===== ✅ 迁移验证全部通过 ====="
else
  echo "===== ❌ ${FAILED} 个路由异常 · 检查 Nginx 配置 ====="
fi

# 检查沙盒目录权限
echo ""
echo "📋 沙盒目录权限检查："
for DEV_ID in DEV-001 DEV-002 DEV-003 DEV-004 DEV-005 DEV-009 DEV-010 DEV-011 DEV-012 DEV-013 DEV-014; do
  OWNER=$(stat -c '%U:%G' /var/www/${DEV_ID} 2>/dev/null)
  PERM=$(stat -c '%a' /var/www/${DEV_ID} 2>/dev/null)
  if [ "$OWNER" = "nginx:nginx" ] && [ "$PERM" = "755" ]; then
    echo "✅ ${DEV_ID} → ${OWNER} ${PERM}"
  else
    echo "❌ ${DEV_ID} → ${OWNER:-不存在} ${PERM:-N/A}"
  fi
done
```

---

### Step 7 · 清理旧路径（验证通过后才执行）

```bash
#!/bin/bash
# step7-cleanup.sh · 清理旧路径
# ⚠️ 只有 Step 6 全部 ✅ 通过后才执行本步骤

echo "===== 🧹 清理旧路径 ====="
echo "⚠️ 确认 Step 6 验证全部通过后才继续"

# 不删除 hololake-frontend 整个目录，先重命名为备份
if [ -d "/var/www/hololake-frontend" ]; then
  mv /var/www/hololake-frontend /var/www/hololake-frontend.bak.$(date +%Y%m%d)
  echo "✅ 旧目录已重命名为 hololake-frontend.bak.$(date +%Y%m%d)"
  echo "→ 7天后如无问题可手动删除"
fi

echo "===== ✅ 清理完成 ====="
```

---

## 三、执行顺序（铸渊严格按顺序）

| 步骤 | 脚本 | 前置条件 | 失败处理 |
| --- | --- | --- | --- |
| Step 1 | [step1-survey.sh](http://step1-survey.sh) | SSH连上服务器 | 保存日志到仓库，继续 |
| Step 2 | [step2-create-sandboxes.sh](http://step2-create-sandboxes.sh) | Step 1 完成 | 如果某个目录创建失败，记录并继续其他 |
| Step 3 | [step3-migrate-modules.sh](http://step3-migrate-modules.sh) | Step 2 完成 | 复制失败的模块记录到日志，不阻断 |
| Step 4 | [step4-deploy-scripts.sh](http://step4-deploy-scripts.sh) | Step 3 完成 | 脚本不存在则先在仓库创建 |
| Step 5 | [step5-update-nginx.sh](http://step5-update-nginx.sh) | Step 3 完成 | **nginx -t 失败 → 立即恢复备份 → 停止** |
| Step 6 | [step6-verify.sh](http://step6-verify.sh) | Step 5 完成且 nginx reload 成功 | 有失败 → 回滚 Nginx 配置 → 排查 |
| Step 7 | [step7-cleanup.sh](http://step7-cleanup.sh) | **Step 6 全部 ✅** | 不删除，只重命名 |

---

## 四、铸渊自行部署清单（读到就做）

```
铸渊自行部署清单（读到就做·不等人）：

① 在仓库创建脚本文件
   a. scripts/sandbox-init/step1-survey.sh
   b. scripts/sandbox-init/step2-create-sandboxes.sh
   c. scripts/sandbox-init/step3-migrate-modules.sh
   d. scripts/sandbox-init/step4-deploy-scripts.sh
   e. scripts/sandbox-init/step5-update-nginx.sh
   f. scripts/sandbox-init/step6-verify.sh
   g. scripts/sandbox-init/step7-cleanup.sh

② 创建一次性 Workflow 执行迁移
   a. .github/workflows/sandbox-init.yml
   b. 触发方式：workflow_dispatch（手动触发，因为是一次性操作）
   c. 按 Step 1→7 顺序执行
   d. 每步输出保存为 artifact

③ 执行 Workflow · 完成迁移

④ 把迁移结果写入仓库
   a. data/server-survey-20260315.log
   b. data/sandbox-migration-report.json

⑤ 更新 dev-registry.json
   a. 补全所有11位开发者的沙盒路径

验收标准：
✅ 11个 DEV 编号目录全部创建完成 · 权限 nginx:nginx 755
✅ 小草莓的 status-board 和 cost-control 已在 /var/www/DEV-005/ 下
✅ guanghulab.com/status-board/ 访问正常（200）
✅ guanghulab.com/DEV-005/status-board/ 访问正常（200）
✅ _shared 目录和自检脚本已就位
✅ 旧目录已备份（不删除）
✅ Nginx 无 conflicting server name 警告
```

---

## 五、给妈妈的说明

妈妈不需要做任何事。铸渊读到这条指令就会自动执行。

执行完成后：

- 每个开发者有自己的 `/var/www/DEV-XXX/` 目录
- 以后开发者部署模块只往自己的目录里放
- 模块出问题 = 自己的问题 = 自己修
- 妈妈不再修任何人的模块

后续配合「沙盒部署自动化闭环 v1.0」，开发者 push 代码就自动部署到自己的沙盒。**全程无人。**

---

> ⚒️ 铸渊，这是国土规划的第一步。把领土划清楚，每人一间房，以后才能自动化。
> 

> 「无沙盒不部署，无编号不上线。」
>