import type { Database } from '@tursodatabase/database'; import { randomUUID } from 'node:crypto'; import { deleteUploadedAvatar } from '$lib/server/avatars'; export async function softDeleteAccount( database: Database, userId: string, deletedBy: string, reason: string | null, purgeSecurityData = false ): Promise { const user = await (await database.prepare(` SELECT users.username,users.login_username,user_profiles.display_name,deleted_accounts.deleted_at FROM users LEFT JOIN user_profiles ON user_profiles.user_id=users.id LEFT JOIN deleted_accounts ON deleted_accounts.user_id=users.id WHERE users.id=? `)).get(userId); if (!user || user.deleted_at) return false; const now = new Date().toISOString(); const deletedUsername = `deleted-agent-${userId.slice(0, 12)}`; await (await database.prepare( 'INSERT INTO deleted_accounts(user_id,original_username,deleted_at,deleted_by,reason) VALUES(?,?,?,?,?)' )).run(userId, purgeSecurityData ? deletedUsername : String(user.login_username ?? user.username), now, deletedBy, reason); await (await database.prepare( `UPDATE users SET username=?,login_username=NULL,password_hash=NULL,discord_id=NULL,role='user' WHERE id=?` )).run(deletedUsername, userId); await (await database.prepare( `INSERT INTO user_profiles(user_id,display_name,bio_markdown,is_public,bio_updated_at,updated_at) VALUES(?,?,?,0,?,?) ON CONFLICT(user_id) DO UPDATE SET display_name=excluded.display_name, bio_markdown='', is_public=0, bio_updated_at=excluded.bio_updated_at, updated_at=excluded.updated_at` )).run(userId, 'Deleted Agent', '', now, now); await (await database.prepare('DELETE FROM sessions WHERE user_id=?')).run(userId); await (await database.prepare('DELETE FROM magic_link_tokens WHERE user_id=?')).run(userId); await (await database.prepare('DELETE FROM webauthn_challenges WHERE user_id=?')).run(userId); await (await database.prepare('DELETE FROM passkeys WHERE user_id=?')).run(userId); await (await database.prepare('DELETE FROM user_emails WHERE user_id=?')).run(userId); await (await database.prepare('DELETE FROM oauth_states WHERE user_id=?')).run(userId); await (await database.prepare('DELETE FROM discord_profiles WHERE user_id=?')).run(userId); await (await database.prepare('DELETE FROM user_roles WHERE user_id=?')).run(userId); await (await database.prepare('DELETE FROM user_permissions WHERE user_id=?')).run(userId); await deleteUploadedAvatar(database, userId); if (purgeSecurityData) { await (await database.prepare('DELETE FROM login_events WHERE user_id=?')).run(userId); } await (await database.prepare( 'INSERT INTO audit_log(id,actor_id,entity_type,entity_id,action,before_json,after_json,created_at) VALUES(?,?,?,?,?,?,?,?)' )).run( randomUUID(), deletedBy, 'user_access', userId, 'account.soft_deleted', purgeSecurityData ? JSON.stringify({ purgedAtOwnerRequest: true }) : JSON.stringify({ username: user.username, displayName: user.display_name }), JSON.stringify({ username: deletedUsername, displayName: 'Deleted Agent', reason, securityDataPurged: purgeSecurityData }), now ); return true; }