Merge branch 'worktree-agent-a9901af2' into Develop

# Conflicts:
#	.planning/REQUIREMENTS.md
#	.planning/ROADMAP.md
#	.planning/STATE.md
#	bun.lock
#	package.json
#	src/server/middleware/auth.ts
#	src/server/routes/auth.ts
#	src/server/routes/oauth.ts
#	src/server/services/auth.service.ts
This commit is contained in:
2026-04-04 20:48:38 +02:00
12 changed files with 257 additions and 369 deletions

View File

@@ -1,114 +1,10 @@
import { randomBytes } from "node:crypto";
import { count, eq } from "drizzle-orm";
import { eq } from "drizzle-orm";
import { db as prodDb } from "../../db/index.ts";
import { apiKeys, sessions, users } from "../../db/schema.ts";
import { apiKeys } from "../../db/schema.ts";
type Db = typeof prodDb;
// ── User Management ──────────────────────────────────────────────────
export async function createUser(
db: Db = prodDb,
username: string,
password: string,
) {
const passwordHash = await Bun.password.hash(password);
const [row] = await db
.insert(users)
.values({ username, passwordHash })
.returning();
return row;
}
export async function verifyPassword(
db: Db = prodDb,
username: string,
password: string,
) {
const [user] = await db
.select()
.from(users)
.where(eq(users.username, username));
if (!user) return null;
const valid = await Bun.password.verify(password, user.passwordHash);
return valid ? user : null;
}
export async function getUserCount(db: Db = prodDb): Promise<number> {
const [result] = await db.select({ value: count() }).from(users);
return result?.value ?? 0;
}
export async function changePassword(
db: Db = prodDb,
username: string,
currentPassword: string,
newPassword: string,
): Promise<boolean> {
const user = await verifyPassword(db, username, currentPassword);
if (!user) return false;
const newHash = await Bun.password.hash(newPassword);
await db
.update(users)
.set({ passwordHash: newHash })
.where(eq(users.id, user.id));
return true;
}
// ── Session Management ───────────────────────────────────────────────
export async function createSession(
db: Db = prodDb,
userId: number,
expiryDays = 30,
) {
const id = randomBytes(32).toString("hex");
const expiresAt = new Date(Date.now() + expiryDays * 24 * 60 * 60 * 1000);
const [row] = await db
.insert(sessions)
.values({ id, userId, expiresAt })
.returning();
return row;
}
export async function getSession(db: Db = prodDb, sessionId: string) {
const [session] = await db
.select()
.from(sessions)
.where(eq(sessions.id, sessionId));
if (!session) return null;
if (session.expiresAt < new Date()) {
await db.delete(sessions).where(eq(sessions.id, sessionId));
return null;
}
return session;
}
export async function deleteSession(db: Db = prodDb, sessionId: string) {
await db.delete(sessions).where(eq(sessions.id, sessionId));
}
export async function refreshSession(
db: Db = prodDb,
sessionId: string,
expiryDays = 30,
) {
const expiresAt = new Date(Date.now() + expiryDays * 24 * 60 * 60 * 1000);
await db
.update(sessions)
.set({ expiresAt })
.where(eq(sessions.id, sessionId));
}
// ── API Key Management ───────────────────────────────────────────────
export async function createApiKey(db: Db = prodDb, name: string) {
@@ -116,10 +12,11 @@ export async function createApiKey(db: Db = prodDb, name: string) {
const keyHash = await Bun.password.hash(rawKey);
const keyPrefix = rawKey.slice(0, 8);
const [record] = await db
const record = db
.insert(apiKeys)
.values({ name, keyHash, keyPrefix })
.returning();
.returning()
.get();
return { ...record, rawKey };
}
@@ -129,10 +26,11 @@ export async function verifyApiKey(
rawKey: string,
): Promise<boolean> {
const prefix = rawKey.slice(0, 8);
const candidates = await db
const candidates = db
.select()
.from(apiKeys)
.where(eq(apiKeys.keyPrefix, prefix));
.where(eq(apiKeys.keyPrefix, prefix))
.all();
for (const candidate of candidates) {
const valid = await Bun.password.verify(rawKey, candidate.keyHash);
@@ -142,7 +40,7 @@ export async function verifyApiKey(
return false;
}
export async function listApiKeys(db: Db = prodDb) {
export function listApiKeys(db: Db = prodDb) {
return db
.select({
id: apiKeys.id,
@@ -150,9 +48,10 @@ export async function listApiKeys(db: Db = prodDb) {
keyPrefix: apiKeys.keyPrefix,
createdAt: apiKeys.createdAt,
})
.from(apiKeys);
.from(apiKeys)
.all();
}
export async function deleteApiKey(db: Db = prodDb, id: number) {
await db.delete(apiKeys).where(eq(apiKeys.id, id));
export function deleteApiKey(db: Db = prodDb, id: number) {
db.delete(apiKeys).where(eq(apiKeys.id, id)).run();
}