From 65938e314754b411369d9a1f908ff6d80e093d48 Mon Sep 17 00:00:00 2001 From: alex Date: Sun, 18 Feb 2024 13:57:50 +0100 Subject: [PATCH] delete user --- src/controllers/paymentController.ts | 3 +- src/controllers/userController.ts | 194 +++++++++++++++++++++++++-- src/utils/constants.ts | 2 + src/utils/utils.ts | 3 + 4 files changed, 187 insertions(+), 15 deletions(-) diff --git a/src/controllers/paymentController.ts b/src/controllers/paymentController.ts index ff806b6..9bd5c5a 100644 --- a/src/controllers/paymentController.ts +++ b/src/controllers/paymentController.ts @@ -8,7 +8,7 @@ import { } from "../utils/constants"; import UserPendingPayment from "../models/userPendingPayment"; import User from "../models/user"; -import { getUserSession } from "../utils/utils"; +import { getUserSession, stripe } from "../utils/utils"; import Store from "../models/store"; import Session from "../models/session"; @@ -17,7 +17,6 @@ const ZeitAdlerBasicYearly = "zeitadler-basic-yearly"; const ZeitAdlerPremiumMonthly = "zeitadler-premium-monthly"; const ZeitAdlerPremiumYearly = "zeitadler-premium-yearly"; -const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string); let cachedPrices = [] as any[]; export async function loadPrices() { diff --git a/src/controllers/userController.ts b/src/controllers/userController.ts index f793921..e4cea56 100644 --- a/src/controllers/userController.ts +++ b/src/controllers/userController.ts @@ -4,6 +4,7 @@ import User from "../models/user"; import { isCompanyNameValid, isEmailValid, + isFeedbackValid, isLanguageCodeValid, isPasswordValid, isPaymentIntervalValid, @@ -33,6 +34,7 @@ import { newStoreId, newUserId, saveSession, + stripe, } from "../utils/utils"; import Store from "../models/store"; import Session from "../models/session"; @@ -44,6 +46,13 @@ import EmailVerification from "../models/emailVerification"; import UserPendingEmailChange from "../models/userPendingEmailChange"; import { CreateCheckoutSession, getPriceId } from "./paymentController"; import UserPendingPayment from "../models/userPendingPayment"; +import StoreServiceActivity from "../models/storeServiceActivity"; +import StoreService from "../models/storeService"; +import StoreServiceActivityUsers from "../models/storeServiceActivityUsers"; +import { + isTerminPlanerGoogleCalendarConnected, + terminPlanerRequest, +} from "../utils/terminPlaner"; export async function SignUp(req: Request, res: Response) { try { @@ -799,7 +808,8 @@ export async function DeleteUserProfile(req: Request, res: Response) { try { const { reason, feedback, password } = req.body; - if (!reason || !feedback || !password) { + if (!reason || !feedback || !password || !isFeedbackValid(feedback)) { + console.log("invalid request"); return res.status(400).send({ err: "invalid request" }); } @@ -813,7 +823,7 @@ export async function DeleteUserProfile(req: Request, res: Response) { where: { user_id: session.user_id, }, - attributes: ["password"], + attributes: ["password", "stripe_customer_id"], }); if (!user) { @@ -828,18 +838,176 @@ export async function DeleteUserProfile(req: Request, res: Response) { return res.status(400).send({ err: "invalid request" }); } - // set user state to pending deletion + // delete stripe subscription - await User.update( - { - state: ACCOUNT_STATE.PENDING_DELETION, - }, - { - where: { - user_id: session.user_id, - }, + if (user.stripe_customer_id) { + const subscription = await stripe.subscriptions.list({ + customer: user.stripe_customer_id, + }); + + if (subscription.data.length > 0) { + const subscriptionId = subscription.data[0].id; + userLogger.info( + session.user_id, + "Canceling stripe subscription id:", + subscriptionId + ); + + await stripe.subscriptions.cancel(subscriptionId); } - ); + + await stripe.customers.del(user.stripe_customer_id); + + userLogger.info( + session.user_id, + "Deleted stripe customer id:", + user.stripe_customer_id + ); + } + + // delete store if user is a store owner + + const store = await Store.findOne({ + where: { + owner_user_id: session.user_id, + }, + attributes: ["store_id"], + }); + + // user is a store owner + if (store) { + await Store.destroy({ + where: { + store_id: store.store_id, + }, + }); + + userLogger.info(session.user_id, "Deleted store id:", store.store_id); + + // delete all employees of this store + + const employees = await User.findAll({ + where: { + store_id: store.store_id, + }, + attributes: ["user_id"], + }); + + if (employees) { + for (const employee of employees) { + const googleCalendarConnected = + await isTerminPlanerGoogleCalendarConnected(employee.user_id); + + if (googleCalendarConnected) { + await terminPlanerRequest("/api/v1/removeGoogleAccount", "POST", { + userId: employee.user_id, + }); + } + + await Session.destroy({ + where: { + user_id: employee.user_id, + }, + }); + + userLogger.info( + session.user_id, + "Deleted store employee id:", + employee.user_id + ); + } + + await User.destroy({ + where: { + user_id: employees.map((employee) => employee.user_id), + }, + }); + + userLogger.info( + session.user_id, + "Deleted store employees, count:", + employees.length.toString() + ); + } + + // delete all store services, store service activities, and store service activity users + + const storeServices = await StoreService.findAll({ + where: { + store_id: store.store_id, + }, + attributes: ["service_id"], + }); + + if (storeServices) { + const storeServiceActivities = await StoreServiceActivity.findAll({ + where: { + service_id: storeServices.map((service) => service.service_id), + }, + }); + + if (storeServiceActivities) { + const storeServiceActivityUsers = + await StoreServiceActivityUsers.findAll({ + where: { + activity_id: storeServiceActivities.map( + (activity) => activity.activity_id + ), + }, + }); + + if (storeServiceActivityUsers) { + await StoreServiceActivityUsers.destroy({ + where: { + activity_id: storeServiceActivityUsers.map( + (activity) => activity.activity_id + ), + }, + }); + + userLogger.info( + session.user_id, + "Deleted store service activity users, count:", + storeServiceActivityUsers.length.toString() + ); + } + + await StoreServiceActivity.destroy({ + where: { + activity_id: storeServiceActivities.map( + (activity) => activity.activity_id + ), + }, + }); + + userLogger.info( + session.user_id, + "Deleted store service activities, count:", + storeServiceActivities.length.toString() + ); + + await StoreService.destroy({ + where: { + service_id: storeServices.map((service) => service.service_id), + }, + }); + + userLogger.info( + session.user_id, + "Deleted store services, count:", + storeServices.length.toString() + ); + } + } + } + + // destroy user + + await User.destroy({ + where: { + user_id: session.user_id, + }, + }); // delete all sessions of this user by deleting all sessions with this user_id @@ -857,7 +1025,7 @@ export async function DeleteUserProfile(req: Request, res: Response) { feedback: `${reason} - ${feedback}`, }); - userLogger.info(session.user_id, "User deleted profile"); + userLogger.info(session.user_id, "Deleted user account"); res.status(200).send({ msg: "user deleted" }); } catch (error) { diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 6cfb536..6018203 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -1,3 +1,5 @@ +import Stripe from "stripe"; + export const DEFAULT_SESSION_EXPIRY = 365 * 24 * 60 * 60 * 1000; // 365 days export const SESSION_EXPIRY_NOT_REMEMBER_ME = 60 * 60 * 1000; // 1 hour export const USER_SESSION_LENGTH = 32; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 78f2577..828ff59 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -10,6 +10,7 @@ import { SESSION_EXPIRY_NOT_REMEMBER_ME, USER_SESSION_LENGTH, } from "./constants"; +import Stripe from "stripe"; export async function matchPassword(decodedPassword: string, password: string) { return await bcrypt.compare(decodedPassword, password); @@ -115,3 +116,5 @@ export function getEmailVerificationUrl( ) { return `${DASHBOARD_URL}/verify/${String(state)}/${emailVerificationId}`; } + +export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string);