/** * User API handlers */ import type { IApiContext, IApiResponse } from '../router.ts'; import { PermissionService } from '../../services/permission.service.ts'; import { AuthService } from '../../services/auth.service.ts'; import { User } from '../../models/user.ts'; export class UserApi { private permissionService: PermissionService; constructor(permissionService: PermissionService) { this.permissionService = permissionService; } /** * GET /api/v1/users */ public async list(ctx: IApiContext): Promise { if (!ctx.actor?.userId) { return { status: 401, body: { error: 'Authentication required' } }; } // Only system admins can list all users if (!ctx.actor.user?.isSystemAdmin) { return { status: 403, body: { error: 'System admin access required' } }; } try { const users = await User.getInstances({}); return { status: 200, body: { users: users.map((u) => ({ id: u.id, email: u.email, username: u.username, displayName: u.displayName, isSystemAdmin: u.isSystemAdmin, isActive: u.isActive, createdAt: u.createdAt, lastLoginAt: u.lastLoginAt, })), }, }; } catch (error) { console.error('[UserApi] List error:', error); return { status: 500, body: { error: 'Failed to list users' } }; } } /** * GET /api/v1/users/:id */ public async get(ctx: IApiContext): Promise { if (!ctx.actor?.userId) { return { status: 401, body: { error: 'Authentication required' } }; } const { id } = ctx.params; // Users can view their own profile, admins can view any if (id !== ctx.actor.userId && !ctx.actor.user?.isSystemAdmin) { return { status: 403, body: { error: 'Access denied' } }; } try { const user = await User.findById(id); if (!user) { return { status: 404, body: { error: 'User not found' } }; } return { status: 200, body: { id: user.id, email: user.email, username: user.username, displayName: user.displayName, avatarUrl: user.avatarUrl, isSystemAdmin: user.isSystemAdmin, isActive: user.isActive, createdAt: user.createdAt, lastLoginAt: user.lastLoginAt, }, }; } catch (error) { console.error('[UserApi] Get error:', error); return { status: 500, body: { error: 'Failed to get user' } }; } } /** * POST /api/v1/users */ public async create(ctx: IApiContext): Promise { if (!ctx.actor?.userId) { return { status: 401, body: { error: 'Authentication required' } }; } // Only system admins can create users if (!ctx.actor.user?.isSystemAdmin) { return { status: 403, body: { error: 'System admin access required' } }; } try { const body = await ctx.request.json(); const { email, username, password, displayName, isSystemAdmin } = body; if (!email || !username || !password) { return { status: 400, body: { error: 'Email, username, and password are required' }, }; } // Check if email already exists const existing = await User.findByEmail(email); if (existing) { return { status: 409, body: { error: 'Email already in use' } }; } // Check if username already exists const existingUsername = await User.findByUsername(username); if (existingUsername) { return { status: 409, body: { error: 'Username already in use' } }; } // Hash password const passwordHash = await AuthService.hashPassword(password); // Create user const user = new User(); user.id = await User.getNewId(); user.email = email; user.username = username; user.passwordHash = passwordHash; user.displayName = displayName || username; user.isSystemAdmin = isSystemAdmin || false; user.isActive = true; user.createdAt = new Date(); await user.save(); return { status: 201, body: { id: user.id, email: user.email, username: user.username, displayName: user.displayName, isSystemAdmin: user.isSystemAdmin, createdAt: user.createdAt, }, }; } catch (error) { console.error('[UserApi] Create error:', error); return { status: 500, body: { error: 'Failed to create user' } }; } } /** * PUT /api/v1/users/:id */ public async update(ctx: IApiContext): Promise { if (!ctx.actor?.userId) { return { status: 401, body: { error: 'Authentication required' } }; } const { id } = ctx.params; // Users can update their own profile, admins can update any if (id !== ctx.actor.userId && !ctx.actor.user?.isSystemAdmin) { return { status: 403, body: { error: 'Access denied' } }; } try { const user = await User.findById(id); if (!user) { return { status: 404, body: { error: 'User not found' } }; } const body = await ctx.request.json(); const { displayName, avatarUrl, password, isActive, isSystemAdmin } = body; if (displayName !== undefined) user.displayName = displayName; if (avatarUrl !== undefined) user.avatarUrl = avatarUrl; // Only admins can change these if (ctx.actor.user?.isSystemAdmin) { if (isActive !== undefined) user.isActive = isActive; if (isSystemAdmin !== undefined) user.isSystemAdmin = isSystemAdmin; } // Password change if (password) { user.passwordHash = await AuthService.hashPassword(password); } await user.save(); return { status: 200, body: { id: user.id, email: user.email, username: user.username, displayName: user.displayName, avatarUrl: user.avatarUrl, isSystemAdmin: user.isSystemAdmin, isActive: user.isActive, }, }; } catch (error) { console.error('[UserApi] Update error:', error); return { status: 500, body: { error: 'Failed to update user' } }; } } /** * DELETE /api/v1/users/:id */ public async delete(ctx: IApiContext): Promise { if (!ctx.actor?.userId) { return { status: 401, body: { error: 'Authentication required' } }; } // Only system admins can delete users if (!ctx.actor.user?.isSystemAdmin) { return { status: 403, body: { error: 'System admin access required' } }; } const { id } = ctx.params; // Cannot delete yourself if (id === ctx.actor.userId) { return { status: 400, body: { error: 'Cannot delete your own account' } }; } try { const user = await User.findById(id); if (!user) { return { status: 404, body: { error: 'User not found' } }; } // Soft delete - deactivate instead of removing user.isActive = false; await user.save(); return { status: 200, body: { message: 'User deactivated successfully' }, }; } catch (error) { console.error('[UserApi] Delete error:', error); return { status: 500, body: { error: 'Failed to delete user' } }; } } }