Add database integration, auth API, admin/user portals, and deploy-db.sh

This commit is contained in:
2026-06-27 16:40:58 -05:00
parent 26559d4cf8
commit cc59177a74
25 changed files with 1423 additions and 118 deletions
+64
View File
@@ -0,0 +1,64 @@
import { cookies } from 'next/headers';
import { SignJWT, jwtVerify } from 'jose';
import bcrypt from 'bcryptjs';
import { db } from './db';
const JWT_SECRET = process.env.JWT_SECRET || 'change-me-in-production';
const secret = new TextEncoder().encode(JWT_SECRET);
export async function hashPassword(password: string) {
return bcrypt.hashSync(password, 10);
}
export async function verifyPassword(password: string, hash: string) {
return bcrypt.compareSync(password, hash);
}
export async function authenticateUser(username: string, password: string) {
const { rows } = await db.query('SELECT id, username, password_hash, role FROM users WHERE username = $1', [username]);
if (rows.length === 0) return null;
const user = rows[0];
const valid = await verifyPassword(password, user.password_hash);
if (!valid) return null;
return { id: user.id, username: user.username, role: user.role };
}
export async function createSession(user: { id: number; username: string; role: string }) {
const token = await new SignJWT({ id: user.id, username: user.username, role: user.role })
.setProtectedHeader({ alg: 'HS256' })
.setExpirationTime('7d')
.sign(secret);
cookies().set('session', token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', maxAge: 60 * 60 * 24 * 7 });
return token;
}
export async function getSession() {
const token = cookies().get('session')?.value;
if (!token) return null;
try {
const { payload } = await jwtVerify(token, secret);
return payload as { id: number; username: string; role: string };
} catch {
return null;
}
}
export async function clearSession() {
cookies().delete('session');
}
export async function requireRole(role: 'admin' | 'user') {
const session = await getSession();
if (!session || session.role !== role) {
throw new Error('Unauthorized');
}
return session;
}
export async function requireAuth() {
const session = await getSession();
if (!session) {
throw new Error('Unauthorized');
}
return session;
}