diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts new file mode 100644 index 0000000..316e8fb --- /dev/null +++ b/src/lib/server/auth.ts @@ -0,0 +1,71 @@ +import { db } from './db'; +import { users, sessions } from './schema'; +import { eq, and, gt } from 'drizzle-orm'; +import { generateSessionToken, hashSessionToken, verifyPassword } from './auth-utils'; + +const SESSION_DURATION_MS = 30 * 24 * 60 * 60 * 1000; // 30 days + +export async function createSession(userId: number): Promise { + const token = generateSessionToken(); + const sessionId = hashSessionToken(token); + const expiresAt = new Date(Date.now() + SESSION_DURATION_MS); + + await db.insert(sessions).values({ sessionId, userId, expiresAt }); + return token; +} + +export async function validateSession(token: string) { + const sessionId = hashSessionToken(token); + + const result = await db + .select({ + session: sessions, + user: { + id: users.id, + email: users.email, + name: users.name, + role: users.role, + active: users.active, + mustChangePassword: users.mustChangePassword + } + }) + .from(sessions) + .innerJoin(users, eq(sessions.userId, users.id)) + .where( + and( + eq(sessions.sessionId, sessionId), + gt(sessions.expiresAt, new Date()) + ) + ) + .limit(1); + + if (result.length === 0) return null; + + const { session, user } = result[0]; + if (!user.active) return null; + + return { session, user }; +} + +export async function destroySession(token: string): Promise { + const sessionId = hashSessionToken(token); + await db.delete(sessions).where(eq(sessions.sessionId, sessionId)); +} + +export async function authenticateUser(email: string, password: string) { + const result = await db + .select() + .from(users) + .where(eq(users.email, email.toLowerCase())) + .limit(1); + + if (result.length === 0) return null; + + const user = result[0]; + if (!user.active) return null; + + const valid = await verifyPassword(user.passwordHash, password); + if (!valid) return null; + + return user; +}