import { PASSWORD_ENTRY, PASSWORD_ENTRY_STORE } from '../../../../models/password_entry.ts'; import { SESSIONS } from '../../../../models/session.ts'; import { USER, USER_STORE } from '../../../../models/user.ts'; import { USER_PERMISSIONS, USER_PERMISSIONS_STORE } from '../../../../models/user_permissions.ts'; import parse_body from '../../../../utils/bodyparser.ts'; export const PERMISSIONS: Record) => Promise> = {}; // GET /api/users/:id - Get single user PERMISSIONS.GET = (_req: Request, meta: Record): Promise => { const user_is_self = !!meta.user && !!meta.params && meta.user.id === meta.params.id; const can_read_self = meta.user_permissions?.permissions.includes('self.read'); const can_read_others = meta.user_permissions?.permissions?.includes('users.read'); return can_read_others || (can_read_self && user_is_self); }; export async function GET(_req: Request, meta: Record): Promise { const user_id: string = meta.params?.id?.toLowerCase().trim() ?? ''; const user: USER | null = user_id.length === 49 ? await USER_STORE.get(user_id) : null; // lurid is 49 chars as we use them, eg: "also-play-flow-want-form-wide-thus-work-burn-same" if (!user) { return Response.json({ error: { message: `Could not locate a user with id: "${user_id}"`, cause: 'unknown_user' } }, { status: 404 }); } const user_is_self = meta.user?.id === user.id; const has_permission_to_read = (user_is_self && meta.user_permissions?.permissions?.includes('self.read')) || (meta.user_permissions?.permissions?.includes('users.read')); if (!has_permission_to_read) { return Response.json({ error: { message: 'Permission denied.', cause: 'permission_denied' } }, { status: 400 }); } return Response.json(user, { status: 200 }); } // PUT /api/users/:id - Update user PERMISSIONS.PUT = (_req: Request, meta: Record): Promise => { const user_is_self = !!meta.user && !!meta.params && meta.user.id === meta.params.id; const can_write_self = meta.user_permissions?.permissions.includes('self.write'); const can_write_others = meta.user_permissions?.permissions?.includes('users.write'); return can_write_others || (can_write_self && user_is_self); }; export async function PUT(req: Request, meta: { params: Record }): Promise { const now = new Date().toISOString(); const id: string = meta.params.id ?? ''; const existing = await USER_STORE.get(id); if (!existing) { return Response.json({ error: { message: 'User not found', cause: 'unknown_user' } }, { status: 404 }); } try { const body = await parse_body(req); const updated = { ...existing, username: body.username || existing.username, timestamps: { created: existing.timestamps.created, updated: now } }; await USER_STORE.update(updated); return Response.json(updated, { status: 200 }); } catch (err) { return Response.json({ error: { message: (err as Error)?.message ?? 'Unknown error due to invalid user data.', cause: (err as Error)?.cause ?? 'invalid_user_data' } }, { status: 400 }); } } // DELETE /api/users/:id - Delete user PERMISSIONS.DELETE = (_req: Request, meta: Record): Promise => { const user_is_self = !!meta.user && !!meta.params && meta.user.id === meta.params.id; const can_write_self = meta.user_permissions?.permissions.includes('self.write'); const can_write_others = meta.user_permissions?.permissions?.includes('users.write'); return can_write_others || (can_write_self && user_is_self); }; export async function DELETE(_req: Request, meta: { params: Record }): Promise { const user_id: string = meta.params.id ?? ''; const user: USER | null = await USER_STORE.get(user_id); if (!user) { return Response.json({ error: { message: 'Error deleting user.', cause: 'unknown_user' } }, { status: 404 }); } const password_entry: PASSWORD_ENTRY | null = await PASSWORD_ENTRY_STORE.get(user_id); if (password_entry) { await PASSWORD_ENTRY_STORE.delete(password_entry); } const user_permissions: USER_PERMISSIONS | null = await USER_PERMISSIONS_STORE.get(user_id); if (user_permissions) { await USER_PERMISSIONS_STORE.delete(user_permissions); } const sessions = await SESSIONS.find({ user_id }); for (const session of sessions) { await SESSIONS.delete(session); } await USER_STORE.delete(user); return Response.json({ deleted: true }, { status: 200 }); }