Separate login and display identities

This commit is contained in:
JB
2026-07-24 23:45:08 -04:00
parent 05acc022c7
commit c0834804d4
13 changed files with 182 additions and 46 deletions
+23 -1
View File
@@ -10,9 +10,15 @@ export const load: PageServerLoad = async ({ cookies, params }) => {
const operator = await requirePermission(await getSessionUser(cookies), 'users.view');
const database = await getDatabase();
const [profile, roles, permissions, assignedRoles, directPermissions, logins, builds, comments] = await Promise.all([
(await database.prepare(`SELECT users.id,users.username,users.role,users.discord_id,users.created_at,user_status.*,
(await database.prepare(`SELECT users.id,users.username,COALESCE(user_profiles.display_name,users.username) AS display_name,
users.role,users.discord_id,users.created_at,
CASE WHEN users.password_hash IS NOT NULL THEN 1 ELSE 0 END AS has_password,
discord_profiles.username AS discord_username,discord_profiles.display_name AS discord_display_name,
discord_profiles.avatar_hash AS discord_avatar_hash,user_status.*,
deleted_accounts.deleted_at,deleted_accounts.deleted_by,deleted_accounts.reason AS deletion_reason
FROM users LEFT JOIN user_status ON user_status.user_id=users.id
LEFT JOIN user_profiles ON user_profiles.user_id=users.id
LEFT JOIN discord_profiles ON discord_profiles.user_id=users.id
LEFT JOIN deleted_accounts ON deleted_accounts.user_id=users.id WHERE users.id=?`)).get(params.id),
(await database.prepare('SELECT * FROM roles ORDER BY name')).all(),
(await database.prepare('SELECT * FROM permissions ORDER BY name')).all(),
@@ -102,5 +108,21 @@ export const actions: Actions = {
return fail(409, { adminDeleteError: 'This account has already been deleted.' });
}
return { accountDeleted: true };
},
unlinkDiscord: async ({ cookies, params }) => {
const operator = await requirePermission(await getSessionUser(cookies), 'users.identities');
const database = await getDatabase();
const target = await (await database.prepare('SELECT discord_id,password_hash FROM users WHERE id=?')).get(params.id);
if (!target) return fail(404, { identityError: 'User not found.' });
if (!target.discord_id) return fail(409, { identityError: 'This user does not have a linked Discord account.' });
if (typeof target.password_hash !== 'string') {
return fail(400, { identityError: 'Discord is this user’s only sign-in method. They must set a password before it can be unlinked.' });
}
const now = new Date().toISOString();
await (await database.prepare('UPDATE users SET discord_id=NULL WHERE id=?')).run(params.id);
await (await database.prepare('DELETE FROM discord_profiles WHERE user_id=?')).run(params.id);
await (await database.prepare('INSERT INTO audit_log(id,actor_id,entity_type,entity_id,action,before_json,created_at) VALUES(?,?,?,?,?,?,?)'))
.run(randomUUID(), operator.id, 'user', params.id, 'identity.discord_unlinked', JSON.stringify({ discordId: target.discord_id }), now);
return { identityUnlinked: true };
}
};