5 changed files with 56 additions and 0 deletions
@ -0,0 +1,9 @@ |
|||||
|
import { createError } from "h3"; |
||||
|
|
||||
|
/** 与规格一致的错误体;依赖 Nitro 将 `createError` 的 `data` 序列化进 JSON。 */ |
||||
|
export function jsonError(status: number, code: string, message: string): never { |
||||
|
throw createError({ |
||||
|
statusCode: status, |
||||
|
data: { error: { code, message } }, |
||||
|
}); |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
export type Mailer = { |
||||
|
sendVerificationEmail(input: { to: string; token: string }): Promise<void>; |
||||
|
sendPasswordResetEmail(input: { to: string; token: string }): Promise<void>; |
||||
|
}; |
||||
|
|
||||
|
export const noopMailer: Mailer = { |
||||
|
async sendVerificationEmail() {}, |
||||
|
async sendPasswordResetEmail() {}, |
||||
|
}; |
||||
@ -0,0 +1,22 @@ |
|||||
|
import { createError } from "h3"; |
||||
|
import { getRedis } from "./redis"; |
||||
|
|
||||
|
export async function rateLimitOrThrow( |
||||
|
key: string, |
||||
|
limit: number, |
||||
|
windowSeconds: number, |
||||
|
): Promise<void> { |
||||
|
const redis = getRedis(); |
||||
|
const n = await redis.incr(key); |
||||
|
if (n === 1) { |
||||
|
await redis.expire(key, windowSeconds); |
||||
|
} |
||||
|
if (n > limit) { |
||||
|
throw createError({ |
||||
|
statusCode: 429, |
||||
|
data: { |
||||
|
error: { code: "RATE_LIMITED", message: "请求过于频繁,请稍后再试" }, |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,5 @@ |
|||||
|
const NEEDS_VERIFIED = new Set<string>(["patch-me"]); |
||||
|
|
||||
|
export function needsEmailVerified(handlerId: string): boolean { |
||||
|
return NEEDS_VERIFIED.has(handlerId); |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
import { describe, expect, it } from "bun:test"; |
||||
|
import { needsEmailVerified } from "../../server/utils/verification-policy"; |
||||
|
|
||||
|
describe("verification-policy", () => { |
||||
|
it("patch-me requires verified", () => { |
||||
|
expect(needsEmailVerified("patch-me")).toBe(true); |
||||
|
}); |
||||
|
it("unknown defaults false", () => { |
||||
|
expect(needsEmailVerified("other")).toBe(false); |
||||
|
}); |
||||
|
}); |
||||
Loading…
Reference in new issue