mirror of
https://github.com/SMNETSTUDIO/WeChat-AI.git
synced 2026-08-12 22:23:42 +08:00
feat: add LINUXDO_AUTH_ENABLED switch to disable LINUX DO Connect login
This commit is contained in:
@@ -148,6 +148,10 @@ LINUXDO_CLIENT_SECRET=
|
||||
LINUXDO_REDIRECT_URI=http://127.0.0.1:8787/api/v1/auth/callback
|
||||
# Comma-separated LINUX DO user ids and/or usernames who become admins
|
||||
LINUXDO_ADMIN_IDS=12345,your_username
|
||||
# Master switch for LINUX DO Connect login. Set to false to disable the
|
||||
# "使用 LINUX DO 登录" button and refuse /api/v1/auth/login + /callback even
|
||||
# when LINUXDO_CLIENT_* are configured (e.g. keep only username/password auth).
|
||||
# LINUXDO_AUTH_ENABLED=true
|
||||
# Optional overrides:
|
||||
# LINUXDO_AUTHORIZE_URL=https://connect.linux.do/oauth2/authorize
|
||||
# LINUXDO_TOKEN_URL=https://connect.linux.do/oauth2/token
|
||||
|
||||
@@ -8319,9 +8319,20 @@
|
||||
}
|
||||
} catch {
|
||||
$("gate").classList.add("show");
|
||||
hideAdminOauthBtn();
|
||||
}
|
||||
}
|
||||
|
||||
async function hideAdminOauthBtn() {
|
||||
try {
|
||||
const cfg = await api("/api/v1/auth/config");
|
||||
if (cfg && cfg.oauthEnabled === false) {
|
||||
const btn = $("loginBtn");
|
||||
if (btn) btn.style.display = "none";
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
function showTabError(tab, err) {
|
||||
const sec = document.getElementById(tab);
|
||||
if (!sec) return;
|
||||
|
||||
@@ -3929,13 +3929,23 @@
|
||||
await resolveInviteFromUrl();
|
||||
try {
|
||||
const cfg = await api("/api/v1/auth/config");
|
||||
if (cfg && cfg.localAuthEnabled === false) {
|
||||
if (cfg) {
|
||||
if (cfg.localAuthEnabled === false) {
|
||||
setAuthTab("login");
|
||||
if ($("authTabs")) $("authTabs").style.display = "none";
|
||||
if ($("authLoginPanel")) $("authLoginPanel").style.display = "none";
|
||||
if ($("authRegisterPanel")) $("authRegisterPanel").style.display = "none";
|
||||
setInviteBanner(null);
|
||||
}
|
||||
if (cfg.oauthEnabled === false) {
|
||||
const btn = $("loginBtn");
|
||||
if (btn) btn.style.display = "none";
|
||||
const divider = $("loginGate")?.querySelector(".auth-divider");
|
||||
if (divider) divider.style.display = "none";
|
||||
const note = $("loginGate")?.querySelector(".auth-footer-note");
|
||||
if (note) note.style.display = "none";
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,9 @@ async function main(): Promise<void> {
|
||||
exitCode = 1;
|
||||
} else {
|
||||
ok(`OAuth redirect=${oauth.redirectUri}`);
|
||||
if (!cfg.linuxdoAuthEnabled) {
|
||||
warn("LINUXDO_AUTH_ENABLED=false — LINUX DO 登录已关闭(login/callback 拒绝)");
|
||||
}
|
||||
}
|
||||
if (cfg.adminIds.size === 0) {
|
||||
warn("LINUXDO_ADMIN_IDS 为空 — 无人自动成为管理员");
|
||||
|
||||
@@ -251,6 +251,12 @@ export interface AppConfig {
|
||||
inviteQuotaWindowHours: number;
|
||||
inviteQuotaMax: number;
|
||||
firstUserIsAdmin: boolean;
|
||||
/**
|
||||
* LINUX DO Connect OAuth login. Default on when LINUXDO_CLIENT_* configured.
|
||||
* Set LINUXDO_AUTH_ENABLED=false to disable the "使用 LINUX DO 登录" path
|
||||
* entirely (login + callback both refuse) even when credentials exist.
|
||||
*/
|
||||
linuxdoAuthEnabled: boolean;
|
||||
/**
|
||||
* Optional ops labels for multi-node fleet display (not public URLs).
|
||||
* WORKER_ID itself is read by BotWorkerManager from process.env.
|
||||
@@ -428,6 +434,7 @@ export function loadConfig(env: NodeJS.ProcessEnv = process.env): AppConfig {
|
||||
inviteQuotaWindowHours: Number(env.INVITE_QUOTA_WINDOW_HOURS ?? "24"),
|
||||
inviteQuotaMax: Number(env.INVITE_QUOTA_MAX ?? "3"),
|
||||
firstUserIsAdmin: env.FIRST_USER_IS_ADMIN !== "false",
|
||||
linuxdoAuthEnabled: env.LINUXDO_AUTH_ENABLED !== "false",
|
||||
llmBaseUrl: env.LLM_BASE_URL ?? "https://api.openai.com/v1",
|
||||
llmApiKey: env.LLM_API_KEY ?? "",
|
||||
llmModel: env.LLM_MODEL ?? "gpt-4o-mini",
|
||||
|
||||
@@ -451,7 +451,9 @@ async function main(): Promise<void> {
|
||||
);
|
||||
console.log(
|
||||
oauth
|
||||
? `[oauth] LINUX DO enabled → ${oauth.redirectUri}`
|
||||
? `[oauth] LINUX DO enabled → ${oauth.redirectUri}${
|
||||
cfg.linuxdoAuthEnabled ? "" : " (登录已关闭 LINUXDO_AUTH_ENABLED=false)"
|
||||
}`
|
||||
: "[oauth] LINUX DO 未配置(设置 LINUXDO_CLIENT_ID/SECRET/REDIRECT_URI)",
|
||||
);
|
||||
|
||||
|
||||
@@ -648,7 +648,7 @@ export async function registerRoutes(
|
||||
setPublicCache(reply, CC_AUTH_CONFIG, CDN_AUTH_CONFIG);
|
||||
const oauth = loadLinuxDoConfig();
|
||||
return {
|
||||
oauthEnabled: Boolean(oauth),
|
||||
oauthEnabled: Boolean(oauth) && ctx.cfg.linuxdoAuthEnabled,
|
||||
provider: "linux.do",
|
||||
localAuthEnabled: ctx.cfg.localAuthEnabled,
|
||||
inviteRequiredForLocal: ctx.cfg.inviteRequiredForLocal,
|
||||
@@ -658,6 +658,11 @@ export async function registerRoutes(
|
||||
|
||||
app.get("/api/v1/auth/login", async (req, reply) => {
|
||||
const oauth = loadLinuxDoConfig();
|
||||
if (!ctx.cfg.linuxdoAuthEnabled) {
|
||||
return reply
|
||||
.code(503)
|
||||
.send({ error: "LINUX DO 登录已关闭(LINUXDO_AUTH_ENABLED=false)" });
|
||||
}
|
||||
if (!oauth) {
|
||||
return reply
|
||||
.code(503)
|
||||
@@ -671,6 +676,9 @@ export async function registerRoutes(
|
||||
|
||||
app.get("/api/v1/auth/callback", async (req, reply) => {
|
||||
const oauth = loadLinuxDoConfig();
|
||||
if (!ctx.cfg.linuxdoAuthEnabled) {
|
||||
return reply.code(503).type("text/plain; charset=utf-8").send("LINUX DO 登录已关闭");
|
||||
}
|
||||
if (!oauth) return reply.code(503).send("oauth not configured");
|
||||
const q = req.query as { code?: string; state?: string; error?: string };
|
||||
if (q.error) return reply.code(400).send(`oauth error: ${q.error}`);
|
||||
|
||||
@@ -73,6 +73,7 @@ export type RuntimeSettingKey =
|
||||
| "tryChatMaxHistory"
|
||||
| "personaForkEnabled"
|
||||
// auth / invites
|
||||
| "linuxdoAuthEnabled"
|
||||
| "localAuthEnabled"
|
||||
| "passwordMinLength"
|
||||
| "inviteRequiredForLocal"
|
||||
@@ -743,6 +744,14 @@ export const SETTING_SPECS: SettingSpec[] = [
|
||||
},
|
||||
|
||||
// ── 注册与邀请 ──
|
||||
{
|
||||
key: "linuxdoAuthEnabled",
|
||||
env: "LINUXDO_AUTH_ENABLED",
|
||||
group: "auth",
|
||||
label: "启用 LINUX DO 登录",
|
||||
type: "bool",
|
||||
hint: "关闭后「使用 LINUX DO 登录」按钮隐藏,login/callback 一律拒绝(需已配置 LINUXDO_CLIENT_*)",
|
||||
},
|
||||
{
|
||||
key: "localAuthEnabled",
|
||||
env: "LOCAL_AUTH_ENABLED",
|
||||
|
||||
@@ -30,6 +30,19 @@ PUBLIC_BASE_URL=http://127.0.0.1:8787
|
||||
|
||||
`LINUXDO_ADMIN_IDS`:匹配 OAuth 返回的 **用户 id** 或 **username** 即视为管理员。
|
||||
|
||||
### 关闭 LINUX DO 登录
|
||||
|
||||
如需禁用 LINUX DO Connect 登录(仅保留用户名密码/邀请码):
|
||||
|
||||
```env
|
||||
LINUXDO_AUTH_ENABLED=false
|
||||
```
|
||||
|
||||
- 前端(`/app`、`/admin`)隐藏「使用 LINUX DO 登录」按钮
|
||||
- `/api/v1/auth/login` 与 `/api/v1/auth/callback` 一律返回 503
|
||||
- 即使 `LINUXDO_CLIENT_ID/SECRET` 仍配置着也不生效
|
||||
- 该开关可在 `/admin → 设置` 运行时切换,无需重启(`localAuthEnabled` 同理)
|
||||
|
||||
## 3. 流程
|
||||
|
||||
1. 用户访问 `/app` → 可用 **用户名密码** 登录,或点「LINUX DO 登录」
|
||||
|
||||
Reference in New Issue
Block a user