feat: one account can bind both netease + skin identities
- db: user_identities table (UNIQUE user_id+source), migrate backfill from users - auth: GET/POST/DELETE /api/auth/identities - bind/unbind/list identities (netease requires UID, one per source, name uniqueness, keep >=1) - register/external/plugin/admin-created users auto-write primary identity - tickets: submit uses selected identity (validated belongs to user) - dashboard: 我的身份 card with bind/unbind UI - ticket-create: identity selector when >1 identity
This commit is contained in:
@@ -30,6 +30,10 @@ router.post('/', authenticate, requireRole('owner','admin'), async (req, res) =>
|
||||
if (await getRow('SELECT id FROM users WHERE email = ?', [email])) return res.status(400).json({ error: '邮箱已被使用' });
|
||||
const hashed = bcrypt.hashSync(password, 10);
|
||||
const r = await query('INSERT INTO users(username, password, email, game_name, game_uid, role, source, active, email_verified) VALUES (?,?,?,?,?,?,?,1,1)', [username, hashed, email, game_name, game_uid, role, source || 'netease']);
|
||||
try {
|
||||
await query('INSERT INTO user_identities(user_id, source, game_name, game_uid) VALUES (?,?,?,?)',
|
||||
[r.insertId, source || 'netease', game_name, game_uid]);
|
||||
} catch {}
|
||||
await query("INSERT INTO audit_logs(user_id, username, action, entity_type, entity_id, details) VALUES (?,?,?,?,?,?)", [req.user.id, req.user.username, 'create_user', 'user', r.insertId, `创建用户 ${username}`]);
|
||||
res.status(201).json({ id: r.insertId, message: '创建成功' });
|
||||
});
|
||||
@@ -50,6 +54,17 @@ router.put('/:id', authenticate, requireRole('owner','admin'), async (req, res)
|
||||
const sets = Object.keys(fields).map(k => `${k} = ?`).join(', ');
|
||||
await query(`UPDATE users SET ${sets} WHERE id = ?`, [...Object.values(fields), req.params.id]);
|
||||
|
||||
// 主身份字段变化时同步 user_identities 中对应来源
|
||||
if (fields.game_name !== undefined || fields.source !== undefined || fields.game_uid !== undefined) {
|
||||
const cur = await getRow('SELECT source, game_name, game_uid FROM users WHERE id = ?', [req.params.id]);
|
||||
if (cur?.game_name) {
|
||||
try {
|
||||
await query('INSERT INTO user_identities(user_id, source, game_name, game_uid) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE game_name = VALUES(game_name), game_uid = VALUES(game_uid)',
|
||||
[req.params.id, cur.source || 'netease', cur.game_name, cur.game_uid || '']);
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
if (req.body.admin_servers !== undefined) {
|
||||
await query('DELETE FROM user_servers WHERE user_id = ?', [req.params.id]);
|
||||
if (Array.isArray(req.body.admin_servers)) {
|
||||
|
||||
Reference in New Issue
Block a user