import { Hono } from 'hono' import { authMiddleware, requireRole } from '../middleware/auth' import { hashPassword } from '../utils/password' import { generateId } from '../utils/jwt' import type { Env, ApiResponse, PageResponse, User, UserPermission } from '../types' const user = new Hono<{ Bindings: Env }>() /** * 创建成功响应 */ function success(data: T, msg = 'success'): ApiResponse { return { code: 200, msg, data } } /** * 创建错误响应 */ function error(msg: string, code = 500): ApiResponse { return { code, msg, data: null } } // 所有用户管理接口都需要认证和 admin 角色 user.use('*', authMiddleware()) user.use('*', requireRole('admin')) // ==================== 用户管理 ==================== /** * 获取用户列表 * GET /api/users */ user.get('/', async (c) => { try { const { role, status, search, pageSize = '20', page = '1' } = c.req.query() const limit = Math.min(parseInt(pageSize), 100) const offset = (parseInt(page) - 1) * limit let sql = 'SELECT id, username, email, role, status, last_login, created_at, updated_at FROM users WHERE is_deleted = 0' const params: (string | number)[] = [] if (role) { sql += ' AND role = ?' params.push(role) } if (status) { sql += ' AND status = ?' params.push(status) } if (search) { sql += ' AND (username LIKE ? OR email LIKE ?)' params.push(`%${search}%`, `%${search}%`) } // 获取总数 const countResult = await c.env.DB .prepare(sql.replace('SELECT id, username, email, role, status, last_login, created_at, updated_at', 'SELECT COUNT(*) as count')) .bind(...params) .first<{ count: number }>() // 获取分页数据 sql += ' ORDER BY created_at DESC LIMIT ? OFFSET ?' params.push(limit, offset) const users = await c.env.DB .prepare(sql) .bind(...params) .all() const data: PageResponse = { rows: users.results || [], total: countResult?.count || 0, } return c.json(success(data)) } catch (err) { console.error('List users error:', err) return c.json(error(err instanceof Error ? err.message : '获取用户列表失败')) } }) /** * 获取用户详情 * GET /api/users/:id */ user.get('/:id', async (c) => { try { const userId = c.req.param('id') const userData = await c.env.DB .prepare('SELECT id, username, email, role, status, last_login, created_at, updated_at FROM users WHERE id = ? AND is_deleted = 0') .bind(userId) .first() if (!userData) { return c.json(error('用户不存在', 404), 404) } return c.json(success(userData)) } catch (err) { console.error('Get user error:', err) return c.json(error(err instanceof Error ? err.message : '获取用户详情失败')) } }) /** * 创建用户 * POST /api/users */ user.post('/', async (c) => { try { const body = await c.req.json<{ username: string password: string email?: string role?: 'admin' | 'operator' | 'viewer' }>() if (!body.username || !body.password) { return c.json(error('用户名和密码不能为空', 400), 400) } // 检查用户名是否已存在 const existing = await c.env.DB .prepare('SELECT id FROM users WHERE username = ?') .bind(body.username) .first() if (existing) { return c.json(error('用户名已存在', 400), 400) } const userId = generateId() const passwordHash = await hashPassword(body.password) const now = Math.floor(Date.now() / 1000) await c.env.DB .prepare(` INSERT INTO users (id, username, email, password_hash, role, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `) .bind( userId, body.username, body.email || null, passwordHash, body.role || 'viewer', 'active', now, now ) .run() // 记录操作日志 const adminUser = c.get('user') await c.env.DB .prepare(` INSERT INTO audit_logs (id, user_id, action, resource, resource_id, details, created_at) VALUES (?, ?, ?, ?, ?, ?, ?) `) .bind(generateId(), adminUser.sub, 'create', 'user', userId, JSON.stringify({ username: body.username }), now) .run() const newUser = await c.env.DB .prepare('SELECT id, username, email, role, status, created_at, updated_at FROM users WHERE id = ?') .bind(userId) .first() return c.json(success(newUser, '用户创建成功')) } catch (err) { console.error('Create user error:', err) return c.json(error(err instanceof Error ? err.message : '创建用户失败')) } }) /** * 更新用户 * PUT /api/users/:id */ user.put('/:id', async (c) => { try { const userId = c.req.param('id') const body = await c.req.json<{ email?: string role?: 'admin' | 'operator' | 'viewer' status?: 'active' | 'disabled' password?: string }>() const existing = await c.env.DB .prepare('SELECT id FROM users WHERE id = ? AND is_deleted = 0') .bind(userId) .first() if (!existing) { return c.json(error('用户不存在', 404), 404) } const updates: string[] = [] const params: (string | number)[] = [] if (body.email !== undefined) { updates.push('email = ?') params.push(body.email) } if (body.role) { updates.push('role = ?') params.push(body.role) } if (body.status) { updates.push('status = ?') params.push(body.status) } if (body.password) { const passwordHash = await hashPassword(body.password) updates.push('password_hash = ?') params.push(passwordHash) } if (updates.length === 0) { return c.json(error('没有要更新的字段', 400), 400) } const now = Math.floor(Date.now() / 1000) updates.push('updated_at = ?') params.push(now) params.push(userId) await c.env.DB .prepare(`UPDATE users SET ${updates.join(', ')} WHERE id = ?`) .bind(...params) .run() // 记录操作日志 const adminUser = c.get('user') await c.env.DB .prepare(` INSERT INTO audit_logs (id, user_id, action, resource, resource_id, details, created_at) VALUES (?, ?, ?, ?, ?, ?, ?) `) .bind(generateId(), adminUser.sub, 'update', 'user', userId, JSON.stringify(body), now) .run() const updatedUser = await c.env.DB .prepare('SELECT id, username, email, role, status, created_at, updated_at FROM users WHERE id = ?') .bind(userId) .first() return c.json(success(updatedUser, '用户更新成功')) } catch (err) { console.error('Update user error:', err) return c.json(error(err instanceof Error ? err.message : '更新用户失败')) } }) /** * 删除用户 * DELETE /api/users/:id */ user.delete('/:id', async (c) => { try { const userId = c.req.param('id') const adminUser = c.get('user') // 不能删除自己 if (userId === adminUser.sub) { return c.json(error('不能删除自己', 400), 400) } const existing = await c.env.DB .prepare('SELECT id, username FROM users WHERE id = ? AND is_deleted = 0') .bind(userId) .first() if (!existing) { return c.json(error('用户不存在', 404), 404) } const now = Math.floor(Date.now() / 1000) // 软删除时清空 email,避免 UNIQUE 约束冲突 await c.env.DB .prepare('UPDATE users SET is_deleted = 1, email = NULL, updated_at = ? WHERE id = ?') .bind(now, userId) .run() // 记录操作日志 await c.env.DB .prepare(` INSERT INTO audit_logs (id, user_id, action, resource, resource_id, details, created_at) VALUES (?, ?, ?, ?, ?, ?, ?) `) .bind(generateId(), adminUser.sub, 'delete', 'user', userId, JSON.stringify({ username: existing.username }), now) .run() return c.json(success(null, '用户删除成功')) } catch (err) { console.error('Delete user error:', err) return c.json(error(err instanceof Error ? err.message : '删除用户失败')) } }) // ==================== 权限管理 ==================== /** * 获取用户权限列表 * GET /api/users/:id/permissions */ user.get('/:id/permissions', async (c) => { try { const userId = c.req.param('id') const permissions = await c.env.DB .prepare(` SELECT up.*, c.name as camera_name FROM user_permissions up LEFT JOIN cameras c ON up.camera_id = c.id WHERE up.user_id = ? AND up.is_deleted = 0 ORDER BY up.granted_at DESC `) .bind(userId) .all() return c.json(success(permissions.results || [])) } catch (err) { console.error('List permissions error:', err) return c.json(error(err instanceof Error ? err.message : '获取权限列表失败')) } }) /** * 添加用户权限 * POST /api/users/:id/permissions */ user.post('/:id/permissions', async (c) => { try { const userId = c.req.param('id') const adminUser = c.get('user') const body = await c.req.json<{ camera_id: string permission: 'view' | 'control' | 'manage' }>() if (!body.camera_id || !body.permission) { return c.json(error('camera_id 和 permission 不能为空', 400), 400) } // 检查用户是否存在 const userExists = await c.env.DB .prepare('SELECT id FROM users WHERE id = ? AND is_deleted = 0') .bind(userId) .first() if (!userExists) { return c.json(error('用户不存在', 404), 404) } // 检查摄像头是否存在 const cameraExists = await c.env.DB .prepare('SELECT id FROM cameras WHERE id = ? AND is_deleted = 0') .bind(body.camera_id) .first() if (!cameraExists) { return c.json(error('摄像头不存在', 404), 404) } // 检查是否已存在权限 const existing = await c.env.DB .prepare('SELECT id FROM user_permissions WHERE user_id = ? AND camera_id = ? AND is_deleted = 0') .bind(userId, body.camera_id) .first() const now = Math.floor(Date.now() / 1000) if (existing) { // 更新现有权限 await c.env.DB .prepare('UPDATE user_permissions SET permission = ?, granted_at = ?, granted_by = ? WHERE user_id = ? AND camera_id = ?') .bind(body.permission, now, adminUser.sub, userId, body.camera_id) .run() } else { // 创建新权限 await c.env.DB .prepare(` INSERT INTO user_permissions (id, user_id, camera_id, permission, granted_at, granted_by) VALUES (?, ?, ?, ?, ?, ?) `) .bind(generateId(), userId, body.camera_id, body.permission, now, adminUser.sub) .run() } return c.json(success(null, '权限设置成功')) } catch (err) { console.error('Add permission error:', err) return c.json(error(err instanceof Error ? err.message : '设置权限失败')) } }) /** * 删除用户权限 * DELETE /api/users/:id/permissions/:permissionId */ user.delete('/:id/permissions/:permissionId', async (c) => { try { const userId = c.req.param('id') const permissionId = c.req.param('permissionId') const existing = await c.env.DB .prepare('SELECT id FROM user_permissions WHERE id = ? AND user_id = ? AND is_deleted = 0') .bind(permissionId, userId) .first() if (!existing) { return c.json(error('权限不存在', 404), 404) } await c.env.DB .prepare('UPDATE user_permissions SET is_deleted = 1 WHERE id = ?') .bind(permissionId) .run() return c.json(success(null, '权限删除成功')) } catch (err) { console.error('Delete permission error:', err) return c.json(error(err instanceof Error ? err.message : '删除权限失败')) } }) export default user