From cab328596cec8226ab28e4ae634b9d83498410bf Mon Sep 17 00:00:00 2001 From: Labham Jain Date: Thu, 5 Oct 2023 21:40:17 +0530 Subject: [PATCH 1/2] fix: add delete customer route --- datasource.js | 23 ++++++++ dump.rdb | Bin 80059 -> 87206 bytes package.json | 6 +- src/api/index.ts | 10 +++- src/api/middlewares/user-deactivation.ts | 15 +++++ src/api/routes/admin/delete-customer.ts | 18 ++++++ .../1696512365971-DeletedCustomer.ts | 26 +++++++++ ...1696513431383-UpdateFieldDeleteCustomer.ts | 15 +++++ ...1696513616474-UpdateFieldDeleteCustomer.ts | 13 +++++ ...1696520782799-UpdateFieldDeleteCustomer.ts | 14 +++++ .../1696521959509-AddUniqueConstraints.ts | 12 ++++ src/migrations/README.md | 29 ---------- src/models/deleted-customer.ts | 7 +++ src/repositories/deleted-customer.ts | 7 +++ src/services/deleted-customer.ts | 53 ++++++++++++++++++ 15 files changed, 214 insertions(+), 34 deletions(-) create mode 100644 datasource.js create mode 100644 src/api/middlewares/user-deactivation.ts create mode 100644 src/api/routes/admin/delete-customer.ts create mode 100644 src/migrations/1696512365971-DeletedCustomer.ts create mode 100644 src/migrations/1696513431383-UpdateFieldDeleteCustomer.ts create mode 100644 src/migrations/1696513616474-UpdateFieldDeleteCustomer.ts create mode 100644 src/migrations/1696520782799-UpdateFieldDeleteCustomer.ts create mode 100644 src/migrations/1696521959509-AddUniqueConstraints.ts delete mode 100644 src/migrations/README.md create mode 100644 src/models/deleted-customer.ts create mode 100644 src/repositories/deleted-customer.ts create mode 100644 src/services/deleted-customer.ts diff --git a/datasource.js b/datasource.js new file mode 100644 index 0000000..8fdf427 --- /dev/null +++ b/datasource.js @@ -0,0 +1,23 @@ +const { DataSource } = require("typeorm"); +const dotenv = require("dotenv"); + +dotenv.config() + +const AppDataSource = new DataSource({ + type: "postgres", + port: 5432, + port: process.env.DB_PORT, + username: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_NAME, + host: process.env.DB_HOST, + entities: [ + ], + migrations: [ + "dist/migrations/*.js", + ], +}) + +module.exports = { + datasource: AppDataSource, +} \ No newline at end of file diff --git a/dump.rdb b/dump.rdb index 56e54e837cb22c0d9c1e98fa766c3616c9c5131d..6c45609d821d3a2d7bf85b43d7ba6ce180232294 100644 GIT binary patch delta 17282 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 OJ)8SK{8#tt8~!g2H5{t| delta 5806 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 diff --git a/package.json b/package.json index d689ad1..cc9cd0f 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,9 @@ "seed": "medusa seed -f ./data/seed.json", "start": "npm run build && medusa migrations run && medusa develop -p=8080", "dev": "npm run build && medusa develop", - "build:admin": "medusa-admin build" + "build:admin": "medusa-admin build", + "migration:run": "npm run build && npx typeorm migration:run -d datasource.js", + "migration:generate": "npx typeorm migration:generate -d datasource.js" }, "dependencies": { "@babel/preset-typescript": "^7.21.5", @@ -92,4 +94,4 @@ "engines": { "node": ">=18.0.0" } -} +} \ No newline at end of file diff --git a/src/api/index.ts b/src/api/index.ts index 754711a..4d4587c 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -1,10 +1,12 @@ -import { Router } from "express" +import express, { Router } from "express" import { getConfigFile, } from "medusa-core-utils"; import { ConfigModule } from "@medusajs/medusa"; import cors from 'cors' import { restrictUser } from "./routes/admin/restrict-user"; +import validateUserNotDeactivated from "./middlewares/user-deactivation"; +import { deleteCustomer } from "./routes/admin/delete-customer"; export default (rootDirectory: string): Router | Router[] => { const { configModule: { projectConfig } } = getConfigFile(rootDirectory, "medusa-config") @@ -16,9 +18,11 @@ export default (rootDirectory: string): Router | Router[] => { const router = Router(); router.use(cors(storefrontCorsConfig)) - const endpointHandlers = [restrictUser] - + // middlewares + router.use('/store/auth', express.json(), validateUserNotDeactivated); + // endpoints + const endpointHandlers = [restrictUser, deleteCustomer] endpointHandlers.forEach(endpointHandle => endpointHandle(router)) return router diff --git a/src/api/middlewares/user-deactivation.ts b/src/api/middlewares/user-deactivation.ts new file mode 100644 index 0000000..5db9614 --- /dev/null +++ b/src/api/middlewares/user-deactivation.ts @@ -0,0 +1,15 @@ +import { CustomerService } from "@medusajs/medusa"; +import { NextFunction, Request, Response } from "express"; + +const validateUserNotDeactivated = async (req: Request, res: Response, next: NextFunction) => { + const customerService = req.scope.resolve('customerService') as CustomerService; + + if (req.method.toLowerCase() === 'post' && req.body.email) { + const customer = await customerService.retrieveRegisteredByEmail(req.body.email); + console.log(customer); + } + + next(); +} + +export default validateUserNotDeactivated; \ No newline at end of file diff --git a/src/api/routes/admin/delete-customer.ts b/src/api/routes/admin/delete-customer.ts new file mode 100644 index 0000000..c8c05fb --- /dev/null +++ b/src/api/routes/admin/delete-customer.ts @@ -0,0 +1,18 @@ +import { authenticate } from "@medusajs/medusa" +import express, { Router } from "express"; +import DeletedCustomerService from "../../../services/deleted-customer"; + +export const deleteCustomer = (router: Router) => { + router.use('/admin/delete-customer', express.json()); + + router.post('/admin/delete-customer', authenticate(), async (req, res) => { + const deletedCustomerService = req.scope.resolve('deletedCustomerService') as DeletedCustomerService; + try { + await deletedCustomerService.deleteCustomer(req.body.email); + return res.json({ status: 200, message: 'User has been deleted!' }) + } catch (error) { + return res.status(500).json({ status: 500, message: 'An error occurred!', error: error instanceof Error ? error.message : error }) + } + + }) +} \ No newline at end of file diff --git a/src/migrations/1696512365971-DeletedCustomer.ts b/src/migrations/1696512365971-DeletedCustomer.ts new file mode 100644 index 0000000..8164833 --- /dev/null +++ b/src/migrations/1696512365971-DeletedCustomer.ts @@ -0,0 +1,26 @@ +import { MigrationInterface, QueryRunner } from "typeorm" + +export class DeletedCustomer1696512365971 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE IF NOT EXISTS "deleted_customer" ( + "id" character varying NOT NULL, + "email" character varying NOT NULL, + "first_name" character varying NOT NULL, + "last_name" character varying NOT NULL, + "billing_address_id" character varying NOT NULL, + "password_hash" character varying NOT NULL, + "phone" character varying NOT NULL, + "has_account" boolean NOT NULL, + "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), + "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), + "metadata" jsonb, + PRIMARY KEY ("id"), + CONSTRAINT "FK_deleted_customer_billing_address_id" FOREIGN KEY ("billing_address_id") REFERENCES "address"("id"))`) + } + + public async down(queryRunner: QueryRunner): Promise { + } + +} diff --git a/src/migrations/1696513431383-UpdateFieldDeleteCustomer.ts b/src/migrations/1696513431383-UpdateFieldDeleteCustomer.ts new file mode 100644 index 0000000..1a5e181 --- /dev/null +++ b/src/migrations/1696513431383-UpdateFieldDeleteCustomer.ts @@ -0,0 +1,15 @@ +import { MigrationInterface, QueryRunner } from "typeorm" + +export class UpdateFieldDeleteCustomer1696513431383 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "deleted_customer" ADD COLUMN "deleted_at" TIMESTAMP WITH TIME ZONE` + ); + } + + + public async down(queryRunner: QueryRunner): Promise { + } + +} diff --git a/src/migrations/1696513616474-UpdateFieldDeleteCustomer.ts b/src/migrations/1696513616474-UpdateFieldDeleteCustomer.ts new file mode 100644 index 0000000..acaef38 --- /dev/null +++ b/src/migrations/1696513616474-UpdateFieldDeleteCustomer.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from "typeorm" + +export class UpdateFieldDeleteCustomer1696513616474 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "deleted_customer" ALTER COLUMN "billing_address_id" DROP NOT NULL` + ); + } + public async down(queryRunner: QueryRunner): Promise { + } + +} diff --git a/src/migrations/1696520782799-UpdateFieldDeleteCustomer.ts b/src/migrations/1696520782799-UpdateFieldDeleteCustomer.ts new file mode 100644 index 0000000..7805c11 --- /dev/null +++ b/src/migrations/1696520782799-UpdateFieldDeleteCustomer.ts @@ -0,0 +1,14 @@ +import { MigrationInterface, QueryRunner } from "typeorm" + +export class UpdateFieldDeleteCustomer1696520782799 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "deleted_customer" ALTER COLUMN "phone" DROP NOT NULL` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + } + +} diff --git a/src/migrations/1696521959509-AddUniqueConstraints.ts b/src/migrations/1696521959509-AddUniqueConstraints.ts new file mode 100644 index 0000000..a809f2d --- /dev/null +++ b/src/migrations/1696521959509-AddUniqueConstraints.ts @@ -0,0 +1,12 @@ +import { MigrationInterface, QueryRunner } from "typeorm" + +export class AddUniqueConstraints1696521959509 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE deleted_customer ADD CONSTRAINT unique_email_id_constraint UNIQUE (email, id);`) + } + + public async down(queryRunner: QueryRunner): Promise { + } + +} diff --git a/src/migrations/README.md b/src/migrations/README.md deleted file mode 100644 index bf42227..0000000 --- a/src/migrations/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Custom migrations - -You may define custom models (entities) that will be registered on the global container by creating files in the `src/models` directory that export an instance of `BaseEntity`. -In that case you also need to provide a migration in order to create the table in the database. - -## Example - -### 1. Create the migration - -See [How to Create Migrations](https://docs.medusajs.com/advanced/backend/migrations/) in the documentation. - -```ts -// src/migration/my-migration.ts - -import { MigrationInterface, QueryRunner } from "typeorm" - -export class MyMigration1617703530229 implements MigrationInterface { - name = "myMigration1617703530229" - - public async up(queryRunner: QueryRunner): Promise { - // write you migration here - } - - public async down(queryRunner: QueryRunner): Promise { - // write you migration here - } -} - -``` diff --git a/src/models/deleted-customer.ts b/src/models/deleted-customer.ts new file mode 100644 index 0000000..5ab3ce6 --- /dev/null +++ b/src/models/deleted-customer.ts @@ -0,0 +1,7 @@ +import { Customer } from "@medusajs/medusa"; +import { Entity } from "typeorm" + +@Entity({ name: 'deleted_customer' }) +export class DeletedCustomer extends Customer { + +} \ No newline at end of file diff --git a/src/repositories/deleted-customer.ts b/src/repositories/deleted-customer.ts new file mode 100644 index 0000000..2ef4747 --- /dev/null +++ b/src/repositories/deleted-customer.ts @@ -0,0 +1,7 @@ +import { + dataSource, +} from "@medusajs/medusa/dist/loaders/database" +import { DeletedCustomer } from "../models/deleted-customer" + +export const DeletedCustomerRepository = dataSource.getRepository(DeletedCustomer) +export default DeletedCustomerRepository \ No newline at end of file diff --git a/src/services/deleted-customer.ts b/src/services/deleted-customer.ts new file mode 100644 index 0000000..9280760 --- /dev/null +++ b/src/services/deleted-customer.ts @@ -0,0 +1,53 @@ +import { Customer, CustomerService, TransactionBaseService } from '@medusajs/medusa' +import { DeletedCustomer } from '../models/deleted-customer'; +import { Repository } from 'typeorm'; +import DeletedCustomerRepository from '../repositories/deleted-customer'; + +export default class DeletedCustomerService extends TransactionBaseService { + protected readonly deletedCustomerRepository: Repository; + protected readonly customerService: CustomerService; + + constructor(container) { + // @ts-ignore + super(container); + this.customerService = container.customerService; + this.deletedCustomerRepository = this.activeManager_.withRepository(DeletedCustomerRepository) + } + + async deleteCustomer(email: string) { + try { + // Manually moving customer field to deleted_customer since medusa does not provides password hash in API. + const insertQuery = ` + INSERT INTO deleted_customer (id, email, first_name, last_name, billing_address_id, password_hash, phone, has_account, created_at, updated_at, metadata) + SELECT id, email, first_name, last_name, billing_address_id, password_hash, phone, has_account, created_at, updated_at, metadata::jsonb + FROM customer + WHERE email = $1 + ON CONFLICT (email, id) DO NOTHING; + `; + await this.activeManager_.query(insertQuery, [email]); + + + // delete customer from customer table + + const customer = await this.customerService.retrieveRegisteredByEmail(email); + + await this.customerService.delete(customer.id) as Customer + + // update time of delete in deleted_customer + + const updateDeleteTimestampQuery = ` + UPDATE deleted_customer + SET deleted_at = now() + WHERE email = $1; + `; + const result = await this.activeManager_.query(updateDeleteTimestampQuery, [email]) + + return result; + } catch (error) { + // Handle any errors that may occur during the query. + console.error(error); + throw error; + } + } + +} \ No newline at end of file From fe9898b2f464ee6761d8a6625691d9b69ece66cf Mon Sep 17 00:00:00 2001 From: Labham Jain Date: Fri, 6 Oct 2023 13:50:38 +0530 Subject: [PATCH 2/2] fix: update deletion method --- dump.rdb | Bin 87206 -> 98445 bytes src/api/index.ts | 11 ++-- src/api/middlewares/user-deactivation.ts | 15 ----- src/api/routes/admin/delete-customer.ts | 13 ++--- src/api/routes/admin/list-deleted.ts | 17 ++++++ src/api/routes/admin/restore-customer.ts | 21 +++++++ src/api/routes/admin/restrict-user.ts | 18 ------ .../1696512365971-DeletedCustomer.ts | 26 --------- ...1696513431383-UpdateFieldDeleteCustomer.ts | 15 ----- ...1696513616474-UpdateFieldDeleteCustomer.ts | 13 ----- ...1696520782799-UpdateFieldDeleteCustomer.ts | 14 ----- .../1696521959509-AddUniqueConstraints.ts | 12 ---- src/models/deleted-customer.ts | 7 --- src/services/customer.ts | 39 +++++++++++++ src/services/deleted-customer.ts | 53 ------------------ 15 files changed, 87 insertions(+), 187 deletions(-) delete mode 100644 src/api/middlewares/user-deactivation.ts create mode 100644 src/api/routes/admin/list-deleted.ts create mode 100644 src/api/routes/admin/restore-customer.ts delete mode 100644 src/api/routes/admin/restrict-user.ts delete mode 100644 src/migrations/1696512365971-DeletedCustomer.ts delete mode 100644 src/migrations/1696513431383-UpdateFieldDeleteCustomer.ts delete mode 100644 src/migrations/1696513616474-UpdateFieldDeleteCustomer.ts delete mode 100644 src/migrations/1696520782799-UpdateFieldDeleteCustomer.ts delete mode 100644 src/migrations/1696521959509-AddUniqueConstraints.ts delete mode 100644 src/models/deleted-customer.ts create mode 100644 src/services/customer.ts delete mode 100644 src/services/deleted-customer.ts diff --git a/dump.rdb b/dump.rdb index 6c45609d821d3a2d7bf85b43d7ba6ce180232294..01e85c22811f51fedb3e23fd018033961e2c2ae2 100644 GIT binary patch delta 10273 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 delta 799 zcmZ{hPfXKb7>B>F5rk|&q6a~PEJF<;ZnRyywT0;Z!p5M3ZES289Nh=oShuov=oYs? zJQ5R4gS~n(F`f(ujHd|~Of*qrJgNr|o;(o_xQH95frH6=df()i-}Aip*IJ)EZFxWp zKK^_e9kdl?lr)J*+#cZ0fdhRazhIIIC?U>gQ4Rb8KXt7g*VMO|QQNJ;fuRqd+;ANN z0IteNmbpxMF*Fl53CseTSz@Qiut%{5lBHs10tHF|zf*k(`_(jdv6BuHEJs*50ypbu zo7T4qZ5CLm)JLRKc_pThiPL3QuH=hlJ*C8Gv@{=DL31{d7j4ny=-<4q<2;(9D6`H& z(3)WtTA60xO7#YIqM*soQ{hx1uq?R)$&f#r$;SfT2{BS;{K5H%l-jXBvp-k;7}ID- zS~!Z~Xu_;x>1M-8)d#yVaHUD#mQKFXz!|&!NX1|U_GynQ(4YwwXweK+*b~$z6a|Tr zB2OlgsD^dQN+O#@Nmss@Q&6FXwJT{6HI|~(L0DNUjpL1BY?MwaQ2%!s~Rp2~W@+ zXP8+h$y;z9chOb{YZz$}in5XyYi2`--Q4ZMS<1^eW`kCKd^$Adi12*S7LOS#di4#~ Uuf4ttN7@h9S6264vJ1n%0sZ{?mH+?% diff --git a/src/api/index.ts b/src/api/index.ts index 4d4587c..0a6c56a 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -1,12 +1,12 @@ -import express, { Router } from "express" +import { Router } from "express" import { getConfigFile, } from "medusa-core-utils"; import { ConfigModule } from "@medusajs/medusa"; import cors from 'cors' -import { restrictUser } from "./routes/admin/restrict-user"; -import validateUserNotDeactivated from "./middlewares/user-deactivation"; import { deleteCustomer } from "./routes/admin/delete-customer"; +import { listDeletedCustomers } from "./routes/admin/list-deleted"; +import { restoreCustomer } from "./routes/admin/restore-customer"; export default (rootDirectory: string): Router | Router[] => { const { configModule: { projectConfig } } = getConfigFile(rootDirectory, "medusa-config") @@ -18,11 +18,8 @@ export default (rootDirectory: string): Router | Router[] => { const router = Router(); router.use(cors(storefrontCorsConfig)) - // middlewares - router.use('/store/auth', express.json(), validateUserNotDeactivated); - // endpoints - const endpointHandlers = [restrictUser, deleteCustomer] + const endpointHandlers = [deleteCustomer, listDeletedCustomers, restoreCustomer] endpointHandlers.forEach(endpointHandle => endpointHandle(router)) return router diff --git a/src/api/middlewares/user-deactivation.ts b/src/api/middlewares/user-deactivation.ts deleted file mode 100644 index 5db9614..0000000 --- a/src/api/middlewares/user-deactivation.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { CustomerService } from "@medusajs/medusa"; -import { NextFunction, Request, Response } from "express"; - -const validateUserNotDeactivated = async (req: Request, res: Response, next: NextFunction) => { - const customerService = req.scope.resolve('customerService') as CustomerService; - - if (req.method.toLowerCase() === 'post' && req.body.email) { - const customer = await customerService.retrieveRegisteredByEmail(req.body.email); - console.log(customer); - } - - next(); -} - -export default validateUserNotDeactivated; \ No newline at end of file diff --git a/src/api/routes/admin/delete-customer.ts b/src/api/routes/admin/delete-customer.ts index c8c05fb..70805c9 100644 --- a/src/api/routes/admin/delete-customer.ts +++ b/src/api/routes/admin/delete-customer.ts @@ -1,18 +1,17 @@ -import { authenticate } from "@medusajs/medusa" +import { CustomerService, authenticate } from "@medusajs/medusa" import express, { Router } from "express"; -import DeletedCustomerService from "../../../services/deleted-customer"; export const deleteCustomer = (router: Router) => { - router.use('/admin/delete-customer', express.json()); + router.use('/admin/customers/delete', express.json(), authenticate()); - router.post('/admin/delete-customer', authenticate(), async (req, res) => { - const deletedCustomerService = req.scope.resolve('deletedCustomerService') as DeletedCustomerService; + router.post('/admin/customers/delete', async (req, res) => { try { - await deletedCustomerService.deleteCustomer(req.body.email); + const customerService = req.scope.resolve('customerService') as CustomerService; + const customer = await customerService.retrieveRegisteredByEmail(req.body.email) + await customerService.delete(customer.id) return res.json({ status: 200, message: 'User has been deleted!' }) } catch (error) { return res.status(500).json({ status: 500, message: 'An error occurred!', error: error instanceof Error ? error.message : error }) } - }) } \ No newline at end of file diff --git a/src/api/routes/admin/list-deleted.ts b/src/api/routes/admin/list-deleted.ts new file mode 100644 index 0000000..0ad873e --- /dev/null +++ b/src/api/routes/admin/list-deleted.ts @@ -0,0 +1,17 @@ +import { authenticate } from "@medusajs/medusa" +import { Router } from "express"; +import CustomerService from "../../../services/customer"; + +export const listDeletedCustomers = (router: Router) => { + router.use('/admin/customers/list-deleted', authenticate()); + + router.get('/admin/customers/list-deleted', async (req, res) => { + const customerService = req.scope.resolve('customerService') as CustomerService + try { + const result = await customerService.retrieveDeletedCustomer() + return res.json(result) + } catch (error) { + return res.status(500).json({ status: 500, message: 'An error occurred!', error: error instanceof Error ? error.message : error }) + } + }) +} \ No newline at end of file diff --git a/src/api/routes/admin/restore-customer.ts b/src/api/routes/admin/restore-customer.ts new file mode 100644 index 0000000..bf7cb89 --- /dev/null +++ b/src/api/routes/admin/restore-customer.ts @@ -0,0 +1,21 @@ +import { authenticate } from "@medusajs/medusa" +import express, { Router } from "express"; +import CustomerService from "../../../services/customer"; + +export const restoreCustomer = (router: Router) => { + router.use('/admin/customers/restore', express.json(), authenticate()); + + router.post('/admin/customers/restore', async (req, res) => { + + if (!req.body.email) return res.status(422).json({ status: 422, message: 'Insufficient payload!' }) + + try { + const customerService = req.scope.resolve('customerService') as CustomerService + await customerService.restoreCustomer(req.body.email); + + return res.json({ status: 200, message: 'User has been restored!' }) + } catch (error) { + return res.status(500).json({ status: 500, message: 'An error occurred!', error: error instanceof Error ? error.message : error }) + } + }) +} \ No newline at end of file diff --git a/src/api/routes/admin/restrict-user.ts b/src/api/routes/admin/restrict-user.ts deleted file mode 100644 index 5c76636..0000000 --- a/src/api/routes/admin/restrict-user.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { CustomerService, authenticate } from "@medusajs/medusa" -import express, { Router } from "express"; - -export const restrictUser = (router: Router) => { - router.use('/admin/restrict-user', express.json()); - - router.post('/admin/restrict-user', authenticate(), async (req, res) => { - const customerService = req.scope.resolve('customerService') as CustomerService; - try { - const result = await customerService.retrieveRegisteredByEmail(req.body.email) - await customerService.delete(result.id); - return res.json({ status: 200, message: 'User has been deleted!' }) - } catch (error) { - return res.status(500).json({ status: 500, message: 'An error occurred!', error: error instanceof Error ? error.message : error }) - } - - }) -} \ No newline at end of file diff --git a/src/migrations/1696512365971-DeletedCustomer.ts b/src/migrations/1696512365971-DeletedCustomer.ts deleted file mode 100644 index 8164833..0000000 --- a/src/migrations/1696512365971-DeletedCustomer.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class DeletedCustomer1696512365971 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE IF NOT EXISTS "deleted_customer" ( - "id" character varying NOT NULL, - "email" character varying NOT NULL, - "first_name" character varying NOT NULL, - "last_name" character varying NOT NULL, - "billing_address_id" character varying NOT NULL, - "password_hash" character varying NOT NULL, - "phone" character varying NOT NULL, - "has_account" boolean NOT NULL, - "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), - "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), - "metadata" jsonb, - PRIMARY KEY ("id"), - CONSTRAINT "FK_deleted_customer_billing_address_id" FOREIGN KEY ("billing_address_id") REFERENCES "address"("id"))`) - } - - public async down(queryRunner: QueryRunner): Promise { - } - -} diff --git a/src/migrations/1696513431383-UpdateFieldDeleteCustomer.ts b/src/migrations/1696513431383-UpdateFieldDeleteCustomer.ts deleted file mode 100644 index 1a5e181..0000000 --- a/src/migrations/1696513431383-UpdateFieldDeleteCustomer.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class UpdateFieldDeleteCustomer1696513431383 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "deleted_customer" ADD COLUMN "deleted_at" TIMESTAMP WITH TIME ZONE` - ); - } - - - public async down(queryRunner: QueryRunner): Promise { - } - -} diff --git a/src/migrations/1696513616474-UpdateFieldDeleteCustomer.ts b/src/migrations/1696513616474-UpdateFieldDeleteCustomer.ts deleted file mode 100644 index acaef38..0000000 --- a/src/migrations/1696513616474-UpdateFieldDeleteCustomer.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class UpdateFieldDeleteCustomer1696513616474 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "deleted_customer" ALTER COLUMN "billing_address_id" DROP NOT NULL` - ); - } - public async down(queryRunner: QueryRunner): Promise { - } - -} diff --git a/src/migrations/1696520782799-UpdateFieldDeleteCustomer.ts b/src/migrations/1696520782799-UpdateFieldDeleteCustomer.ts deleted file mode 100644 index 7805c11..0000000 --- a/src/migrations/1696520782799-UpdateFieldDeleteCustomer.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class UpdateFieldDeleteCustomer1696520782799 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "deleted_customer" ALTER COLUMN "phone" DROP NOT NULL` - ); - } - - public async down(queryRunner: QueryRunner): Promise { - } - -} diff --git a/src/migrations/1696521959509-AddUniqueConstraints.ts b/src/migrations/1696521959509-AddUniqueConstraints.ts deleted file mode 100644 index a809f2d..0000000 --- a/src/migrations/1696521959509-AddUniqueConstraints.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm" - -export class AddUniqueConstraints1696521959509 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE deleted_customer ADD CONSTRAINT unique_email_id_constraint UNIQUE (email, id);`) - } - - public async down(queryRunner: QueryRunner): Promise { - } - -} diff --git a/src/models/deleted-customer.ts b/src/models/deleted-customer.ts deleted file mode 100644 index 5ab3ce6..0000000 --- a/src/models/deleted-customer.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Customer } from "@medusajs/medusa"; -import { Entity } from "typeorm" - -@Entity({ name: 'deleted_customer' }) -export class DeletedCustomer extends Customer { - -} \ No newline at end of file diff --git a/src/services/customer.ts b/src/services/customer.ts new file mode 100644 index 0000000..a389c94 --- /dev/null +++ b/src/services/customer.ts @@ -0,0 +1,39 @@ +import { CustomerService as BaseCustomerService, Customer } from '@medusajs/medusa' + +export default class CustomerService extends BaseCustomerService { + constructor(container) { + super(container) + } + + + async retrieveDeletedCustomer() { + const deletedCustomerQuery = ` + SELECT id, email, first_name, last_name, billing_address_id, phone, has_account, metadata, created_at, updated_at, deleted_at FROM customer WHERE deleted_at IS NOT NULL + ` + + const listOfDeletedCustomers = await this.activeManager_.query(deletedCustomerQuery) + + return listOfDeletedCustomers + } + + async restoreCustomer(email: string) { + // check if email exists! + + const customerByEmailQuery = ` + SELECT * FROM customer WHERE email = $1 + ` + + const customerResult = await this.activeManager_.query(customerByEmailQuery, [email]) as Customer[] + + if (!customerResult.length) { + console.log(customerResult); + throw new Error('Customer not found with this email!') + } + + const restoreCustomerQuery = ` + UPDATE customer SET deleted_at = NULL WHERE email = $1 + ` + + await this.activeManager_.query(restoreCustomerQuery, [email]) + } +} \ No newline at end of file diff --git a/src/services/deleted-customer.ts b/src/services/deleted-customer.ts deleted file mode 100644 index 9280760..0000000 --- a/src/services/deleted-customer.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Customer, CustomerService, TransactionBaseService } from '@medusajs/medusa' -import { DeletedCustomer } from '../models/deleted-customer'; -import { Repository } from 'typeorm'; -import DeletedCustomerRepository from '../repositories/deleted-customer'; - -export default class DeletedCustomerService extends TransactionBaseService { - protected readonly deletedCustomerRepository: Repository; - protected readonly customerService: CustomerService; - - constructor(container) { - // @ts-ignore - super(container); - this.customerService = container.customerService; - this.deletedCustomerRepository = this.activeManager_.withRepository(DeletedCustomerRepository) - } - - async deleteCustomer(email: string) { - try { - // Manually moving customer field to deleted_customer since medusa does not provides password hash in API. - const insertQuery = ` - INSERT INTO deleted_customer (id, email, first_name, last_name, billing_address_id, password_hash, phone, has_account, created_at, updated_at, metadata) - SELECT id, email, first_name, last_name, billing_address_id, password_hash, phone, has_account, created_at, updated_at, metadata::jsonb - FROM customer - WHERE email = $1 - ON CONFLICT (email, id) DO NOTHING; - `; - await this.activeManager_.query(insertQuery, [email]); - - - // delete customer from customer table - - const customer = await this.customerService.retrieveRegisteredByEmail(email); - - await this.customerService.delete(customer.id) as Customer - - // update time of delete in deleted_customer - - const updateDeleteTimestampQuery = ` - UPDATE deleted_customer - SET deleted_at = now() - WHERE email = $1; - `; - const result = await this.activeManager_.query(updateDeleteTimestampQuery, [email]) - - return result; - } catch (error) { - // Handle any errors that may occur during the query. - console.error(error); - throw error; - } - } - -} \ No newline at end of file