From fe3ce25ae3cda48c0702c2d452e17f6ec214009d Mon Sep 17 00:00:00 2001 From: decolua Date: Fri, 15 May 2026 09:15:53 +0700 Subject: [PATCH] Update JWT_SECRET handling --- README.md | 2 +- README.zh-CN.md | 2 +- i18n/README.ja-JP.md | 2 +- i18n/README.vi.md | 2 +- i18n/README.zh-CN.md | 2 +- src/dashboardGuard.js | 1 + src/lib/auth/dashboardSession.js | 20 +++++++++++++++++--- src/proxy.js | 1 + 8 files changed, 24 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 97278ca..91db36b 100644 --- a/README.md +++ b/README.md @@ -1096,7 +1096,7 @@ docker pull decolua/9router:latest # update to latest | Variable | Default | Description | |----------|---------|-------------| -| `JWT_SECRET` | `9router-default-secret-change-me` | JWT signing secret for dashboard auth cookie (**change in production**) | +| `JWT_SECRET` | Auto-generated (`~/.9router/jwt-secret`) | JWT signing secret for dashboard auth cookie (override to share across instances) | | `INITIAL_PASSWORD` | `123456` | First login password when no saved hash exists | | `DATA_DIR` | `~/.9router` | Main app data location (SQLite at `$DATA_DIR/db/data.sqlite`) | | `PORT` | framework default | Service port (`20128` in examples) | diff --git a/README.zh-CN.md b/README.zh-CN.md index 106603e..8ba77ff 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1089,7 +1089,7 @@ docker stop 9router && docker rm 9router | 变量 | 默认值 | 描述 | |----------|---------|-------------| -| `JWT_SECRET` | `9router-default-secret-change-me` | 用于控制面板 auth cookie 的 JWT 签名密钥(**生产环境请更改**) | +| `JWT_SECRET` | 自动生成(`~/.9router/jwt-secret`) | 用于控制面板 auth cookie 的 JWT 签名密钥(设置可在多实例间共享) | | `INITIAL_PASSWORD` | `123456` | 当没有保存的哈希时首次登录的密码 | | `DATA_DIR` | `~/.9router` | 主应用数据库位置(`db.json`) | | `PORT` | 框架默认值 | 服务端口(示例中为 `20128`) | diff --git a/i18n/README.ja-JP.md b/i18n/README.ja-JP.md index 71248e7..3ec1163 100644 --- a/i18n/README.ja-JP.md +++ b/i18n/README.ja-JP.md @@ -1016,7 +1016,7 @@ docker stop 9router && docker rm 9router | 変数 | デフォルト | 説明 | |------|-----------|------| -| `JWT_SECRET` | `9router-default-secret-change-me` | ダッシュボード認証クッキーのJWT署名シークレット(**本番環境では変更必須**) | +| `JWT_SECRET` | 自動生成(`~/.9router/jwt-secret`) | ダッシュボード認証クッキーのJWT署名シークレット(複数インスタンス間で共有する場合に設定) | | `INITIAL_PASSWORD` | `123456` | 保存されたハッシュがない場合の初回ログインパスワード | | `DATA_DIR` | `~/.9router` | メインアプリのデータベース格納場所(`db.json`) | | `PORT` | フレームワークデフォルト | サービスポート(例では`20128`) | diff --git a/i18n/README.vi.md b/i18n/README.vi.md index 598b9f8..d0a51de 100644 --- a/i18n/README.vi.md +++ b/i18n/README.vi.md @@ -1018,7 +1018,7 @@ docker stop 9router && docker rm 9router | Biến | Mặc định | Mô tả | |----------|---------|-------------| -| `JWT_SECRET` | `9router-default-secret-change-me` | Bí mật ký JWT cho cookie xác thực bảng điều khiển (**thay đổi trong production**) | +| `JWT_SECRET` | Tự động sinh (`~/.9router/jwt-secret`) | Bí mật ký JWT cho cookie xác thực bảng điều khiển (đặt để chia sẻ giữa nhiều instance) | | `INITIAL_PASSWORD` | `123456` | Mật khẩu đăng nhập đầu tiên khi không có hash đã lưu tồn tại | | `DATA_DIR` | `~/.9router` |ị trí cơ sở dữ liệu ứng dụng chính (`db.json`) | | `PORT` | framework default | Cổng dịch vụ (`20128` trong các ví dụ) | diff --git a/i18n/README.zh-CN.md b/i18n/README.zh-CN.md index e5c96bd..92e0624 100644 --- a/i18n/README.zh-CN.md +++ b/i18n/README.zh-CN.md @@ -1015,7 +1015,7 @@ docker stop 9router && docker rm 9router | 变量 | 默认值 | 描述 | |----------|---------|-------------| -| `JWT_SECRET` | `9router-default-secret-change-me` | 仪表板认证 cookie 的 JWT 签名密钥(**生产环境中请更改**) | +| `JWT_SECRET` | 自动生成(`~/.9router/jwt-secret`) | 仪表板认证 cookie 的 JWT 签名密钥(设置可在多实例间共享) | | `INITIAL_PASSWORD | `123456` | 当没有保存的哈希时的首次登录密码 | | `DATA_DIR` | `~/.9router` | 主应用数据库位置(`db.json`) | | `PORT` | 框架默认值 | 服务端口(示例中为 `20128`) | diff --git a/src/dashboardGuard.js b/src/dashboardGuard.js index 6fbbdd0..38681b6 100644 --- a/src/dashboardGuard.js +++ b/src/dashboardGuard.js @@ -32,6 +32,7 @@ const PROTECTED_API_PATHS = [ "/api/provider-nodes/validate", "/api/cli-tools", "/api/mcp", + "/api/translator", ]; // Routes that spawn child processes — restrict to localhost regardless of auth. diff --git a/src/lib/auth/dashboardSession.js b/src/lib/auth/dashboardSession.js index 4a0ee36..dfa8882 100644 --- a/src/lib/auth/dashboardSession.js +++ b/src/lib/auth/dashboardSession.js @@ -1,8 +1,22 @@ import { SignJWT, jwtVerify } from "jose"; +import fs from "node:fs"; +import path from "node:path"; +import crypto from "node:crypto"; +import { DATA_DIR } from "@/lib/dataDir"; -const SECRET = new TextEncoder().encode( - process.env.JWT_SECRET || "9router-default-secret-change-me" -); +function loadJwtSecret() { + if (process.env.JWT_SECRET) return process.env.JWT_SECRET; + const file = path.join(DATA_DIR, "jwt-secret"); + try { + return fs.readFileSync(file, "utf8").trim(); + } catch {} + fs.mkdirSync(DATA_DIR, { recursive: true }); + const generated = crypto.randomBytes(32).toString("hex"); + fs.writeFileSync(file, generated, { mode: 0o600 }); + return generated; +} + +const SECRET = new TextEncoder().encode(loadJwtSecret()); export function shouldUseSecureCookie(request) { const forceSecureCookie = process.env.AUTH_COOKIE_SECURE === "true"; diff --git a/src/proxy.js b/src/proxy.js index d0b5647..59917b7 100644 --- a/src/proxy.js +++ b/src/proxy.js @@ -12,5 +12,6 @@ export const config = { "/api/provider-nodes/validate", "/api/cli-tools/:path*", "/api/mcp/:path*", + "/api/translator/:path*", ], };