46 lines
1.8 KiB
TypeScript
46 lines
1.8 KiB
TypeScript
import { getAuthRuntimeConfig, safeNextPath } from "@/lib/auth/config";
|
|
import { jsonError, jsonOk, readJsonBody } from "@/lib/server/api";
|
|
import { authenticatePlatformUser } from "@/lib/server/account-store";
|
|
import {
|
|
checkIpLoginRateLimit,
|
|
clearIpLoginRateLimit,
|
|
clientIpFromRequest,
|
|
createPlatformSession,
|
|
setPlatformSessionCookie
|
|
} from "@/lib/server/auth/local";
|
|
|
|
export const runtime = "nodejs";
|
|
export const dynamic = "force-dynamic";
|
|
|
|
export async function POST(request: Request) {
|
|
const ip = clientIpFromRequest(request);
|
|
try {
|
|
const config = getAuthRuntimeConfig();
|
|
if (!config.configured || !config.sessionSecret) {
|
|
throw Object.assign(new Error(`账号认证配置不完整:${config.missing.join(", ") || "ZHINIAN_AUTH_SESSION_SECRET"}`), { status: 503 });
|
|
}
|
|
checkIpLoginRateLimit(ip);
|
|
const body = await readJsonBody<Record<string, unknown>>(request);
|
|
const phone = stringValue(body.phone) || stringValue(body.username);
|
|
const password = stringValue(body.password);
|
|
if (!phone || !password) throw Object.assign(new Error("手机号和密码不能为空。"), { status: 400 });
|
|
const user = await authenticatePlatformUser(phone, password);
|
|
clearIpLoginRateLimit(ip);
|
|
const session = await createPlatformSession(user);
|
|
const response = jsonOk({
|
|
ok: true,
|
|
redirectTo: safeNextPath(stringValue(body.next)),
|
|
user: session.user,
|
|
authMode: session.authMode
|
|
});
|
|
await setPlatformSessionCookie(response, request.url, session);
|
|
return response;
|
|
} catch (error) {
|
|
return jsonError(error, 401, { request, source: "api.auth.password", logClientErrors: false });
|
|
}
|
|
}
|
|
|
|
function stringValue(value: unknown): string | undefined {
|
|
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
}
|