Skip to content

Commit

Permalink
Add delete user option to user profile
Browse files Browse the repository at this point in the history
  • Loading branch information
Draikth committed Jul 18, 2024
1 parent f29043c commit aeb02cb
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 4 deletions.
40 changes: 40 additions & 0 deletions app/api/user/delete/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { NextRequest, NextResponse } from 'next/server';
import { deleteUser, UserWithPasswordHash } from '../../../../database/users';

type DeleteUserResponseBody =
| { message: string }
| { message: string; user?: UserWithPasswordHash }
| { message: string; error?: string };

export async function DELETE(
request: NextRequest,
): Promise<NextResponse<DeleteUserResponseBody>> {
const { sessionToken } = await request.json();

if (!sessionToken) {
return NextResponse.json(
{ message: 'Invalid request', error: 'Session token is missing' },
{ status: 400 },
);
}

try {
const user = await deleteUser(sessionToken);
if (user) {
return NextResponse.json(
{ message: 'User deleted successfully', user },
{ status: 200 },
);
} else {
return NextResponse.json(
{ message: 'User not found or session expired' },
{ status: 404 },
);
}
} catch (error) {
return NextResponse.json(
{ message: 'Internal server error', error: (error as Error).message },
{ status: 500 },
);
}
}
33 changes: 33 additions & 0 deletions app/profile/[username]/DeleteProfileForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use client';

import React from 'react';

type DeleteProfileFormProps = {
sessionToken: string;
};

export default function DeleteProfileForm({ sessionToken }: DeleteProfileFormProps) {
const handleDelete = async (event: React.FormEvent) => {
event.preventDefault();

const response = await fetch('/api/user/delete', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ sessionToken }),
});

if (response.ok) {
window.location.href = '/login'; // Redirect to login page after deletion
} else {
console.error('Failed to delete user');
}
};

return (
<form onSubmit={handleDelete}>
<button>Delete Profile</button>
</form>
);
}
10 changes: 8 additions & 2 deletions app/profile/[username]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';
import { getUser } from '../../../database/users';
import DeleteProfileForm from './DeleteProfileForm'; // Import the client component

type Props = {
params: {
Expand All @@ -20,6 +21,11 @@ export default async function UserProfilePage(props: Props) {
redirect('/login');
}

// 4. If user exists, render the page
return <h1>{props.params.username}'s Profile</h1>;
// 4. If user exists, render the page and include the DeleteProfileForm component
return (
<>
<h1>{props.params.username}'s Profile</h1>
<DeleteProfileForm sessionToken={sessionCookie.value} />
</>
);
}
14 changes: 14 additions & 0 deletions database/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ export const getUser = cache(async (sessionToken: string) => {
return user;
});

export const deleteUser = cache(async (sessionToken: string) => {
const [user] = await sql<UserWithPasswordHash[]>`
DELETE FROM users USING sessions
WHERE
sessions.token = ${sessionToken}
AND sessions.expiry_timestamp > now()
AND users.id = sessions.user_id
RETURNING
users.*
`;

return user;
});

export const getUserInsecure = cache(async (username: string) => {
const [user] = await sql<User[]>`
SELECT
Expand Down
5 changes: 3 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
{
"name": "next"
}
]
],
"jsx": "preserve"
},
"include": [
"**/*.ts",
Expand All @@ -17,4 +18,4 @@
"**/*.mjs",
".next/types/**/*.ts"
]
}
}

0 comments on commit aeb02cb

Please sign in to comment.