users.js
4.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/**
* User Management API — Admin only
*
* GET /api/users List users (search, filter, sort, pagination)
* PUT /api/users/:id Update user (role, display_name)
* DELETE /api/users/:id Delete user
*/
const express = require('express');
const router = express.Router();
const userDb = require('../services/user-db');
const { requireAdmin } = require('../middleware/jwt-auth');
// All routes require admin
router.use(requireAdmin);
/**
* GET /api/users
* Query: q (search), role, provider, sort (field), order (asc/desc), page, limit
*/
router.get('/', (req, res) => {
try {
const db = userDb.getDb();
const { q, role, provider, sort = 'created_at', order = 'desc', page = 1, limit = 50 } = req.query;
let where = [];
let params = [];
if (q) {
where.push('(email LIKE ? OR display_name LIKE ? OR github_login LIKE ?)');
params.push(`%${q}%`, `%${q}%`, `%${q}%`);
}
if (role) {
where.push('role = ?');
params.push(role);
}
if (provider) {
where.push('provider = ?');
params.push(provider);
}
const whereClause = where.length > 0 ? `WHERE ${where.join(' AND ')}` : '';
// Validate sort field
const allowedSorts = ['id', 'email', 'display_name', 'role', 'provider', 'created_at', 'last_login'];
const sortField = allowedSorts.includes(sort) ? sort : 'created_at';
const sortOrder = order === 'asc' ? 'ASC' : 'DESC';
const offset = (Math.max(1, parseInt(page)) - 1) * parseInt(limit);
const lim = Math.min(200, Math.max(1, parseInt(limit)));
const total = db.prepare(`SELECT COUNT(*) as count FROM users ${whereClause}`).get(...params).count;
const users = db.prepare(
`SELECT id, email, display_name, avatar_url, provider, provider_id, github_login, role, api_key, created_at, last_login
FROM users ${whereClause}
ORDER BY ${sortField} ${sortOrder}
LIMIT ? OFFSET ?`
).all(...params, lim, offset);
res.json({
users,
pagination: {
total,
page: parseInt(page),
limit: lim,
pages: Math.ceil(total / lim),
}
});
} catch (err) {
console.error('User list error:', err);
res.status(500).json({ error: 'Failed to fetch users' });
}
});
/**
* PUT /api/users/:id
*/
router.put('/:id', (req, res) => {
try {
const db = userDb.getDb();
const userId = parseInt(req.params.id);
const user = userDb.getUserById(userId);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
// Prevent admin from removing their own admin role
if (userId === req.user.id && req.body.role && req.body.role !== 'admin') {
return res.status(400).json({ error: 'Cannot remove your own admin role' });
}
const updates = [];
const params = [];
if (req.body.role !== undefined) {
if (!['user', 'admin'].includes(req.body.role)) {
return res.status(400).json({ error: 'Invalid role. Must be "user" or "admin".' });
}
updates.push('role = ?');
params.push(req.body.role);
}
if (req.body.display_name !== undefined) {
updates.push('display_name = ?');
params.push(req.body.display_name);
}
if (updates.length === 0) {
return res.status(400).json({ error: 'No fields to update' });
}
params.push(userId);
db.prepare(`UPDATE users SET ${updates.join(', ')} WHERE id = ?`).run(...params);
const updated = userDb.getUserById(userId);
res.json({ user: userDb.toPublicUser(updated) });
} catch (err) {
console.error('User update error:', err);
res.status(500).json({ error: 'Failed to update user' });
}
});
/**
* DELETE /api/users/:id
*/
router.delete('/:id', (req, res) => {
try {
const db = userDb.getDb();
const userId = parseInt(req.params.id);
// Prevent self-deletion
if (userId === req.user.id) {
return res.status(400).json({ error: 'Cannot delete your own account' });
}
const user = userDb.getUserById(userId);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
db.prepare('DELETE FROM users WHERE id = ?').run(userId);
res.json({ success: true, message: `User ${user.email} deleted` });
} catch (err) {
console.error('User delete error:', err);
res.status(500).json({ error: 'Failed to delete user' });
}
});
module.exports = router;