feat: public events/polls, friends graph, captcha, SSE hardening, and admin UX
Deploy / release (push) Skipped
Deploy / deploy (push) Successful in 2m55s

Ship product gaps: register/vote pages, friend add/accept/decline/remove, email verify TTL, captcha on login/forgot, soft-fail user actions, SSE abort/shared client, Commando Centrum error toasts, admin delete for events/polls, and IT/NL i18n fills.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-07-21 21:08:33 +02:00
1 parent 2ff08e5127
commit ed7db6e048
76 files changed
+4834 -1376

No files matched your search

+37 -5
View File
@@ -4,16 +4,25 @@ import { env } from "@/env";
import { checkLogin } from "@/lib/auth/password";
import { prisma } from "@/lib/prisma";
import { clientIp, rateLimit } from "@/lib/rate-limit";
import { captchaConfig, verifyCaptcha } from "@/lib/services/captcha";
import { siteSettings } from "@/lib/services/site-settings";
export type PrecheckResult = "ok" | "invalid" | "twofactor";
export type PrecheckResult =
| "ok"
| "invalid"
| "twofactor"
| "unverified"
| "captcha";
/**
* Validates username+password WITHOUT creating a session, and reports whether a
* TOTP code is still required. Lets the login form do the two-step 2FA flow.
* Also enforces captcha + optional email-verification when configured.
*/
export async function precheckLogin(
username: string,
password: string,
captchaToken?: string | null,
): Promise<PrecheckResult> {
const u = String(username ?? "")
.normalize("NFC")
@@ -21,14 +30,29 @@ export async function precheckLogin(
const p = String(password ?? "");
if (!u || !p) return "invalid";
if (!(await rateLimit(`precheck:${await clientIp()}`, 10, 5 * 60_000)).ok)
return "invalid";
const ip = await clientIp();
if (!(await rateLimit(`precheck:${ip}`, 10, 5 * 60_000)).ok) return "invalid";
let user: { password: string; twoFactorConfirmedAt: Date | null } | null;
const cfg = await captchaConfig();
if (cfg.provider !== "none") {
if (!(await verifyCaptcha(captchaToken ?? null, ip))) return "captcha";
}
let user: {
password: string;
twoFactorConfirmedAt: Date | null;
mail: string | null;
mailVerified: string;
} | null;
try {
user = await prisma.user.findUnique({
where: { username: u },
select: { password: true, twoFactorConfirmedAt: true },
select: {
password: true,
twoFactorConfirmedAt: true,
mail: true,
mailVerified: true,
},
});
} catch {
return "invalid";
@@ -50,5 +74,13 @@ export async function precheckLogin(
});
if (!res.valid) return "invalid";
if (
(await siteSettings.getBool("require_email_verification", false)) &&
user.mail &&
user.mailVerified !== "1"
) {
return "unverified";
}
return user.twoFactorConfirmedAt ? "twofactor" : "ok";
}