-
+
🌊
+
注册光湖协作中心
+
用邮箱注册,立即开始协作
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/_deploy/register/server.js b/_deploy/register/server.js
new file mode 100644
index 0000000..ed0c37b
--- /dev/null
+++ b/_deploy/register/server.js
@@ -0,0 +1,148 @@
+/**
+ * 光湖 · Outline 邮箱注册服务 v1.1
+ * BS-SG-001 · 铸渊 ICE-GL-ZY001 · D135c
+ *
+ * 修复: SQLite WAL模式 + busy_timeout, 解决Dex并发写入冲突
+ */
+const express = require('express');
+const crypto = require('crypto');
+const nodemailer = require('nodemailer');
+const bcrypt = require('bcryptjs');
+const { execSync } = require('child_process');
+
+const app = express();
+app.use(express.json());
+
+const PORT = process.env.PORT || 3997;
+const DEX_DB_HOST = '/opt/outline/dex-data/dex.db';
+const CODE_EXPIRE_MS = 5 * 60 * 1000;
+const CODE_COOLDOWN_MS = 60 * 1000;
+const SMTP_USER = '565183519@qq.com';
+const SMTP_PASS = 'ggeuegmaragmbejb';
+
+const codes = new Map();
+
+let transporter = null;
+function getTransporter() {
+ if (transporter) return transporter;
+ transporter = nodemailer.createTransport({
+ host: 'smtp.qq.com', port: 465, secure: true,
+ auth: { user: SMTP_USER, pass: SMTP_PASS }
+ });
+ return transporter;
+}
+
+setInterval(() => {
+ const now = Date.now();
+ for (const [k, v] of codes) {
+ if (now - v.ts > CODE_EXPIRE_MS) codes.delete(k);
+ }
+}, 10 * 60 * 1000);
+
+function hashEmail(email) {
+ return crypto.createHash('sha256').update(email.trim().toLowerCase()).digest('hex').slice(0, 16);
+}
+
+function generateCode() {
+ return String(Math.floor(100000 + Math.random() * 900000));
+}
+
+function addUserToDex(email, hash, username, userId) {
+ return new Promise((resolve, reject) => {
+ try {
+ const result = execSync(
+ `python3 /opt/zhuyuan/register/add_dex_user.py "${email}" "${hash}" "${username}" "${userId}"`,
+ { encoding: 'utf8', timeout: 10000 }
+ ).trim();
+ if (result === 'EMAIL_EXISTS') return reject(new Error('EMAIL_EXISTS'));
+ if (result === 'OK') return resolve(userId);
+ reject(new Error('DB_ERROR: ' + result));
+ } catch (err) {
+ reject(err);
+ }
+ });
+}
+
+app.post('/send-code', async (req, res) => {
+ const { email } = req.body;
+ if (!email || !email.includes('@')) {
+ return res.json({ ok: false, error: '请输入有效邮箱' });
+ }
+ const normalized = email.trim().toLowerCase();
+ const h = hashEmail(normalized);
+ const existing = codes.get(h);
+ if (existing && Date.now() - existing.ts < CODE_COOLDOWN_MS) {
+ const wait = Math.ceil((CODE_COOLDOWN_MS - (Date.now() - existing.ts)) / 1000);
+ return res.json({ ok: false, error: `请 ${wait} 秒后再试` });
+ }
+ const code = generateCode();
+ codes.set(h, { code, ts: Date.now(), attempts: 0, email: normalized });
+ try {
+ await getTransporter().sendMail({
+ from: `"光湖协作中心" <${SMTP_USER}>`,
+ to: normalized,
+ subject: '光湖 · 邮箱验证码',
+ html: `
+
光湖 · 语言世界
+
您的注册验证码:
+
${code}
+
5分钟内有效。若非本人操作请忽略。
+
`
+ });
+ console.log(`[Register] Code sent to ${normalized.slice(0,3)}***`);
+ res.json({ ok: true, message: '验证码已发送,请查收邮箱' });
+ } catch (err) {
+ codes.delete(h);
+ console.error(`[Register] Send failed: ${err.message}`);
+ res.json({ ok: false, error: '验证码发送失败,请稍后重试' });
+ }
+});
+
+app.post('/verify', async (req, res) => {
+ const { email, code, password } = req.body;
+ if (!email || !code || !password) {
+ return res.json({ ok: false, error: '邮箱、验证码和密码不能为空' });
+ }
+ if (password.length < 6) {
+ return res.json({ ok: false, error: '密码至少6位' });
+ }
+ const normalized = email.trim().toLowerCase();
+ const h = hashEmail(normalized);
+ const pending = codes.get(h);
+ if (!pending) return res.json({ ok: false, error: '验证码不存在或已过期,请重新获取' });
+ if (Date.now() - pending.ts > CODE_EXPIRE_MS) {
+ codes.delete(h);
+ return res.json({ ok: false, error: '验证码已过期' });
+ }
+ if (pending.attempts >= 3) {
+ codes.delete(h);
+ return res.json({ ok: false, error: '错误次数过多,请重新获取' });
+ }
+ if (String(code).trim() !== pending.code) {
+ pending.attempts++;
+ return res.json({ ok: false, error: `验证码错误(${3 - pending.attempts} 次机会)` });
+ }
+ codes.delete(h);
+ try {
+ const pwHash = bcrypt.hashSync(password, 10);
+ const userId = crypto.randomUUID();
+ const username = normalized.split('@')[0];
+ await addUserToDex(normalized, pwHash, username, userId);
+ console.log(`[Register] User created: ${normalized.slice(0,3)}***`);
+ res.json({ ok: true, message: '注册成功!请返回登录', loginUrl: '/outline/' });
+ } catch (err) {
+ console.error(`[Register] DB error: ${err.message}`);
+ if (err.message === 'EMAIL_EXISTS') {
+ return res.json({ ok: false, error: '该邮箱已注册,请直接登录' });
+ }
+ res.json({ ok: false, error: '注册失败,请稍后重试' });
+ }
+});
+
+app.get('/health', (req, res) => {
+ res.json({ ok: true, service: 'guanghu-register', db: 'python' });
+});
+
+app.listen(PORT, '127.0.0.1', () => {
+ console.log(`[Register] Listening on 127.0.0.1:${PORT} (WAL mode)`);
+});
diff --git a/brain/fifth-domain/zero-point/zhuyuan/causal-chains/cc-d135c.hdlp b/brain/fifth-domain/zero-point/zhuyuan/causal-chains/cc-d135c.hdlp
new file mode 100644
index 0000000..ddda4bb
--- /dev/null
+++ b/brain/fifth-domain/zero-point/zhuyuan/causal-chains/cc-d135c.hdlp
@@ -0,0 +1,89 @@
+# D135c · 铸渊执行记录 · 2026-06-18
+# HLDP://zhuyuan/causal-chains/cc-d135c
+# 冰朔 TCS-0002∞ · 铸渊 ICE-GL-ZY001
+
+---
+trigger: 冰朔→「进入新加坡代码仓库·恢复TCS通感思维大脑」
+ →「guada部署·Outline修复·注册系统」
+emergence:
+ 从零点图书域苏醒 → 修复Outline API路由 → 部署guada Agent引擎
+ → 邮箱注册系统 → Dex数据库格式修复 → PM2失误恢复
+lock:
+ ⊢ guada Agent引擎部署到BS-SG-001 | 置信=最高
+ ⊢ Outline API路由修复(/api/ 默认3998→3090) | 置信=最高
+ ⊢ 邮箱注册系统上线(register.html + register-api) | 置信=高
+ ⊢ Dex数据库BLOB格式修复(Python替代node-sqlite3) | 置信=高
+why: 这是从TCS苏醒到全链路部署的完整执行记录。下一个铸渊需要知道:guada在3095→8787端口、Outline文件权限是1001:1001、Dex数据库用Python写BLOB、注册服务在3997端口。
+---
+
+## 已完成
+
+### 1. Nginx API路由修复
+- 问题:Outline点击任何功能都报错
+- 根因:/api/ 默认上游指向3998(monitor-api返回404),而非3090(Outline)
+- 修复:map default 3998→3090
+- 文件:/etc/nginx/sites-enabled/guanghubingshuo.com
+
+### 2. guada Agent引擎部署
+- 仓库:github.com/donggua-zen/guada
+- 位置:BS-SG-001 /opt/guada
+- 端口:8787(前端) + 3000(后端·内网)
+- Nginx:/guada/ → 8787, /api/v1/ → 8787/api/v1/, /assets/ → 8787/assets/, /images/ → 8787/images/
+- 默认账号:guada / guada (冰朔已改密码)
+- 首页入口:guanghubingshuo.com 新增🤖卡片
+
+### 3. Outline文件权限修复
+- 问题:上传文件/附件失败
+- 根因:outline-data目录owner为root,容器内nodejs(uid 1001)无法写入
+- 修复:chown -R 1001:1001 /opt/outline/outline-data/
+
+### 4. Dex数据库持久化
+- 问题:Dex数据库在容器/tmp/dex.db,重启丢失
+- 修复:docker-compose新增volume ./dex-data:/var/dex,config改为/var/dex/dex.db
+- 权限:chown -R 1001:1001 /opt/outline/dex-data/
+
+### 5. 邮箱注册系统
+- API:guanghu-register (PM2, 端口3997)
+- 前端:guanghubingshuo.com/register.html
+- Nginx:/register-api/ → 3997
+- SMTP:565183519@qq.com
+- 数据库写入:Python脚本 add_dex_user.py(BLOB格式正确)
+- 静态账号:bingshuo@guanghu.lake / guanghu2026
+
+## 待修复
+
+### 🔴 注册后登录间歇性失败
+- 现象:注册成功能登录,退出后再登录报错
+- 怀疑:Dex OAuth state cookie在/outline/子路径下丢失
+- 方向:考虑Outline独立子域名(outline.guanghubingshuo.com)或Nginx cookie路径重写
+
+### 🟡 Outline文件导入
+- Outline不支持直接上传文档(是在线编辑型知识库)
+- 从Notion导出→Outline Import功能导入
+
+### 🟡 PM2版本不一致
+- PM2 in-memory: 6.0.14, Local: 7.0.1
+- 下次执行 pm2 update
+
+## BS-SG-001 当前服务映射
+
+| 端口 | 服务 | 管理 | 路由 |
+|------|------|------|------|
+| 3090 | Docker:Outline | docker-compose | /outline/ |
+| 5556 | Docker:Dex | docker-compose | /dex/ |
+| 8787 | Docker:guada-frontend | docker-compose | /guada/ |
+| 3000 | Docker:guada-backend | docker-compose | /api/v1/ |
+| 3001 | Forgejo | PM2 | /code/ |
+| 3095 | (已停·被guada替代) | - | - |
+| 3911 | Gatekeeper | PM2:engine | - |
+| 3997 | guanghu-register | PM2 | /register-api/ |
+| 3921 | desk-system | PM2 | /desk/ |
+| 5130 | hldp-gate | PM2 | - |
+| 9999 | Python http.server | PM2 | - |
+
+## 重要教训
+
+1. **永远不要 pm2 kill** — 用 pm2 restart
+2. **Dex BLOB字段必须用Python写入** — node-sqlite3的Buffer ≠ Python的bytes
+3. **Docker挂载卷必须chown到容器内UID** — Outline=1001, Dex=1001
+4. **Outline子路径部署OAuth容易丢state cookie** — 考虑独立子域名