21258edd95
Hop-State: A_06FP5KEDBTB4A9ZT7QB498G Hop-Proposal: R_06FP5KDWMCKVVMQZT4MVZ28 Hop-Task: T_06FP5DZ7T0G45FG93PT90B8 Hop-Attempt: AT_06FP5DZ7T0PKQW99V27JCV8
122 lines
4.0 KiB
TypeScript
122 lines
4.0 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import { db, users, posts, sessions, likes, follows, notifications, chatMessages, chatConversations } from '@/db';
|
|
import { eq, or, and } from 'drizzle-orm';
|
|
import { requireSignedAction, type SignedAction } from '@/lib/auth/verify-signature';
|
|
import { verifyPassword } from '@/lib/auth';
|
|
import { cookies } from 'next/headers';
|
|
|
|
export async function POST(request: Request) {
|
|
try {
|
|
const signedAction: SignedAction = await request.json();
|
|
|
|
// Verify signature and get user
|
|
const user = await requireSignedAction(signedAction);
|
|
|
|
if (signedAction.action !== 'delete_account') {
|
|
return NextResponse.json(
|
|
{ error: 'Invalid action type' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const { password } = signedAction.data;
|
|
|
|
// Verify password
|
|
if (!user.passwordHash) {
|
|
return NextResponse.json(
|
|
{ error: 'Account has no password set' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const isPasswordValid = await verifyPassword(password, user.passwordHash);
|
|
|
|
if (!isPasswordValid) {
|
|
return NextResponse.json(
|
|
{ error: 'Password is incorrect' },
|
|
{ status: 403 }
|
|
);
|
|
}
|
|
|
|
const userId = user.id;
|
|
const userDid = user.did;
|
|
|
|
// Delete all user data in proper order to respect foreign keys
|
|
|
|
// 1. Delete chat messages sent by this user
|
|
await db.delete(chatMessages)
|
|
.where(eq(chatMessages.senderDid, userDid));
|
|
|
|
// 2. Find and delete conversations where user is a participant
|
|
// First get conversation IDs where user is participant1 (local user)
|
|
// For participant2, we need to check by handle since it's stored as text (can be remote)
|
|
const conversations = await db.query.chatConversations.findMany({
|
|
where: { OR: [{ participant1Id: userId }, { participant2Handle: user.handle }] },
|
|
});
|
|
|
|
const conversationIds = conversations.map(c => c.id);
|
|
|
|
// Delete messages in those conversations
|
|
if (conversationIds.length > 0) {
|
|
for (const convId of conversationIds) {
|
|
await db.delete(chatMessages)
|
|
.where(eq(chatMessages.conversationId, convId));
|
|
}
|
|
}
|
|
|
|
// 3. Delete the conversations themselves
|
|
if (conversationIds.length > 0) {
|
|
for (const convId of conversationIds) {
|
|
await db.delete(chatConversations)
|
|
.where(eq(chatConversations.id, convId));
|
|
}
|
|
}
|
|
|
|
// 4. Delete notifications
|
|
await db.delete(notifications)
|
|
.where(or(
|
|
eq(notifications.userId, userId),
|
|
eq(notifications.actorId, userId)
|
|
));
|
|
|
|
// 5. Delete likes
|
|
await db.delete(likes)
|
|
.where(eq(likes.userId, userId));
|
|
|
|
// 6. Delete follows (both directions)
|
|
await db.delete(follows)
|
|
.where(or(
|
|
eq(follows.followerId, userId),
|
|
eq(follows.followingId, userId)
|
|
));
|
|
|
|
// 7. Delete posts (this will cascade delete reposts and post likes via triggers if set up)
|
|
await db.delete(posts)
|
|
.where(eq(posts.userId, userId));
|
|
|
|
// 8. Delete sessions
|
|
await db.delete(sessions)
|
|
.where(eq(sessions.userId, userId));
|
|
|
|
// 9. Finally, delete the user
|
|
await db.delete(users)
|
|
.where(eq(users.id, userId));
|
|
|
|
// Clear session cookie
|
|
const cookieStore = await cookies();
|
|
cookieStore.delete('synapsis_session');
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
message: 'Account deleted successfully',
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Account deletion error:', error);
|
|
return NextResponse.json(
|
|
{ error: error instanceof Error ? error.message : 'Failed to delete account' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|