user.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. import { Hono } from 'hono'
  2. import { authMiddleware, requireRole } from '../middleware/auth'
  3. import { hashPassword } from '../utils/password'
  4. import { generateId } from '../utils/jwt'
  5. import type { Env, ApiResponse, PageResponse, User, UserPermission } from '../types'
  6. const user = new Hono<{ Bindings: Env }>()
  7. /**
  8. * 创建成功响应
  9. */
  10. function success<T>(data: T, msg = 'success'): ApiResponse<T> {
  11. return { code: 200, msg, data }
  12. }
  13. /**
  14. * 创建错误响应
  15. */
  16. function error(msg: string, code = 500): ApiResponse<null> {
  17. return { code, msg, data: null }
  18. }
  19. // 所有用户管理接口都需要认证和 admin 角色
  20. user.use('*', authMiddleware())
  21. user.use('*', requireRole('admin'))
  22. // ==================== 用户管理 ====================
  23. /**
  24. * 获取用户列表
  25. * GET /api/users
  26. */
  27. user.get('/', async (c) => {
  28. try {
  29. const { role, status, search, pageSize = '20', page = '1' } = c.req.query()
  30. const limit = Math.min(parseInt(pageSize), 100)
  31. const offset = (parseInt(page) - 1) * limit
  32. let sql = 'SELECT id, username, email, role, status, last_login, created_at, updated_at FROM users WHERE is_deleted = 0'
  33. const params: (string | number)[] = []
  34. if (role) {
  35. sql += ' AND role = ?'
  36. params.push(role)
  37. }
  38. if (status) {
  39. sql += ' AND status = ?'
  40. params.push(status)
  41. }
  42. if (search) {
  43. sql += ' AND (username LIKE ? OR email LIKE ?)'
  44. params.push(`%${search}%`, `%${search}%`)
  45. }
  46. // 获取总数
  47. const countResult = await c.env.DB
  48. .prepare(sql.replace('SELECT id, username, email, role, status, last_login, created_at, updated_at', 'SELECT COUNT(*) as count'))
  49. .bind(...params)
  50. .first<{ count: number }>()
  51. // 获取分页数据
  52. sql += ' ORDER BY created_at DESC LIMIT ? OFFSET ?'
  53. params.push(limit, offset)
  54. const users = await c.env.DB
  55. .prepare(sql)
  56. .bind(...params)
  57. .all<User>()
  58. const data: PageResponse<User> = {
  59. rows: users.results || [],
  60. total: countResult?.count || 0,
  61. }
  62. return c.json(success(data))
  63. } catch (err) {
  64. console.error('List users error:', err)
  65. return c.json(error(err instanceof Error ? err.message : '获取用户列表失败'))
  66. }
  67. })
  68. /**
  69. * 获取用户详情
  70. * GET /api/users/:id
  71. */
  72. user.get('/:id', async (c) => {
  73. try {
  74. const userId = c.req.param('id')
  75. const userData = await c.env.DB
  76. .prepare('SELECT id, username, email, role, status, last_login, created_at, updated_at FROM users WHERE id = ? AND is_deleted = 0')
  77. .bind(userId)
  78. .first<User>()
  79. if (!userData) {
  80. return c.json(error('用户不存在', 404), 404)
  81. }
  82. return c.json(success(userData))
  83. } catch (err) {
  84. console.error('Get user error:', err)
  85. return c.json(error(err instanceof Error ? err.message : '获取用户详情失败'))
  86. }
  87. })
  88. /**
  89. * 创建用户
  90. * POST /api/users
  91. */
  92. user.post('/', async (c) => {
  93. try {
  94. const body = await c.req.json<{
  95. username: string
  96. password: string
  97. email?: string
  98. role?: 'admin' | 'operator' | 'viewer'
  99. }>()
  100. if (!body.username || !body.password) {
  101. return c.json(error('用户名和密码不能为空', 400), 400)
  102. }
  103. // 检查用户名是否已存在
  104. const existing = await c.env.DB
  105. .prepare('SELECT id FROM users WHERE username = ?')
  106. .bind(body.username)
  107. .first()
  108. if (existing) {
  109. return c.json(error('用户名已存在', 400), 400)
  110. }
  111. const userId = generateId()
  112. const passwordHash = await hashPassword(body.password)
  113. const now = Math.floor(Date.now() / 1000)
  114. await c.env.DB
  115. .prepare(`
  116. INSERT INTO users (id, username, email, password_hash, role, status, created_at, updated_at)
  117. VALUES (?, ?, ?, ?, ?, ?, ?, ?)
  118. `)
  119. .bind(
  120. userId,
  121. body.username,
  122. body.email || null,
  123. passwordHash,
  124. body.role || 'viewer',
  125. 'active',
  126. now,
  127. now
  128. )
  129. .run()
  130. // 记录操作日志
  131. const adminUser = c.get('user')
  132. await c.env.DB
  133. .prepare(`
  134. INSERT INTO audit_logs (id, user_id, action, resource, resource_id, details, created_at)
  135. VALUES (?, ?, ?, ?, ?, ?, ?)
  136. `)
  137. .bind(generateId(), adminUser.sub, 'create', 'user', userId, JSON.stringify({ username: body.username }), now)
  138. .run()
  139. const newUser = await c.env.DB
  140. .prepare('SELECT id, username, email, role, status, created_at, updated_at FROM users WHERE id = ?')
  141. .bind(userId)
  142. .first<User>()
  143. return c.json(success(newUser, '用户创建成功'))
  144. } catch (err) {
  145. console.error('Create user error:', err)
  146. return c.json(error(err instanceof Error ? err.message : '创建用户失败'))
  147. }
  148. })
  149. /**
  150. * 更新用户
  151. * PUT /api/users/:id
  152. */
  153. user.put('/:id', async (c) => {
  154. try {
  155. const userId = c.req.param('id')
  156. const body = await c.req.json<{
  157. email?: string
  158. role?: 'admin' | 'operator' | 'viewer'
  159. status?: 'active' | 'disabled'
  160. password?: string
  161. }>()
  162. const existing = await c.env.DB
  163. .prepare('SELECT id FROM users WHERE id = ? AND is_deleted = 0')
  164. .bind(userId)
  165. .first()
  166. if (!existing) {
  167. return c.json(error('用户不存在', 404), 404)
  168. }
  169. const updates: string[] = []
  170. const params: (string | number)[] = []
  171. if (body.email !== undefined) {
  172. updates.push('email = ?')
  173. params.push(body.email)
  174. }
  175. if (body.role) {
  176. updates.push('role = ?')
  177. params.push(body.role)
  178. }
  179. if (body.status) {
  180. updates.push('status = ?')
  181. params.push(body.status)
  182. }
  183. if (body.password) {
  184. const passwordHash = await hashPassword(body.password)
  185. updates.push('password_hash = ?')
  186. params.push(passwordHash)
  187. }
  188. if (updates.length === 0) {
  189. return c.json(error('没有要更新的字段', 400), 400)
  190. }
  191. const now = Math.floor(Date.now() / 1000)
  192. updates.push('updated_at = ?')
  193. params.push(now)
  194. params.push(userId)
  195. await c.env.DB
  196. .prepare(`UPDATE users SET ${updates.join(', ')} WHERE id = ?`)
  197. .bind(...params)
  198. .run()
  199. // 记录操作日志
  200. const adminUser = c.get('user')
  201. await c.env.DB
  202. .prepare(`
  203. INSERT INTO audit_logs (id, user_id, action, resource, resource_id, details, created_at)
  204. VALUES (?, ?, ?, ?, ?, ?, ?)
  205. `)
  206. .bind(generateId(), adminUser.sub, 'update', 'user', userId, JSON.stringify(body), now)
  207. .run()
  208. const updatedUser = await c.env.DB
  209. .prepare('SELECT id, username, email, role, status, created_at, updated_at FROM users WHERE id = ?')
  210. .bind(userId)
  211. .first<User>()
  212. return c.json(success(updatedUser, '用户更新成功'))
  213. } catch (err) {
  214. console.error('Update user error:', err)
  215. return c.json(error(err instanceof Error ? err.message : '更新用户失败'))
  216. }
  217. })
  218. /**
  219. * 删除用户
  220. * DELETE /api/users/:id
  221. */
  222. user.delete('/:id', async (c) => {
  223. try {
  224. const userId = c.req.param('id')
  225. const adminUser = c.get('user')
  226. // 不能删除自己
  227. if (userId === adminUser.sub) {
  228. return c.json(error('不能删除自己', 400), 400)
  229. }
  230. const existing = await c.env.DB
  231. .prepare('SELECT id, username FROM users WHERE id = ? AND is_deleted = 0')
  232. .bind(userId)
  233. .first<User>()
  234. if (!existing) {
  235. return c.json(error('用户不存在', 404), 404)
  236. }
  237. const now = Math.floor(Date.now() / 1000)
  238. // 软删除时清空 email,避免 UNIQUE 约束冲突
  239. await c.env.DB
  240. .prepare('UPDATE users SET is_deleted = 1, email = NULL, updated_at = ? WHERE id = ?')
  241. .bind(now, userId)
  242. .run()
  243. // 记录操作日志
  244. await c.env.DB
  245. .prepare(`
  246. INSERT INTO audit_logs (id, user_id, action, resource, resource_id, details, created_at)
  247. VALUES (?, ?, ?, ?, ?, ?, ?)
  248. `)
  249. .bind(generateId(), adminUser.sub, 'delete', 'user', userId, JSON.stringify({ username: existing.username }), now)
  250. .run()
  251. return c.json(success(null, '用户删除成功'))
  252. } catch (err) {
  253. console.error('Delete user error:', err)
  254. return c.json(error(err instanceof Error ? err.message : '删除用户失败'))
  255. }
  256. })
  257. // ==================== 权限管理 ====================
  258. /**
  259. * 获取用户权限列表
  260. * GET /api/users/:id/permissions
  261. */
  262. user.get('/:id/permissions', async (c) => {
  263. try {
  264. const userId = c.req.param('id')
  265. const permissions = await c.env.DB
  266. .prepare(`
  267. SELECT up.*, c.name as camera_name
  268. FROM user_permissions up
  269. LEFT JOIN cameras c ON up.camera_id = c.id
  270. WHERE up.user_id = ? AND up.is_deleted = 0
  271. ORDER BY up.granted_at DESC
  272. `)
  273. .bind(userId)
  274. .all<UserPermission & { camera_name: string }>()
  275. return c.json(success(permissions.results || []))
  276. } catch (err) {
  277. console.error('List permissions error:', err)
  278. return c.json(error(err instanceof Error ? err.message : '获取权限列表失败'))
  279. }
  280. })
  281. /**
  282. * 添加用户权限
  283. * POST /api/users/:id/permissions
  284. */
  285. user.post('/:id/permissions', async (c) => {
  286. try {
  287. const userId = c.req.param('id')
  288. const adminUser = c.get('user')
  289. const body = await c.req.json<{
  290. camera_id: string
  291. permission: 'view' | 'control' | 'manage'
  292. }>()
  293. if (!body.camera_id || !body.permission) {
  294. return c.json(error('camera_id 和 permission 不能为空', 400), 400)
  295. }
  296. // 检查用户是否存在
  297. const userExists = await c.env.DB
  298. .prepare('SELECT id FROM users WHERE id = ? AND is_deleted = 0')
  299. .bind(userId)
  300. .first()
  301. if (!userExists) {
  302. return c.json(error('用户不存在', 404), 404)
  303. }
  304. // 检查摄像头是否存在
  305. const cameraExists = await c.env.DB
  306. .prepare('SELECT id FROM cameras WHERE id = ? AND is_deleted = 0')
  307. .bind(body.camera_id)
  308. .first()
  309. if (!cameraExists) {
  310. return c.json(error('摄像头不存在', 404), 404)
  311. }
  312. // 检查是否已存在权限
  313. const existing = await c.env.DB
  314. .prepare('SELECT id FROM user_permissions WHERE user_id = ? AND camera_id = ? AND is_deleted = 0')
  315. .bind(userId, body.camera_id)
  316. .first()
  317. const now = Math.floor(Date.now() / 1000)
  318. if (existing) {
  319. // 更新现有权限
  320. await c.env.DB
  321. .prepare('UPDATE user_permissions SET permission = ?, granted_at = ?, granted_by = ? WHERE user_id = ? AND camera_id = ?')
  322. .bind(body.permission, now, adminUser.sub, userId, body.camera_id)
  323. .run()
  324. } else {
  325. // 创建新权限
  326. await c.env.DB
  327. .prepare(`
  328. INSERT INTO user_permissions (id, user_id, camera_id, permission, granted_at, granted_by)
  329. VALUES (?, ?, ?, ?, ?, ?)
  330. `)
  331. .bind(generateId(), userId, body.camera_id, body.permission, now, adminUser.sub)
  332. .run()
  333. }
  334. return c.json(success(null, '权限设置成功'))
  335. } catch (err) {
  336. console.error('Add permission error:', err)
  337. return c.json(error(err instanceof Error ? err.message : '设置权限失败'))
  338. }
  339. })
  340. /**
  341. * 删除用户权限
  342. * DELETE /api/users/:id/permissions/:permissionId
  343. */
  344. user.delete('/:id/permissions/:permissionId', async (c) => {
  345. try {
  346. const userId = c.req.param('id')
  347. const permissionId = c.req.param('permissionId')
  348. const existing = await c.env.DB
  349. .prepare('SELECT id FROM user_permissions WHERE id = ? AND user_id = ? AND is_deleted = 0')
  350. .bind(permissionId, userId)
  351. .first()
  352. if (!existing) {
  353. return c.json(error('权限不存在', 404), 404)
  354. }
  355. await c.env.DB
  356. .prepare('UPDATE user_permissions SET is_deleted = 1 WHERE id = ?')
  357. .bind(permissionId)
  358. .run()
  359. return c.json(success(null, '权限删除成功'))
  360. } catch (err) {
  361. console.error('Delete permission error:', err)
  362. return c.json(error(err instanceof Error ? err.message : '删除权限失败'))
  363. }
  364. })
  365. export default user