spb/chat-spboucher Public
Private universal chat interface over the OpenRouter ecosystem — 400+ models, branching, streaming, usage tracking. Next.js 16 + SQLite, PWA, deployed on m4m64a at chat.spboucher.ai
TypeScript 78.8%
CSS 15.1%
JavaScript 4.9%
Shell 1.2%
1// Author: Simon-Pierre Boucher2// Contact: contact@spboucher.ai3// Project: chat.spboucher.ai45import { NextResponse, type NextRequest } from "next/server";6import {7 verifyCredentials,8 createSession,9 loginRateLimited,10 recordLoginAttempt,11 sessionCookieHeader,12} from "@/lib/auth/auth";13import { clientIp } from "@/lib/auth/guard";1415export const runtime = "nodejs";1617export async function POST(req: NextRequest) {18 const ip = await clientIp();19 if (loginRateLimited(ip)) {20 return NextResponse.json(21 { error: "Too many attempts. Wait 15 minutes and try again." },22 { status: 429 }23 );24 }2526 let body: { username?: string; password?: string };27 try {28 body = await req.json();29 } catch {30 return NextResponse.json({ error: "Invalid request." }, { status: 400 });31 }32 const username = String(body.username ?? "").trim();33 const password = String(body.password ?? "");34 if (!username || !password) {35 return NextResponse.json({ error: "Enter your username and password." }, { status: 400 });36 }3738 const user = await verifyCredentials(username, password);39 recordLoginAttempt(ip, Boolean(user));40 if (!user) {41 return NextResponse.json({ error: "Wrong username or password." }, { status: 401 });42 }4344 const token = createSession(user.id, req.headers.get("user-agent") ?? undefined, ip);45 const res = NextResponse.json({ ok: true, username: user.username });46 res.headers.set("Set-Cookie", sessionCookieHeader(token, 60 * 60 * 24 * 30));47 return res;48}49