feat(shared): update db schema, i18n messages, rate-limit, auth-session-provider

- Update src/shared/db/schema.ts

- Update i18n messages for en and zh-CN (error-book, exam-homework, leave,

  lesson-preparation, notifications, rbac, student, textbooks, diagnostic)

- Update src/shared/lib/rate-limit/index.ts and redis-limiter.ts

- Update src/shared/components/auth-session-provider.tsx
This commit is contained in:
SpecialX
2026-07-04 10:23:29 +08:00
parent 3569d83b8e
commit d2c250a1b3
21 changed files with 848 additions and 331 deletions

View File

@@ -19,14 +19,14 @@ export type { RateLimiter, RateLimitParams, RateLimitResult } from "./types"
export { RATE_LIMIT_RULES, rateLimitHeaders, rateLimitKey } from "./rules"
import { MemoryRateLimiter } from "./memory-limiter"
import { RedisRateLimiter } from "./redis-limiter"
/**
* 单例限流器实例(按 env 选择实现,进程级复用)。
*
* RedisRateLimiter 类本身在 import 时不会加载 @upstash/* 依赖——
* 重依赖通过类方法内的 `await import(...)` 懒加载,
* 仅当 `RATE_LIMIT_DRIVER=redis` 且实际调用 `limit()` 时才加载。
* Redis 实现通过动态 `import("./redis-limiter")` 按需加载,
* 仅当 `RATE_LIMIT_DRIVER=redis` 时才会拉入 `redis-limiter.ts` 模块及其
* `@upstash/ratelimit` / `@upstash/redis` 依赖。默认内存模式下该模块不进入
* 模块图,避免可选依赖缺失导致编译失败。
*/
let singleton: RateLimiter | null = null
@@ -34,13 +34,16 @@ let singleton: RateLimiter | null = null
* 获取当前进程的限流器实例。
*
* - 默认返回内存实现
* - 当 `RATE_LIMIT_DRIVER=redis` 时返回 Redis 实现
* - Redis 实现懒加载所需依赖,未安装 @upstash/ratelimit/redis 时首次调用抛错
* - 当 `RATE_LIMIT_DRIVER=redis` 时动态加载 Redis 实现
* - Redis 实现懒加载 @upstash/* 依赖,未安装时首次调用抛错
*
* 返回 `Promise<RateLimiter>`Redis 实现需动态 import 模块,故为异步。
*/
export function getRateLimiter(): RateLimiter {
export async function getRateLimiter(): Promise<RateLimiter> {
if (singleton) return singleton
if (env.RATE_LIMIT_DRIVER === "redis") {
const { RedisRateLimiter } = await import("./redis-limiter")
singleton = new RedisRateLimiter()
} else {
singleton = new MemoryRateLimiter()
@@ -56,7 +59,7 @@ export function getRateLimiter(): RateLimiter {
* **变更**:返回 `Promise<RateLimitResult>`,调用方需 `await`。
*/
export function rateLimit(params: RateLimitParams): Promise<RateLimitResult> {
return getRateLimiter().limit(params)
return getRateLimiter().then((limiter) => limiter.limit(params))
}
/**
@@ -67,5 +70,5 @@ export function rateLimit(params: RateLimitParams): Promise<RateLimitResult> {
* **变更**:返回 `Promise<void>`,调用方需 `await`。
*/
export function resetRateLimit(key: string): Promise<void> {
return getRateLimiter().reset(key)
return getRateLimiter().then((limiter) => limiter.reset(key))
}

View File

@@ -94,7 +94,8 @@ export class RedisRateLimiter implements RateLimiter {
/** 懒加载 @upstash/redis 的 Redis 客户端 */
private async getRedisClient(): Promise<unknown> {
if (this.redisClient) return this.redisClient
const { Redis } = await import("@upstash/redis")
// webpackIgnore: 让 @upstash/redis 成为运行时可选依赖,未启用 redis 驱动时不要求安装
const { Redis } = await import(/* webpackIgnore: true */ "@upstash/redis")
this.redisClient = new Redis({
url: env.UPSTASH_REDIS_REST_URL!,
token: env.UPSTASH_REDIS_REST_TOKEN!,
@@ -106,7 +107,8 @@ export class RedisRateLimiter implements RateLimiter {
private async getRatelimitCtor(): Promise<UpstashRatelimitCtor> {
if (this.ratelimitCtorPromise) return this.ratelimitCtorPromise
this.ratelimitCtorPromise = (async () => {
const mod = await import("@upstash/ratelimit")
// webpackIgnore: 让 @upstash/ratelimit 成为运行时可选依赖,未启用 redis 驱动时不要求安装
const mod = await import(/* webpackIgnore: true */ "@upstash/ratelimit")
return mod.Ratelimit as unknown as UpstashRatelimitCtor
})()
return this.ratelimitCtorPromise