2023-10-23 14:24:22 -05:00
|
|
|
import { prisma } from "@/lib/api/db";
|
|
|
|
import bcrypt from "bcrypt";
|
|
|
|
import removeFolder from "@/lib/api/storage/removeFolder";
|
|
|
|
import Stripe from "stripe";
|
2023-10-24 14:57:37 -05:00
|
|
|
import { DeleteUserBody } from "@/types/global";
|
|
|
|
import removeFile from "@/lib/api/storage/removeFile";
|
2023-10-23 14:24:22 -05:00
|
|
|
|
|
|
|
export default async function deleteUserById(
|
|
|
|
userId: number,
|
2024-05-02 08:17:56 -05:00
|
|
|
body: DeleteUserBody,
|
2024-05-03 09:22:45 -05:00
|
|
|
isServerAdmin?: boolean
|
2023-10-23 14:24:22 -05:00
|
|
|
) {
|
|
|
|
// First, we retrieve the user from the database
|
|
|
|
const user = await prisma.user.findUnique({
|
|
|
|
where: { id: userId },
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!user) {
|
|
|
|
return {
|
2023-10-24 14:57:37 -05:00
|
|
|
response: "Invalid credentials.",
|
2023-10-23 14:24:22 -05:00
|
|
|
status: 404,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-05-12 21:28:34 -05:00
|
|
|
if (!isServerAdmin) {
|
|
|
|
if (user.password) {
|
|
|
|
const isPasswordValid = bcrypt.compareSync(
|
|
|
|
body.password,
|
2024-07-27 17:40:07 -05:00
|
|
|
user.password
|
2024-05-12 21:28:34 -05:00
|
|
|
);
|
2023-11-19 07:56:03 -06:00
|
|
|
|
2024-05-12 21:28:34 -05:00
|
|
|
if (!isPasswordValid && !isServerAdmin) {
|
|
|
|
return {
|
|
|
|
response: "Invalid credentials.",
|
|
|
|
status: 401, // Unauthorized
|
|
|
|
};
|
|
|
|
}
|
|
|
|
} else {
|
2023-11-19 07:56:03 -06:00
|
|
|
return {
|
2024-05-21 07:00:44 -05:00
|
|
|
response:
|
|
|
|
"User has no password. Please reset your password from the forgot password page.",
|
2023-11-19 07:56:03 -06:00
|
|
|
status: 401, // Unauthorized
|
|
|
|
};
|
|
|
|
}
|
2023-10-23 14:24:22 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Delete the user and all related data within a transaction
|
2023-11-07 07:03:35 -06:00
|
|
|
await prisma
|
|
|
|
.$transaction(
|
|
|
|
async (prisma) => {
|
2024-05-02 08:17:56 -05:00
|
|
|
// Delete Access Tokens
|
|
|
|
await prisma.accessToken.deleteMany({
|
|
|
|
where: { userId },
|
|
|
|
});
|
|
|
|
|
2023-11-07 07:03:35 -06:00
|
|
|
// Delete whitelisted users
|
|
|
|
await prisma.whitelistedUser.deleteMany({
|
|
|
|
where: { userId },
|
|
|
|
});
|
|
|
|
|
|
|
|
// Delete links
|
|
|
|
await prisma.link.deleteMany({
|
|
|
|
where: { collection: { ownerId: userId } },
|
|
|
|
});
|
|
|
|
|
|
|
|
// Delete tags
|
|
|
|
await prisma.tag.deleteMany({
|
|
|
|
where: { ownerId: userId },
|
|
|
|
});
|
|
|
|
|
|
|
|
// Find collections that the user owns
|
|
|
|
const collections = await prisma.collection.findMany({
|
|
|
|
where: { ownerId: userId },
|
|
|
|
});
|
|
|
|
|
|
|
|
for (const collection of collections) {
|
|
|
|
// Delete related users and collections relations
|
|
|
|
await prisma.usersAndCollections.deleteMany({
|
|
|
|
where: { collectionId: collection.id },
|
|
|
|
});
|
|
|
|
|
|
|
|
// Delete archive folders
|
2024-06-28 08:39:31 -05:00
|
|
|
await removeFolder({ filePath: `archives/${collection.id}` });
|
2024-04-08 18:35:06 -05:00
|
|
|
|
|
|
|
await removeFolder({
|
|
|
|
filePath: `archives/preview/${collection.id}`,
|
|
|
|
});
|
2023-11-07 07:03:35 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Delete collections after cleaning up related data
|
|
|
|
await prisma.collection.deleteMany({
|
|
|
|
where: { ownerId: userId },
|
|
|
|
});
|
|
|
|
|
|
|
|
// Delete subscription
|
|
|
|
if (process.env.STRIPE_SECRET_KEY)
|
2024-05-24 16:12:47 -05:00
|
|
|
await prisma.subscription
|
|
|
|
.delete({
|
|
|
|
where: { userId },
|
|
|
|
})
|
|
|
|
.catch((err) => console.log(err));
|
2023-11-07 07:03:35 -06:00
|
|
|
|
2023-12-20 01:49:07 -06:00
|
|
|
await prisma.usersAndCollections.deleteMany({
|
|
|
|
where: {
|
|
|
|
OR: [{ userId: userId }, { collection: { ownerId: userId } }],
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2023-11-07 07:03:35 -06:00
|
|
|
// Delete user's avatar
|
|
|
|
await removeFile({ filePath: `uploads/avatar/${userId}.jpg` });
|
|
|
|
|
|
|
|
// Finally, delete the user
|
|
|
|
await prisma.user.delete({
|
|
|
|
where: { id: userId },
|
|
|
|
});
|
|
|
|
},
|
|
|
|
{ timeout: 20000 }
|
|
|
|
)
|
|
|
|
.catch((err) => console.log(err));
|
2023-10-23 14:24:22 -05:00
|
|
|
|
|
|
|
if (process.env.STRIPE_SECRET_KEY) {
|
|
|
|
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
|
|
|
apiVersion: "2022-11-15",
|
|
|
|
});
|
|
|
|
|
2023-10-24 14:57:37 -05:00
|
|
|
try {
|
|
|
|
const listByEmail = await stripe.customers.list({
|
|
|
|
email: user.email?.toLowerCase(),
|
|
|
|
expand: ["data.subscriptions"],
|
|
|
|
});
|
2023-10-23 14:24:22 -05:00
|
|
|
|
2023-10-24 14:57:37 -05:00
|
|
|
if (listByEmail.data[0].subscriptions?.data[0].id) {
|
|
|
|
const deleted = await stripe.subscriptions.cancel(
|
|
|
|
listByEmail.data[0].subscriptions?.data[0].id,
|
|
|
|
{
|
|
|
|
cancellation_details: {
|
|
|
|
comment: body.cancellation_details?.comment,
|
|
|
|
feedback: body.cancellation_details?.feedback,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
return {
|
|
|
|
response: deleted,
|
|
|
|
status: 200,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
console.log(err);
|
2023-10-23 14:24:22 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
response: "User account and all related data deleted successfully.",
|
|
|
|
status: 200,
|
|
|
|
};
|
|
|
|
}
|