feat: add LINUXDO_AUTH_ENABLED switch to disable LINUX DO Connect login

This commit is contained in:
SMNET Studio
2026-08-11 23:32:44 +08:00
parent c455c41034
commit db2b05dd7d
9 changed files with 75 additions and 8 deletions
+4
View File
@@ -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
+11
View File
@@ -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;
+16 -6
View File
@@ -3929,12 +3929,22 @@
await resolveInviteFromUrl();
try {
const cfg = await api("/api/v1/auth/config");
if (cfg && 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) {
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 (_) {}
}
+3
View File
@@ -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 为空 — 无人自动成为管理员");
+7
View File
@@ -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",
+3 -1
View File
@@ -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",
);
+9 -1
View File
@@ -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}`);
+9
View File
@@ -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",
+13
View File
@@ -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 登录」