feat: add local auth billing and usage management
This commit is contained in:
1 parent
f642b5e71f
commit
196fdde83f
119 files changed
+15695
-2650
No files matched your search
@@ -0,0 +1,103 @@
|
||||
import type { NextResponse } from "next/server";
|
||||
import { SESSION_COOKIE_NAME, shouldUseSecureAuthCookie } from "@/lib/auth/config";
|
||||
import type { AuthMode, AuthSession, AuthUser } from "@/lib/auth/session";
|
||||
import type { PlatformOrganization, PlatformUserRecord } from "@/lib/types";
|
||||
import { clearSessionCookieValues, setSessionCookieValue } from "@/lib/server/auth/session-cookie";
|
||||
import { createSessionCookieValue } from "@/lib/auth/session";
|
||||
import { getPlatformOrganization } from "@/lib/server/account-store";
|
||||
|
||||
export const LOCAL_SESSION_TTL_SECONDS = 24 * 60 * 60;
|
||||
|
||||
const ipAttempts = new Map<string, { count: number; resetAt: number }>();
|
||||
|
||||
export function authUserFromPlatformRecord(user: PlatformUserRecord, organization?: PlatformOrganization | null): AuthUser {
|
||||
const role = user.role;
|
||||
const authorities = role === "super_admin"
|
||||
? ["ROLE_SUPER_ADMIN", "SUPER_ADMIN"]
|
||||
: role === "organization_admin"
|
||||
? ["ROLE_ORGANIZATION_ADMIN", "ORGANIZATION_ADMIN"]
|
||||
: ["ROLE_USER"];
|
||||
return {
|
||||
id: user.id,
|
||||
subject: user.id,
|
||||
username: user.phone,
|
||||
phone: user.phone,
|
||||
displayName: user.displayName,
|
||||
clientId: "platform",
|
||||
organizationId: user.organizationId,
|
||||
organizationName: organization?.name,
|
||||
role,
|
||||
status: user.status,
|
||||
authorities,
|
||||
scope: []
|
||||
};
|
||||
}
|
||||
|
||||
export async function createPlatformSession(user: PlatformUserRecord): Promise<AuthSession> {
|
||||
const organization = user.organizationId ? await getPlatformOrganization(user.organizationId) : null;
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const authMode: AuthMode = user.role === "user" ? "user" : "admin";
|
||||
return {
|
||||
version: 1,
|
||||
authMode,
|
||||
user: authUserFromPlatformRecord(user, organization),
|
||||
issuedAt: now,
|
||||
expiresAt: now + LOCAL_SESSION_TTL_SECONDS,
|
||||
sessionVersion: user.sessionVersion
|
||||
};
|
||||
}
|
||||
|
||||
export async function setPlatformSessionCookie(
|
||||
response: NextResponse,
|
||||
requestUrl: string,
|
||||
session: AuthSession
|
||||
) {
|
||||
const secret = process.env.ZHINIAN_AUTH_SESSION_SECRET || process.env.AUTH_SESSION_SECRET || process.env.NEXTAUTH_SECRET;
|
||||
if (!secret) throw new Error("ZHINIAN_AUTH_SESSION_SECRET 未配置。");
|
||||
setSessionCookieValue(
|
||||
response,
|
||||
requestUrl,
|
||||
await createSessionCookieValue(session, secret),
|
||||
new Date(session.expiresAt * 1000)
|
||||
);
|
||||
}
|
||||
|
||||
export function clearPlatformSessionCookies(response: NextResponse, requestUrl: string) {
|
||||
clearSessionCookieValues(response, requestUrl);
|
||||
response.cookies.set(SESSION_COOKIE_NAME, "", {
|
||||
httpOnly: true,
|
||||
sameSite: "lax",
|
||||
secure: shouldUseSecureAuthCookie(requestUrl),
|
||||
path: "/",
|
||||
maxAge: 0
|
||||
});
|
||||
}
|
||||
|
||||
export function clientIpFromRequest(request: Request): string {
|
||||
return request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ||
|
||||
request.headers.get("x-real-ip")?.trim() ||
|
||||
"unknown";
|
||||
}
|
||||
|
||||
export function checkIpLoginRateLimit(ip: string): void {
|
||||
const now = Date.now();
|
||||
const current = ipAttempts.get(ip);
|
||||
if (!current || current.resetAt <= now) {
|
||||
ipAttempts.set(ip, { count: 1, resetAt: now + 15 * 60 * 1000 });
|
||||
return;
|
||||
}
|
||||
if (current.count >= 30) {
|
||||
const error = new Error("请求过于频繁,请稍后再试。") as Error & { status: number };
|
||||
error.status = 429;
|
||||
throw error;
|
||||
}
|
||||
current.count += 1;
|
||||
}
|
||||
|
||||
export function clearIpLoginRateLimit(ip: string): void {
|
||||
ipAttempts.delete(ip);
|
||||
}
|
||||
|
||||
export function resetLocalAuthRateLimitForTests(): void {
|
||||
ipAttempts.clear();
|
||||
}
|
||||
Reference in new issue
Block a user