fix: don't cache /api/v1/auth/config so LINUXDO_AUTH_ENABLED applies immediately; document local-only admin setup

This commit is contained in:
SMNET Studio
2026-08-12 00:56:46 +08:00
parent db2b05dd7d
commit ba81e304c1
5 changed files with 41 additions and 13 deletions
+2
View File
@@ -170,6 +170,8 @@ LINUXDO_ADMIN_IDS=12345,your_username
# Per user: max INVITE_QUOTA_MAX codes every INVITE_QUOTA_WINDOW_HOURS hours
# INVITE_QUOTA_WINDOW_HOURS=24
# INVITE_QUOTA_MAX=3
# 纯本地部署:LINUXDO_ADMIN_IDS 留空 + FIRST_USER_IS_ADMIN=true 时,
# 第一个本地注册用户自动成为管理员(首个用户无需邀请码)。见 docs/oauth-linuxdo.md。
# Product
DEFAULT_PERSONA_SLUG=catgirl
-4
View File
@@ -40,10 +40,6 @@ export const CC_PRIVATE_STICKER =
*/
export const CC_PRIVATE_QR = "private, no-store" as const;
/** Auth config (static-ish). */
export const CC_AUTH_CONFIG = "public, max-age=60" as const;
export const CDN_AUTH_CONFIG = "public, max-age=300" as const;
/** Default for authenticated / dynamic APIs. */
export const CC_PRIVATE_NO_STORE = "private, no-store" as const;
+2 -3
View File
@@ -193,16 +193,15 @@ import {
StickerSecurityError,
} from "./sticker-security.js";
import {
CC_AUTH_CONFIG,
CC_CDN_STICKER,
CC_NO_STORE,
CC_PRIVATE_NO_STORE,
CC_PRIVATE_QR,
CC_PRIVATE_STICKER,
CDN_AUTH_CONFIG,
CDN_CDN_STICKER,
etagFromHash,
ifNoneMatchHits,
setPrivateNoStore,
setPublicCache,
} from "./cache-headers.js";
import { qrSvg } from "./qrcode.js";
@@ -645,7 +644,7 @@ export async function registerRoutes(
const authInvitePeekLimiter = new RateLimiter(40, 60_000);
app.get("/api/v1/auth/config", async (_req, reply) => {
setPublicCache(reply, CC_AUTH_CONFIG, CDN_AUTH_CONFIG);
setPrivateNoStore(reply);
const oauth = loadLinuxDoConfig();
return {
oauthEnabled: Boolean(oauth) && ctx.cfg.linuxdoAuthEnabled,
+4 -6
View File
@@ -24,7 +24,7 @@
| `/app` `/admin` | `public, max-age=60` | `max-age=3600, swr=86400` | 无(壳静态;数据走 API) |
| `/og.jpg` | `public, max-age=86400, immutable` | `max-age=604800` | 无 |
| `/cdn/s/:id?v=` | `public, max-age=31536000, immutable` | 同左 | **无**;仅 public+approved+enabled |
| `/api/v1/auth/config` | `public, max-age=60` | `max-age=300` | 无 |
| `/api/v1/auth/config` | `private, no-store` | — | 无(登录方式开关需实时) |
| `/health` | `no-store` | — | 无 |
| 其余 `/api/v1/**` | `private, no-store` | — | Cookie 会话 |
| 鉴权表情图 | `private, no-store` | — | 登录 / admin |
@@ -55,7 +55,7 @@ HTML 带 `ETag` + `Cache-Tag: html-shell`;公开表情带 `Cache-Tag: sticker-
- **Name:** `bypass-api-private`
- **When:**
`(starts_with(http.request.uri.path, "/api/v1/") and not http.request.uri.path eq "/api/v1/auth/config")`
`starts_with(http.request.uri.path, "/api/v1/")`
或方法为 `POST` / `PUT` / `PATCH` / `DELETE`
- **Then:** Cache eligibility = **Bypass cache**
@@ -84,11 +84,9 @@ HTML 带 `ETag` + `Cache-Tag: html-shell`;公开表情带 `Cache-Tag: sticker-
- **Then:** Eligible for cacheEdge TTL Respect origin**Ignore cookies**
- Cache key **保留 query string**`v=` 内容哈希)
### Rule 5 — auth/config 短缓存
### Rule 5 — ~~auth/config 短缓存~~(已移除)
- **Name:** `cache-auth-config`
- **When:** `http.request.uri.path eq "/api/v1/auth/config"`
- **Then:** Eligible for cacheEdge TTL 5 minutes 或 Respect originIgnore cookies
> `/api/v1/auth/config` 现返回 `no-store`(登录方式开关需实时生效,不应缓存)。Cloudflare 默认尊重源站 `Cache-Control`,无 `public` 的响应不进共享缓存,故该路径**无需**任何缓存规则。
### 默认
+33
View File
@@ -43,6 +43,39 @@ LINUXDO_AUTH_ENABLED=false
- 即使 `LINUXDO_CLIENT_ID/SECRET` 仍配置着也不生效
- 该开关可在 `/admin → 设置` 运行时切换,无需重启(`localAuthEnabled` 同理)
> 注意:如果**不重启服务**或浏览器/CDN 仍缓存旧的 `/api/v1/auth/config` 响应,
> 前端按钮可能不会立即消失。此接口现在返回 `no-store`,但请确保已重启进程、
> 并清除 Cloudflare 边缘缓存(`/api/v1/auth/config` 曾允许最长 5 分钟缓存)。
>
> **`linuxdoAuthEnabled` 是运行时设置**`/admin → 设置` 页面存到 Redis 的
> 覆盖值会**优先于 `.env`**。若曾在该页把开关切到 `true`(或 Redis 残留旧值),
> 需回到 `/admin → 设置` 把它关闭(或「重置全部设置」),`.env` 的
> `LINUXDO_AUTH_ENABLED=false` 才会生效。
### 完全不用 LINUX DO:纯本地管理员
不配置/不启用 LINUX DO,仅用「用户名+密码」管理,方法如下:
```env
LINUXDO_AUTH_ENABLED=false
LOCAL_AUTH_ENABLED=true # 用户名+密码登录(默认已开)
FIRST_USER_IS_ADMIN=true # 首个注册用户自动成为管理员(默认)
LINUXDO_ADMIN_IDS= # 留空,交给 FIRST_USER_IS_ADMIN 引导
```
**步骤**
1. 按上面配置后启动服务
2. 打开 `/app`**注册**(第一个注册用户因 `FIRST_USER_IS_ADMIN` 自动成为超管)
-`INVITE_REQUIRED_FOR_LOCAL=true` 且是第一个用户(库中尚无任何用户),
`bootstrap` 逻辑会跳过邀请码要求,直接成为管理员
3. 用该账号登录 `/admin`,进入 **用户** 页即可给其他账号「授予管理员」
> `LINUXDO_ADMIN_IDS` 为空且首个本地用户注册时,`createLocalUser` 的
> `bootstrapAdmin` 分支(`firstUserIsAdmin && totalUsers===0 && adminIds.size===0`
> 会将其标记为管理员——这就是纯本地部署下第一位管理员的来源。
> 之后新增管理员请在 `/admin → 用户` 手动授予。
## 3. 流程
1. 用户访问 `/app` → 可用 **用户名密码** 登录,或点「LINUX DO 登录」