Something went wrong. Try again.
An easy to set up and operate PDS admin panel.
Something went wrong. Try again.
2.3 kB · 72 lines
TypeScript
at main
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273import type { FastifyInstance, FastifyRequest, FastifyReply } from "fastify";import bcrypt from "bcryptjs";import type { Db } from "./db.js";
declare module "@fastify/session" { interface FastifySessionObject { operator?: string; }}
const MAX_ATTEMPTS = 5;const WINDOW_MS = 15 * 60 * 1000;
export function registerAuthRoutes( app: FastifyInstance, passwordHash: string | null, appviewUrl: string, pdsHostname: string, db: Db,) { const attempts = new Map<string, { count: number; resetAt: number }>();
app.post("/api/login", async (req, reply) => { if (!passwordHash) { return reply.code(403).send({ error: "password sign-in is disabled" }); } const now = Date.now(); const entry = attempts.get(req.ip); if (entry && entry.resetAt <= now) attempts.delete(req.ip); if (entry && entry.resetAt > now && entry.count >= MAX_ATTEMPTS) { return reply.code(429).send({ error: "too many attempts — try again later" }); }
const { password } = req.body as { password?: string }; if (!password || !bcrypt.compareSync(password, passwordHash)) { const current = attempts.get(req.ip); if (current && current.resetAt > now) current.count += 1; else attempts.set(req.ip, { count: 1, resetAt: now + WINDOW_MS }); return reply.code(401).send({ error: "invalid credentials" }); } attempts.delete(req.ip); req.session.operator = "operator"; return { ok: true }; });
app.post("/api/logout", async (req) => { await req.session.destroy(); return { ok: true }; });
app.get("/api/session", async (req) => { // an admin invited via OAuth but without a passkey yet: the login page shows // the "new admin" entry point only while such an invite is open const pendingAdmins = Boolean( db.prepare("SELECT 1 FROM operators WHERE enrolled_at IS NULL LIMIT 1").get(), ); return { authenticated: Boolean(req.session.operator), operator: req.session.operator ?? null, appviewUrl, pdsHostname, passwordLogin: Boolean(passwordHash), pendingAdmins, }; });}
export async function requireAuth(req: FastifyRequest, reply: FastifyReply) { if (!req.session.operator) { reply.code(401).send({ error: "not authenticated" }); }}