From 44c88543142cdfb5ba84aa1546e52da329dafbea Mon Sep 17 00:00:00 2001 From: Vins Date: Mon, 22 Jul 2024 14:38:51 +0200 Subject: [PATCH 01/16] Circle progress spacing + rounded --- logfile | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 logfile diff --git a/logfile b/logfile new file mode 100644 index 00000000..6aab8728 --- /dev/null +++ b/logfile @@ -0,0 +1,39 @@ +2024-07-16 10:29:09.983 CEST [21605] LOG: starting PostgreSQL 14.11 (Homebrew) on aarch64-apple-darwin22.6.0, compiled by Apple clang version 15.0.0 (clang-1500.1.0.2.5), 64-bit +2024-07-16 10:29:09.985 CEST [21605] LOG: listening on IPv6 address "::1", port 5432 +2024-07-16 10:29:09.985 CEST [21605] LOG: listening on IPv4 address "127.0.0.1", port 5432 +2024-07-16 10:29:09.986 CEST [21605] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432" +2024-07-16 10:29:09.996 CEST [21606] LOG: database system was shut down at 2024-05-04 13:45:41 CEST +2024-07-16 10:29:10.002 CEST [21605] LOG: database system is ready to accept connections +2024-07-16 10:29:13.986 CEST [21782] ERROR: relation "public.notes" does not exist at character 58 +2024-07-16 10:29:13.986 CEST [21782] STATEMENT: SELECT COUNT(*) FROM (SELECT "public"."notes"."uid" FROM "public"."notes" WHERE 1=1 OFFSET $1) AS "sub" +2024-07-16 10:29:14.132 CEST [21798] ERROR: column customers.office_uid does not exist at character 206 +2024-07-16 10:29:14.132 CEST [21798] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."status", "public"."customers"."contact_uid", "public"."customers"."created_at", "public"."customers"."updated_at", "public"."customers"."password", "public"."customers"."office_uid" FROM "public"."customers" WHERE "public"."customers"."contact_uid" IN ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20) OFFSET $21 +2024-07-16 10:29:30.826 CEST [22049] ERROR: column customers.office_uid does not exist at character 206 +2024-07-16 10:29:30.826 CEST [22049] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."status", "public"."customers"."contact_uid", "public"."customers"."created_at", "public"."customers"."updated_at", "public"."customers"."password", "public"."customers"."office_uid" FROM "public"."customers" WHERE "public"."customers"."contact_uid" IN ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20) OFFSET $21 +2024-07-16 10:29:49.261 CEST [22461] ERROR: relation "public.notes" does not exist at character 58 +2024-07-16 10:29:49.261 CEST [22461] STATEMENT: SELECT COUNT(*) FROM (SELECT "public"."notes"."uid" FROM "public"."notes" WHERE 1=1 OFFSET $1) AS "sub" +2024-07-16 10:29:49.463 CEST [22463] ERROR: column customers.office_uid does not exist at character 206 +2024-07-16 10:29:49.463 CEST [22463] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."status", "public"."customers"."contact_uid", "public"."customers"."created_at", "public"."customers"."updated_at", "public"."customers"."password", "public"."customers"."office_uid" FROM "public"."customers" WHERE "public"."customers"."contact_uid" IN ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20) OFFSET $21 +2024-07-16 10:32:09.636 CEST [24972] ERROR: relation "public.notes" does not exist at character 58 +2024-07-16 10:32:09.636 CEST [24972] STATEMENT: SELECT COUNT(*) FROM (SELECT "public"."notes"."uid" FROM "public"."notes" WHERE 1=1 OFFSET $1) AS "sub" +2024-07-16 10:32:09.903 CEST [24974] ERROR: column customers.office_uid does not exist at character 206 +2024-07-16 10:32:09.903 CEST [24974] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."status", "public"."customers"."contact_uid", "public"."customers"."created_at", "public"."customers"."updated_at", "public"."customers"."password", "public"."customers"."office_uid" FROM "public"."customers" WHERE "public"."customers"."contact_uid" IN ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20) OFFSET $21 +2024-07-16 10:32:13.659 CEST [25042] ERROR: relation "public.notes" does not exist at character 58 +2024-07-16 10:32:13.659 CEST [25042] STATEMENT: SELECT COUNT(*) FROM (SELECT "public"."notes"."uid" FROM "public"."notes" WHERE 1=1 OFFSET $1) AS "sub" +2024-07-16 10:32:13.960 CEST [25044] ERROR: column customers.office_uid does not exist at character 206 +2024-07-16 10:32:13.960 CEST [25044] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."status", "public"."customers"."contact_uid", "public"."customers"."created_at", "public"."customers"."updated_at", "public"."customers"."password", "public"."customers"."office_uid" FROM "public"."customers" WHERE "public"."customers"."contact_uid" IN ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20) OFFSET $21 +2024-07-16 10:32:26.772 CEST [25252] ERROR: column customers.office_uid does not exist at character 206 +2024-07-16 10:32:26.772 CEST [25252] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."status", "public"."customers"."contact_uid", "public"."customers"."created_at", "public"."customers"."updated_at", "public"."customers"."password", "public"."customers"."office_uid" FROM "public"."customers" WHERE "public"."customers"."contact_uid" IN ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20) OFFSET $21 +2024-07-16 10:32:45.488 CEST [25549] ERROR: column customers.office_uid does not exist at character 206 +2024-07-16 10:32:45.488 CEST [25549] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."status", "public"."customers"."contact_uid", "public"."customers"."created_at", "public"."customers"."updated_at", "public"."customers"."password", "public"."customers"."office_uid" FROM "public"."customers" WHERE 1=1 ORDER BY "public"."customers"."uid" ASC LIMIT $1 OFFSET $2 +2024-07-16 10:33:22.589 CEST [26127] ERROR: column customers.office_uid does not exist at character 206 +2024-07-16 10:33:22.589 CEST [26127] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."status", "public"."customers"."contact_uid", "public"."customers"."created_at", "public"."customers"."updated_at", "public"."customers"."password", "public"."customers"."office_uid" FROM "public"."customers" WHERE "public"."customers"."contact_uid" IN ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20) OFFSET $21 +2024-07-16 10:42:51.648 CEST [37340] ERROR: relation "public.notes" does not exist at character 58 +2024-07-16 10:42:51.648 CEST [37340] STATEMENT: SELECT COUNT(*) FROM (SELECT "public"."notes"."uid" FROM "public"."notes" WHERE 1=1 OFFSET $1) AS "sub" +2024-07-16 10:42:51.859 CEST [37342] ERROR: column customers.office_uid does not exist at character 206 +2024-07-16 10:42:51.859 CEST [37342] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."status", "public"."customers"."contact_uid", "public"."customers"."created_at", "public"."customers"."updated_at", "public"."customers"."password", "public"."customers"."office_uid" FROM "public"."customers" WHERE "public"."customers"."contact_uid" IN ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21) OFFSET $22 +2024-07-16 10:42:56.305 CEST [37406] ERROR: column customers.office_uid does not exist at character 36 +2024-07-16 10:42:56.305 CEST [37406] STATEMENT: SELECT "public"."customers"."uid", "public"."customers"."office_uid" FROM "public"."customers" WHERE "public"."customers"."office_uid" IN ($1,$2) OFFSET $3 +2024-07-16 10:45:03.049 CEST [39643] LOG: could not receive data from client: Connection reset by peer +2024-07-16 10:45:03.062 CEST [39645] ERROR: relation "_prisma_migrations" does not exist at character 126 +2024-07-16 10:45:03.062 CEST [39645] STATEMENT: SELECT "id", "checksum", "finished_at", "migration_name", "logs", "rolled_back_at", "started_at", "applied_steps_count" FROM "_prisma_migrations" ORDER BY "started_at" ASC From 14aa7e92cb059eb8ba907f8222242150d689d2ce Mon Sep 17 00:00:00 2001 From: Max S Date: Mon, 19 Aug 2024 10:54:12 +0200 Subject: [PATCH 02/16] :bug: change status to ASKED when delete the last document file --- src/app/api/customer/FilesController.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/app/api/customer/FilesController.ts b/src/app/api/customer/FilesController.ts index a5795688..12d91eae 100644 --- a/src/app/api/customer/FilesController.ts +++ b/src/app/api/customer/FilesController.ts @@ -159,13 +159,19 @@ export default class FilesController extends ApiController { //call service to get prisma entity const fileEntity = await this.filesService.deleteKeyAndArchive(uid); - if ( - !(fileFoundEntity.document!.files?.find((file) => file.archived_at === null && file.uid !== uid)) && - fileFoundEntity.document!.document_type!.name === "Autres documents" - ) { + + const isDocumentEmpty = !fileFoundEntity.document!.files?.find((file) => file.archived_at === null && file.uid !== uid); + if (isDocumentEmpty && fileFoundEntity.document!.document_type!.name === "Autres documents") { await this.documentService.delete(fileFoundEntity.document!.uid!); } + // Update document status to 'ASKED' if the deleted file was the last one + if (fileFoundEntity.document && fileFoundEntity.document.uid && isDocumentEmpty) { + const documentToUpdate = Document.hydrate(fileFoundEntity.document); + documentToUpdate.document_status = "ASKED"; + this.documentService.update(fileFoundEntity.document.uid, documentToUpdate); + } + //Hydrate ressource with prisma entity const file = File.hydrate(fileEntity, { strategy: "excludeAll" }); From bb2d1f1d3be97ccee067021826e4ea8f353d583a Mon Sep 17 00:00:00 2001 From: Max S Date: Wed, 21 Aug 2024 11:00:58 +0200 Subject: [PATCH 03/16] :bug: cant delete document with archived file --- src/services/admin/DocumentsService/DocumentsService.ts | 4 +++- src/services/notary/DocumentsService/DocumentsService.ts | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/services/admin/DocumentsService/DocumentsService.ts b/src/services/admin/DocumentsService/DocumentsService.ts index ce3f4242..8983e442 100644 --- a/src/services/admin/DocumentsService/DocumentsService.ts +++ b/src/services/admin/DocumentsService/DocumentsService.ts @@ -62,7 +62,9 @@ export default class DocumentsService extends BaseService { if (!documentEntity) throw new Error("document not found"); const document = Document.hydrate(documentEntity, { strategy: "excludeAll" }); - if (document.files && document.files.length !== 0) { + const isDocumentEmpty = document.files && !document!.files.find((file) => file.archived_at === null); + + if (!isDocumentEmpty) { throw new Error("Can't delete a document with file"); } return this.documentsRepository.delete(uid); diff --git a/src/services/notary/DocumentsService/DocumentsService.ts b/src/services/notary/DocumentsService/DocumentsService.ts index 0dce86dc..6fefc6ea 100644 --- a/src/services/notary/DocumentsService/DocumentsService.ts +++ b/src/services/notary/DocumentsService/DocumentsService.ts @@ -62,7 +62,9 @@ export default class DocumentsService extends BaseService { if (!documentEntity) throw new Error("document not found"); const document = Document.hydrate(documentEntity, { strategy: "excludeAll" }); - if (document.files && document.files.length !== 0 && document.document_status !== "REFUSED") { + const isDocumentEmpty = document.files && !document!.files.find((file) => file.archived_at === null); + + if (!isDocumentEmpty && document.document_status !== "REFUSED") { throw new Error("Can't delete a document with file"); } return this.documentsRepository.delete(uid); From 3e64472821efc85266cef06f6b53b796e5b9391a Mon Sep 17 00:00:00 2001 From: Vins Date: Mon, 9 Sep 2024 05:45:17 +0200 Subject: [PATCH 04/16] Doing --- package.json | 2 +- src/app/api/notary/CustomersController.ts | 65 +++++- .../api/notary/DocumentsNotaryController.ts | 207 ++++++++++++++++++ .../api/notary/DocumentsReminderController.ts | 87 ++++++++ src/app/index.ts | 4 + .../migration.sql | 45 ++++ .../migration.sql | 16 ++ .../migration.sql | 17 ++ src/common/databases/schema.prisma | 45 ++++ src/common/emails/EmailBuilder.ts | 28 +++ src/common/emails/Templates/EmailTemplates.ts | 1 + .../repositories/DocumentsNotaryRepository.ts | 102 +++++++++ .../DocumentsReminderRepository.ts | 46 ++++ .../repositories/FilesNotaryRepository.ts | 83 +++++++ .../common/FilesService/FilesService.ts | 24 ++ .../common/IdNotService/IdNotService.ts | 64 +++--- .../CustomersService/CustomersService.ts | 21 +- .../DocumentsNotaryService.ts | 61 ++++++ .../DocumentsReminder/DocumentsReminder.ts | 28 +++ 19 files changed, 906 insertions(+), 40 deletions(-) create mode 100644 src/app/api/notary/DocumentsNotaryController.ts create mode 100644 src/app/api/notary/DocumentsReminderController.ts create mode 100644 src/common/databases/migrations/20240830075809_notary_documents/migration.sql create mode 100644 src/common/databases/migrations/20240906073736_email_reminder/migration.sql create mode 100644 src/common/databases/migrations/20240909032319_document_notary_customer/migration.sql create mode 100644 src/common/repositories/DocumentsNotaryRepository.ts create mode 100644 src/common/repositories/DocumentsReminderRepository.ts create mode 100644 src/common/repositories/FilesNotaryRepository.ts create mode 100644 src/services/notary/DocumentsNotaryService/DocumentsNotaryService.ts create mode 100644 src/services/notary/DocumentsReminder/DocumentsReminder.ts diff --git a/package.json b/package.json index ba8a4fdc..0483be4f 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "file-type-checker": "^1.0.8", "fp-ts": "^2.16.1", "jsonwebtoken": "^9.0.0", - "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.151", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.160", "module-alias": "^2.2.2", "monocle-ts": "^2.3.13", "multer": "^1.4.5-lts.1", diff --git a/src/app/api/notary/CustomersController.ts b/src/app/api/notary/CustomersController.ts index 83d3d1c9..2df0e275 100644 --- a/src/app/api/notary/CustomersController.ts +++ b/src/app/api/notary/CustomersController.ts @@ -7,13 +7,14 @@ import { Customer } from "le-coffre-resources/dist/Notary"; import { validateOrReject } from "class-validator"; import authHandler from "@App/middlewares/AuthHandler"; import ruleHandler from "@App/middlewares/RulesHandler"; -import { Prisma } from "@prisma/client"; +import { Documents, Prisma } from "@prisma/client"; import customerHandler from "@App/middlewares/OfficeMembershipHandlers/CustomerHandler"; +import DocumentsService from "@Services/notary/DocumentsService/DocumentsService"; @Controller() @Service() export default class CustomersController extends ApiController { - constructor(private customersService: CustomersService) { + constructor(private customersService: CustomersService, private documentsService: DocumentsService) { super(); } @@ -215,4 +216,64 @@ export default class CustomersController extends ApiController { return; } } + + /** + * @description Send a reminder to a specific customer by uid to signe specific documents + */ + @Post("/api/v1/notary/customers/:uid/send_reminder", [authHandler, ruleHandler, customerHandler]) + protected async sendReminder(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + this.httpBadRequest(response, "No uid provided"); + return; + } + + const documentsUid = req.body.documentsUid; + if (!documentsUid || !Array.isArray(documentsUid)) { + this.httpBadRequest(response, "Invalid or missing documents"); + return; + } + + const documentEntities : Documents[] = []; + //For each document uid, use DocumentsService.getByUid to get the document entity and add it to the documents array + for (const documentUid of documentsUid) { + console.log("documentUid", documentUid); + const documentEntity = await this.documentsService.getByUid(documentUid, { document_type : true, folder : true }); + console.log("documentEntity", documentEntity); + + if (!documentEntity) { + this.httpBadRequest(response, "Document not found"); + return; + } + documentEntities.push(documentEntity); + } + + const customerEntity = await this.customersService.getByUid(uid, { contact: true, office : true }); + console.log("customerEntity", customerEntity); + + + if (!customerEntity) { + this.httpNotFoundRequest(response, "customer not found"); + return; + } + + //Hydrate ressource with prisma entity + const customer = Customer.hydrate(customerEntity, { strategy: "excludeAll" }); + console.log("customer", customer); + + + // Call service to send reminder with documents + const reminder = await this.customersService.sendDocumentsReminder(customer, documentEntities); + + console.log("Reminder sent", reminder); + + + // Success + this.httpSuccess(response, customer); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } } diff --git a/src/app/api/notary/DocumentsNotaryController.ts b/src/app/api/notary/DocumentsNotaryController.ts new file mode 100644 index 00000000..d6cf82e2 --- /dev/null +++ b/src/app/api/notary/DocumentsNotaryController.ts @@ -0,0 +1,207 @@ +import { Response, Request } from "express"; +import { Controller, Delete, Get, Post } from "@ControllerPattern/index"; +import ApiController from "@Common/system/controller-pattern/ApiController"; +import { Service } from "typedi"; +import DocumentsNotaryService from "@Services/notary/DocumentsNotaryService/DocumentsNotaryService"; +import { DocumentsNotary, Prisma } from "@prisma/client"; +import { Document, DocumentNotary, FileNotary } from "le-coffre-resources/dist/Notary"; +import authHandler from "@App/middlewares/AuthHandler"; +import ruleHandler from "@App/middlewares/RulesHandler"; +import documentHandler from "@App/middlewares/OfficeMembershipHandlers/DocumentHandler"; +import OfficeFoldersService from "@Services/notary/OfficeFoldersService/OfficeFoldersService"; +import CustomersService from "@Services/admin/CustomersService/CustomersService"; +import UsersService from "@Services/notary/UsersService/UsersService"; +import FilesService from "@Services/common/FilesService/FilesService"; +// import NotificationBuilder from "@Common/notifications/NotificationBuilder"; + +@Controller() +@Service() +export default class DocumentsNotaryController extends ApiController { + constructor( + private documentsNotaryService: DocumentsNotaryService, + private officeFoldersService: OfficeFoldersService, + private customerService: CustomersService, + private userService: UsersService, + private filesService: FilesService, + ) { + super(); + } + + /** + * @description Get all documents + * @returns IDocument[] list of documents + */ + @Get("/api/v1/notary/documents_notary", [authHandler, ruleHandler]) + protected async get(req: Request, response: Response) { + try { + //get query + let query: Prisma.DocumentsNotaryFindManyArgs = {}; + if (req.query["q"]) { + query = JSON.parse(req.query["q"] as string); + if (query.where?.uid) { + this.httpBadRequest(response, "You can't filter by uid"); + return; + } + } + const officeId: string = req.body.user.office_Id; + const officeWhereInput: Prisma.OfficesWhereInput = { uid: officeId }; + if (!query.where) query.where = { folder: { office: officeWhereInput } }; + query.where.folder!.office = officeWhereInput; + + //call service to get prisma entity + const documentEntities = await this.documentsNotaryService.get(query); + + //Hydrate ressource with prisma entity + const documents = Document.hydrateArray(documentEntities, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, documents); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } + + /** + * @description Create a new document + * @returns IDocument created + */ + @Post("/api/v1/notary/documents_notary", [authHandler]) + protected async post(req: Request, response: Response) { + try { + if(!req.file) return; + + const customer = await this.customerService.getByUid(req.body.customerUid); + if (!customer) return; + console.log("customer", customer); + + const folder = await this.officeFoldersService.getByUid(req.body.folderUid); + if (!folder) return; + console.log("folder", folder); + + const user = await this.userService.getByUid(req.body.user.userId); + if (!user) return; + + const documentNotaryEntity = DocumentNotary.hydrate({ + customer: customer, + folder: folder, + depositor: user, + }); + + const documentNotaryEntityCreated = await this.documentsNotaryService.create(documentNotaryEntity); + console.log("documentNotaryEntityCreated", documentNotaryEntityCreated); + + const query = JSON.stringify({ document: { uid: documentNotaryEntityCreated.uid } }); + + const fileEntity = FileNotary.hydrate(JSON.parse(query)); + + const fileEntityCreated = await this.filesService.createFileNotary(fileEntity, req.file); + console.log("fileEntityCreated", fileEntityCreated); + + + + // //init Document resource with request body values + // const documentNotaryEntity = DocumentNotary.hydrate(req.body); + // console.log(documentNotaryEntity); + + // const folder = await this.officeFoldersService.getByUid(documentNotaryEntity.folder?.uid!, { + // folder_anchor: true, + // }); + // if (!folder) { + // this.httpBadRequest(response, "Folder not found"); + // return; + // } + + // const folderRessource = OfficeFolder.hydrate(folder); + // if (folderRessource.folder_anchor) { + // this.httpBadRequest(response, "Cannot add document on an anchored or anchoring folder"); + // return; + // } + + // //validate document + // await validateOrReject(documentNotaryEntity, { groups: ["createDocument"], forbidUnknownValues: false }); + + // //call service to get prisma entity + // const documentNotaryEntityCreated = await this.documentsNotaryService.create(documentNotaryEntity); + + // //Hydrate ressource with prisma entity + // const documentNotary = DocumentNotary.hydrate(documentNotaryEntityCreated, { + // strategy: "excludeAll", + // }); + + // //success + // this.httpCreated(response, documentNotary); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } + + /** + * @description Delete a specific document + */ + @Delete("/api/v1/notary/documents_notary/:uid", [authHandler, ruleHandler, documentHandler]) + protected async delete(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + this.httpBadRequest(response, "No uid provided"); + return; + } + + const documentFound = await this.documentsNotaryService.getByUid(uid); + + if (!documentFound) { + this.httpNotFoundRequest(response, "document not found"); + return; + } + + //call service to get prisma entity + const documentEntity: DocumentsNotary = await this.documentsNotaryService.delete(uid); + + //Hydrate ressource with prisma entity + const document = Document.hydrate(documentEntity, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, document); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } + + /** + * @description Get a specific document by uid + */ + @Get("/api/v1/notary/documents_notary/:uid", [authHandler, ruleHandler, documentHandler]) + protected async getOneByUid(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + this.httpBadRequest(response, "No uid provided"); + return; + } + //get query + let query; + if (req.query["q"]) { + query = JSON.parse(req.query["q"] as string); + } + + const documentEntity = await this.documentsNotaryService.getByUid(uid, query); + + if (!documentEntity) { + this.httpNotFoundRequest(response, "document not found"); + return; + } + + //Hydrate ressource with prisma entity + const document = Document.hydrate(documentEntity, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, document); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } +} diff --git a/src/app/api/notary/DocumentsReminderController.ts b/src/app/api/notary/DocumentsReminderController.ts new file mode 100644 index 00000000..27829d6d --- /dev/null +++ b/src/app/api/notary/DocumentsReminderController.ts @@ -0,0 +1,87 @@ +import { Response, Request } from "express"; +import { Controller, Get } from "@ControllerPattern/index"; +import ApiController from "@Common/system/controller-pattern/ApiController"; +import { Service } from "typedi"; +import { Prisma } from "@prisma/client"; +import { DocumentReminder } from "le-coffre-resources/dist/Notary"; + +import authHandler from "@App/middlewares/AuthHandler"; + +import DocumentsReminderService from "@Services/notary/DocumentsReminder/DocumentsReminder"; +// import NotificationBuilder from "@Common/notifications/NotificationBuilder"; + +@Controller() +@Service() +export default class DocumentsReminderController extends ApiController { + constructor( + private documentsReminderService: DocumentsReminderService, + ) { + super(); + } + + /** + * @description Get all documents + * @returns IDocument[] list of documents + */ + @Get("/api/v1/notary/document_reminders", [authHandler]) + protected async get(req: Request, response: Response) { + try { + //get query + let query: Prisma.DocumentsReminderFindManyArgs = {}; + if (req.query["q"]) { + query = JSON.parse(req.query["q"] as string); + if(query.where?.uid) { + this.httpBadRequest(response, "You can't filter by uid"); + return; + } + } + + //call service to get prisma entity + const documentReminderEntities = await this.documentsReminderService.get(query); + + //Hydrate ressource with prisma entity + const documentReminders = DocumentReminder.hydrateArray(documentReminderEntities, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, documentReminders); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } + + // /** + // * @description Get a specific document by uid + // */ + // @Get("/api/v1/notary/document_reminders/:uid", [authHandler, ruleHandler, documentHandler]) + // protected async getOneByUid(req: Request, response: Response) { + // try { + // const uid = req.params["uid"]; + // if (!uid) { + // this.httpBadRequest(response, "No uid provided"); + // return; + // } + // //get query + // let query; + // if (req.query["q"]) { + // query = JSON.parse(req.query["q"] as string); + // } + + // const documentEntity = await this.documentsNotaryService.getByUid(uid, query); + + // if (!documentEntity) { + // this.httpNotFoundRequest(response, "document not found"); + // return; + // } + + // //Hydrate ressource with prisma entity + // const document = Document.hydrate(documentEntity, { strategy: "excludeAll" }); + + // //success + // this.httpSuccess(response, document); + // } catch (error) { + // this.httpInternalError(response, error); + // return; + // } + // } +} diff --git a/src/app/index.ts b/src/app/index.ts index 3b747d8a..56b47310 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -55,6 +55,8 @@ import StripeWebhooks from "@Common/webhooks/stripeWebhooks"; import RulesGroupsController from "./api/admin/RulesGroupsController"; import NotesController from "./api/customer/NotesController"; import MailchimpController from "./api/notary/MailchimpController"; +import DocumentsReminderController from "./api/notary/DocumentsReminderController"; +import DocumentsNotaryController from "./api/notary/DocumentsNotaryController"; /** * @description This allow to declare all controllers used in the application @@ -118,5 +120,7 @@ export default { Container.get(RulesGroupsController); Container.get(NotesController); Container.get(MailchimpController); + Container.get(DocumentsReminderController) + Container.get(DocumentsNotaryController) }, }; diff --git a/src/common/databases/migrations/20240830075809_notary_documents/migration.sql b/src/common/databases/migrations/20240830075809_notary_documents/migration.sql new file mode 100644 index 00000000..c0136620 --- /dev/null +++ b/src/common/databases/migrations/20240830075809_notary_documents/migration.sql @@ -0,0 +1,45 @@ +-- CreateTable +CREATE TABLE "documents_notary" ( + "uid" TEXT NOT NULL, + "folder_uid" VARCHAR(255) NOT NULL, + "depositor_uid" VARCHAR(255) NOT NULL, + "created_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3), + + CONSTRAINT "documents_notary_pkey" PRIMARY KEY ("uid") +); + +-- CreateTable +CREATE TABLE "files_notary" ( + "uid" TEXT NOT NULL, + "document_uid" VARCHAR(255) NOT NULL, + "file_path" VARCHAR(255) NOT NULL, + "file_name" VARCHAR(255) NOT NULL, + "mimetype" VARCHAR(255) NOT NULL, + "hash" VARCHAR(255) NOT NULL, + "size" INTEGER NOT NULL, + "archived_at" TIMESTAMP(3), + "key" VARCHAR(255), + "created_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3), + + CONSTRAINT "files_notary_pkey" PRIMARY KEY ("uid") +); + +-- CreateIndex +CREATE UNIQUE INDEX "documents_notary_uid_key" ON "documents_notary"("uid"); + +-- CreateIndex +CREATE UNIQUE INDEX "files_notary_uid_key" ON "files_notary"("uid"); + +-- CreateIndex +CREATE UNIQUE INDEX "files_notary_file_path_key" ON "files_notary"("file_path"); + +-- AddForeignKey +ALTER TABLE "documents_notary" ADD CONSTRAINT "documents_notary_folder_uid_fkey" FOREIGN KEY ("folder_uid") REFERENCES "office_folders"("uid") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "documents_notary" ADD CONSTRAINT "documents_notary_depositor_uid_fkey" FOREIGN KEY ("depositor_uid") REFERENCES "users"("uid") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "files_notary" ADD CONSTRAINT "files_notary_document_uid_fkey" FOREIGN KEY ("document_uid") REFERENCES "documents_notary"("uid") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/src/common/databases/migrations/20240906073736_email_reminder/migration.sql b/src/common/databases/migrations/20240906073736_email_reminder/migration.sql new file mode 100644 index 00000000..04034ca4 --- /dev/null +++ b/src/common/databases/migrations/20240906073736_email_reminder/migration.sql @@ -0,0 +1,16 @@ +-- CreateTable +CREATE TABLE "documents_reminder" ( + "uid" TEXT NOT NULL, + "document_uid" VARCHAR(255) NOT NULL, + "reminder_date" TIMESTAMP(3) NOT NULL, + "created_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3), + + CONSTRAINT "documents_reminder_pkey" PRIMARY KEY ("uid") +); + +-- CreateIndex +CREATE UNIQUE INDEX "documents_reminder_uid_key" ON "documents_reminder"("uid"); + +-- AddForeignKey +ALTER TABLE "documents_reminder" ADD CONSTRAINT "documents_reminder_document_uid_fkey" FOREIGN KEY ("document_uid") REFERENCES "documents"("uid") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/src/common/databases/migrations/20240909032319_document_notary_customer/migration.sql b/src/common/databases/migrations/20240909032319_document_notary_customer/migration.sql new file mode 100644 index 00000000..9b636a65 --- /dev/null +++ b/src/common/databases/migrations/20240909032319_document_notary_customer/migration.sql @@ -0,0 +1,17 @@ +/* + Warnings: + + - You are about to drop the column `archived_at` on the `files_notary` table. All the data in the column will be lost. + - You are about to drop the column `key` on the `files_notary` table. All the data in the column will be lost. + - Added the required column `customer_uid` to the `documents_notary` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "documents_notary" ADD COLUMN "customer_uid" VARCHAR(255) NOT NULL; + +-- AlterTable +ALTER TABLE "files_notary" DROP COLUMN "archived_at", +DROP COLUMN "key"; + +-- AddForeignKey +ALTER TABLE "documents_notary" ADD CONSTRAINT "documents_notary_customer_uid_fkey" FOREIGN KEY ("customer_uid") REFERENCES "customers"("uid") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/src/common/databases/schema.prisma b/src/common/databases/schema.prisma index 259c59f1..a88f4839 100644 --- a/src/common/databases/schema.prisma +++ b/src/common/databases/schema.prisma @@ -69,6 +69,7 @@ model Users { votes Votes[] user_notifications UserNotifications[] seats Seats[] + documents_notary DocumentsNotary[] @@map("users") } @@ -129,6 +130,7 @@ model Customers { office Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade) office_uid String @db.VarChar(255) notes Notes[] + documents_notary DocumentsNotary[] @@map("customers") } @@ -170,6 +172,7 @@ model OfficeFolders { stakeholders Users[] @relation("OfficeFolderHasStakeholders") customers Customers[] @relation("OfficeFolderHasCustomers") documents Documents[] + documents_notary DocumentsNotary[] folder_anchor OfficeFolderAnchors? @relation(fields: [folder_anchor_uid], references: [uid]) folder_anchor_uid String? @unique @db.VarChar(255) @@ -215,10 +218,37 @@ model Documents { updated_at DateTime? @updatedAt files Files[] document_history DocumentHistory[] + reminders DocumentsReminder[] @@map("documents") } +model DocumentsNotary { + uid String @id @unique @default(uuid()) + folder OfficeFolders @relation(fields: [folder_uid], references: [uid]) + folder_uid String @db.VarChar(255) + depositor Users @relation(fields: [depositor_uid], references: [uid], onDelete: Cascade) + depositor_uid String @db.VarChar(255) + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + files FilesNotary[] + customer Customers @relation(fields: [customer_uid], references: [uid], onDelete: Cascade) + customer_uid String @db.VarChar(255) + + @@map("documents_notary") +} + +model DocumentsReminder { + uid String @id @unique @default(uuid()) + document Documents @relation(fields: [document_uid], references: [uid], onDelete: Cascade) + document_uid String @db.VarChar(255) + reminder_date DateTime + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + + @@map("documents_reminder") +} + model DocumentHistory { uid String @id @unique @default(uuid()) document_status EDocumentStatus @default(ASKED) @@ -248,6 +278,21 @@ model Files { @@map("files") } +model FilesNotary { + uid String @id @unique @default(uuid()) + document_notary DocumentsNotary @relation(fields: [document_uid], references: [uid], onDelete: Cascade) + document_uid String @db.VarChar(255) + file_path String @unique @db.VarChar(255) + file_name String @db.VarChar(255) + mimetype String @db.VarChar(255) + hash String @db.VarChar(255) + size Int + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + + @@map("files_notary") +} + model DocumentTypes { uid String @id @unique @default(uuid()) name String @db.VarChar(255) diff --git a/src/common/emails/EmailBuilder.ts b/src/common/emails/EmailBuilder.ts index f3296d13..7d795ba9 100644 --- a/src/common/emails/EmailBuilder.ts +++ b/src/common/emails/EmailBuilder.ts @@ -7,6 +7,7 @@ import MailchimpService from "@Services/common/MailchimpService/MailchimpService import { BackendVariables } from "@Common/config/variables/Variables"; import UsersService from "@Services/super-admin/UsersService/UsersService"; import User from "le-coffre-resources/dist/SuperAdmin"; +import { Customer } from "le-coffre-resources/dist/Notary"; @Service() export default class EmailBuilder { @@ -138,4 +139,31 @@ export default class EmailBuilder { } + + public async sendReminder(customer: Customer, documents: Documents[]) { + const to = customer.contact!.email; + const templateVariables = { + office_name: customer.office?.name, + last_name: customer.contact!.last_name, + first_name: customer.contact!.first_name, + link: this.variables.APP_HOST, + }; + + const templateName = ETemplates.DOCUMENT_REMINDER; + const subject = "Vous avez des documents à déposer pour votre dossier."; + + this.mailchimpService.create({ + templateName, + to, + subject, + templateVariables, + uid: "", + from: null, + cc: [], + cci: [], + sentAt: null, + nbTrySend: null, + lastTrySendDate: null, + }); + } } diff --git a/src/common/emails/Templates/EmailTemplates.ts b/src/common/emails/Templates/EmailTemplates.ts index 520098e2..4943b885 100644 --- a/src/common/emails/Templates/EmailTemplates.ts +++ b/src/common/emails/Templates/EmailTemplates.ts @@ -3,4 +3,5 @@ export const ETemplates = { DOCUMENT_REFUSED: "DOCUMENT_REFUSED", DOCUMENT_RECAP: "DOCUMENT_RECAP", SUBSCRIPTION_INVITATION: "SUBSCRIPTION_INVITATION", + DOCUMENT_REMINDER: "DOCUMENT_REMINDER", }; \ No newline at end of file diff --git a/src/common/repositories/DocumentsNotaryRepository.ts b/src/common/repositories/DocumentsNotaryRepository.ts new file mode 100644 index 00000000..4d785adb --- /dev/null +++ b/src/common/repositories/DocumentsNotaryRepository.ts @@ -0,0 +1,102 @@ +import Database from "@Common/databases/database"; +import BaseRepository from "@Repositories/BaseRepository"; +import { Service } from "typedi"; +import { DocumentsNotary, Prisma } from "@prisma/client"; +import { DocumentNotary } from "le-coffre-resources/dist/Notary"; + +@Service() +export default class DocumentsNotaryRepository extends BaseRepository { + constructor(private database: Database) { + super(); + } + protected get model() { + return this.database.getClient().documentsNotary; + } + protected get instanceDb() { + return this.database.getClient(); + } + + /** + * @description : Find many documents + */ + public async findMany(query: Prisma.DocumentsNotaryFindManyArgs) { + query.take = Math.min(query.take || this.defaultFetchRows, this.maxFetchRows); + return this.model.findMany(query); + } + + /** + * @description : Create a document + */ + public async create(document: DocumentNotary): Promise { + const createArgs: Prisma.DocumentsNotaryCreateArgs = { + data: { + folder: { + connect: { + uid: document.folder!.uid, + }, + }, + depositor: { + connect: { + uid: document.depositor!.uid, + }, + }, + customer: { + connect: { + uid: document.customer!.uid, + }, + }, + }, + }; + + const documentCreated = await this.model.create({ ...createArgs }); + + return documentCreated; + } + + /** + * @description : Delete a document + */ + public async delete(uid: string): Promise { + return this.model.delete({ + where: { + uid: uid, + }, + }); + } + + /** + * @description : Find unique document + */ + public async findOneByUid(uid: string, query?: Prisma.DocumentsNotaryInclude): Promise { + return this.model.findUnique({ + where: { + uid: uid, + }, + include: query, + }); + } + + /** + * @description : Find unique document with relations + */ + public async findOneByUidWithOffice(uid: string) { + return this.model.findUnique({ + where: { + uid: uid, + }, + include: { folder: { include: { office: true } } }, + }); + } + + /** + * @description : Find unique document with relations + */ + public async findOneByUidWithFiles(uid: string) { + return this.model.findUnique({ + where: { + uid: uid, + }, + include: { files: true }, + }); + } +} diff --git a/src/common/repositories/DocumentsReminderRepository.ts b/src/common/repositories/DocumentsReminderRepository.ts new file mode 100644 index 00000000..1b5b1ac2 --- /dev/null +++ b/src/common/repositories/DocumentsReminderRepository.ts @@ -0,0 +1,46 @@ +import Database from "@Common/databases/database"; +import BaseRepository from "@Repositories/BaseRepository"; +import { Service } from "typedi"; +import { DocumentsReminder, Prisma } from "@prisma/client"; +import { DocumentReminder } from "le-coffre-resources/dist/Notary"; + +@Service() +export default class DocumentsReminderRepository extends BaseRepository { + constructor(private database: Database) { + super(); + } + protected get model() { + return this.database.getClient().documentsReminder; + } + protected get instanceDb() { + return this.database.getClient(); + } + + /** + * @description : Find many documents + */ + public async findMany(query: Prisma.DocumentsReminderFindManyArgs) { + query.take = Math.min(query.take || this.defaultFetchRows, this.maxFetchRows); + return this.model.findMany(query); + } + + /** + * @description : Create a document + */ + public async create(documentReminder: DocumentReminder): Promise { + const createArgs: Prisma.DocumentsReminderCreateArgs = { + data: { + document: { + connect: { + uid: documentReminder.document!.uid, + }, + }, + reminder_date: new Date(), + }, + }; + + const documentReminderCreated = await this.model.create({ ...createArgs }); + + return documentReminderCreated; + } +} diff --git a/src/common/repositories/FilesNotaryRepository.ts b/src/common/repositories/FilesNotaryRepository.ts new file mode 100644 index 00000000..6f05e843 --- /dev/null +++ b/src/common/repositories/FilesNotaryRepository.ts @@ -0,0 +1,83 @@ +import Database from "@Common/databases/database"; +import BaseRepository from "@Repositories/BaseRepository"; +import { Service } from "typedi"; +import { FilesNotary, Prisma } from "@prisma/client"; +import { FileNotary } from "le-coffre-resources/dist/Notary"; + +@Service() +export default class FilesNotaryRepository extends BaseRepository { + constructor(private database: Database) { + super(); + } + protected get model() { + return this.database.getClient().filesNotary; + } + protected get instanceDb() { + return this.database.getClient(); + } + + /** + * @description : Find many files + */ + public async findMany(query: Prisma.FilesNotaryFindManyArgs) { + query.take = Math.min(query.take || this.defaultFetchRows, this.maxFetchRows); + return this.model.findMany(query); + } + + /** + * @description : Create a file linked to a document + */ + public async create(file: FileNotary, key: string): Promise { + const createArgs: Prisma.FilesNotaryCreateArgs = { + data: { + document_notary: { + connect: { + uid: file.document!.uid, + }, + }, + file_name: file.file_name, + file_path: file.file_path, + mimetype: file.mimetype, + hash: file.hash, + size: file.size, + }, + }; + return this.model.create({ ...createArgs, include: { document_notary: true } }); + } + + /** + * @description : Find unique file + */ + public async findOneByUid(uid: string, query?: Prisma.FilesNotaryInclude) { + return this.model.findUnique({ + where: { + uid: uid, + }, + include: query, + }); + } + + /** + * @description : Find unique file with office + */ + public async findOneByUidWithOffice(uid: string) { + return this.model.findUnique({ + where: { + uid: uid, + }, + include: { document_notary: { include: { folder: { include: { office: true } } } } }, + }); + } + + /** + * @description : Find unique file with document + */ + public async findOneByUidWithDocument(uid: string) { + return this.model.findUnique({ + where: { + uid: uid, + }, + include: { document_notary: true }, + }); + } +} diff --git a/src/services/common/FilesService/FilesService.ts b/src/services/common/FilesService/FilesService.ts index ef02f19b..0e41dc52 100644 --- a/src/services/common/FilesService/FilesService.ts +++ b/src/services/common/FilesService/FilesService.ts @@ -8,6 +8,8 @@ import { BackendVariables } from "@Common/config/variables/Variables"; import { Readable } from "stream"; import { v4 } from "uuid"; import { Files, Prisma } from "@prisma/client"; +import { FileNotary } from "le-coffre-resources/dist/Notary"; +import FilesNotaryRepository from "@Repositories/FilesNotaryRepository"; @Service() export default class FilesService extends BaseService { @@ -16,6 +18,7 @@ export default class FilesService extends BaseService { private ipfsService: IpfsService, private variables: BackendVariables, private cryptoService: CryptoService, + private filesNotaryRepository: FilesNotaryRepository ) { super(); } @@ -85,6 +88,27 @@ export default class FilesService extends BaseService { return this.filesRepository.create(fileToCreate, key); } + /** + * @description : Create a new file + * @throws {Error} If file cannot be created + */ + public async createFileNotary(file: FileNotary, fileData: Express.Multer.File) { + const key = v4(); + const encryptedFile = await this.cryptoService.encrypt(fileData.buffer, key); + const hash = await this.cryptoService.getHash(fileData.buffer); + + const upload = await this.ipfsService.pinFile(Readable.from(encryptedFile), fileData.originalname); + let fileToCreate: FileNotary = file; + fileToCreate.file_name = fileData.originalname; + fileToCreate.file_path = this.variables.PINATA_GATEWAY.concat(upload.IpfsHash); + fileToCreate.mimetype = fileData.mimetype; + fileToCreate.size = fileData.size; + fileToCreate.hash = hash; + fileToCreate.archived_at = null; + + return this.filesNotaryRepository.create(fileToCreate, key); + } + /** * @description : Modify a new file * @throws {Error} If file cannot be modified diff --git a/src/services/common/IdNotService/IdNotService.ts b/src/services/common/IdNotService/IdNotService.ts index f18721ad..e9a8cda1 100644 --- a/src/services/common/IdNotService/IdNotService.ts +++ b/src/services/common/IdNotService/IdNotService.ts @@ -113,24 +113,22 @@ export default class IdNotService extends BaseService { super(); } - public async getIdNotToken(code: string) { + public async getIdNotToken(code: string) { const query = new URLSearchParams({ client_id: this.variables.IDNOT_CLIENT_ID, client_secret: this.variables.IDNOT_CLIENT_SECRET, redirect_uri: this.variables.IDNOT_REDIRECT_URL, code: code, grant_type: "authorization_code", - }); - + }); + const token = await fetch(this.variables.IDNOT_BASE_URL + this.variables.IDNOT_CONNEXION_URL + "?" + query, { method: "POST" }); - - if(token.status !== 200) console.error(await token.text()); - + + if (token.status !== 200) console.error(await token.text()); + const decodedToken = (await token.json()) as IIdNotToken; - - - const decodedIdToken = jwt.decode(decodedToken.id_token) as IdNotJwtPayload; - + + const decodedIdToken = jwt.decode(decodedToken.id_token) as IdNotJwtPayload; return decodedIdToken; } @@ -178,7 +176,7 @@ export default class IdNotService extends BaseService { } public async getOfficeMemberships(officeId: string) { - const officeInfos = await this.officeService.getByUid(officeId); + const officeInfos = await this.officeService.getByUid(officeId); const office = Office.hydrate(officeInfos!); const searchParams = new URLSearchParams({ key: this.variables.IDNOT_API_KEY, @@ -187,7 +185,7 @@ export default class IdNotService extends BaseService { await fetch(`${this.variables.IDNOT_API_BASE_URL}/api/pp/v2/entites/${office.idNot}/personnes?` + searchParams, { method: "GET", }) - ).json()) as any; + ).json()) as any; } public getOfficeStatus(statusName: string) { @@ -291,13 +289,14 @@ export default class IdNotService extends BaseService { await this.userService.updateCheckedAt(user.uid!); } - public async updateOffice(officeId: string) { + public async updateOffice(officeId: string) { const officeInfos = await this.officeService.getByUid(officeId); const office = Office.hydrate(officeInfos!); const searchParams = new URLSearchParams({ key: this.variables.IDNOT_API_KEY, }); - const officeRawData = await fetch(`${this.variables.IDNOT_API_BASE_URL}/api/pp/v2/entities/${office.idNot}?` + searchParams, { + + const officeRawData = await fetch(`${this.variables.IDNOT_API_BASE_URL}/api/pp/v2/entites/${office.idNot}?` + searchParams, { method: "GET", }); if (officeRawData.status === 404) { @@ -339,7 +338,7 @@ export default class IdNotService extends BaseService { const officeLocationData = (await ( await fetch(`${this.variables.IDNOT_API_BASE_URL + userData.entite.locationsUrl}?` + searchParams, { method: "GET" }) ).json()) as IOfficeLocation; - + const office = await this.officeService.get({ where: { idNot: decodedToken.entity_idn } }); // if(officeLocationData.result[0]!.adrGeoCodePostal.slice(0,2) !== "35") { @@ -378,17 +377,17 @@ export default class IdNotService extends BaseService { }, }; - if(!userToAdd.contact.email) { + if (!userToAdd.contact.email) { return null; } - + let userHydrated = User.hydrate(userToAdd); const user = await this.userService.create(userHydrated); const userOffice = await this.officeService.getByUid(user.office_uid); userHydrated = User.hydrate(user); const userOfficeHydrated = Office.hydrate(userOffice!); - if(office.length === 0) { + if (office.length === 0) { const officeRoles = await this.officeRolesService.get({ where: { office: { idNot: "0000" } }, include: { office: true, rules: true }, @@ -401,17 +400,16 @@ export default class IdNotService extends BaseService { where: { office: { idNot: "0000" } }, include: { office: true }, }); - + const officeRolesHydrated = OfficeRole.hydrateArray(officeRoles); const deedTypesHydrated = DeedType.hydrateArray(deedTypes); const documentTypesHydrated = DocumentType.hydrateArray(documentTypes); - - + await this.duplicateOfficeRoles(officeRolesHydrated, userOfficeHydrated); const documentTypesCreated = await this.duplicateDocumentTypes(documentTypesHydrated, userOfficeHydrated); await this.duplicateDeedTypes(deedTypesHydrated, documentTypesCreated, userOfficeHydrated); } - + const officeRole = await this.getOfficeRole(userData.typeLien.name, user.office_uid); if (officeRole) { @@ -427,37 +425,35 @@ export default class IdNotService extends BaseService { public async duplicateDocumentTypes(documentTypes: DocumentType[], office: Office): Promise { let newDocumentTypes: DocumentType[] = []; - for(const documentType of documentTypes) { + for (const documentType of documentTypes) { documentType.office = office; const documentTypeCreated = await this.documentTypesService.create(documentType); newDocumentTypes.push(DocumentType.hydrate(documentTypeCreated)); - }; + } return newDocumentTypes; } public async duplicateDeedTypes(deedTypes: DeedType[], documentTypes: DocumentType[], office: Office) { - for (const deedType of deedTypes) { + for (const deedType of deedTypes) { let newDocumentTypes: DocumentType[] = []; - for (const document of deedType.document_types!) { + for (const document of deedType.document_types!) { const newDocumentType = documentTypes.find((documentType) => documentType.name === document.name); - if(!newDocumentType) continue; + if (!newDocumentType) continue; newDocumentTypes.push(newDocumentType!); - }; + } deedType.document_types = newDocumentTypes; deedType.office = office; await this.deedTypesService.create(deedType); - }; + } } - public async duplicateOfficeRoles(officeRoles: OfficeRole[], office: Office){ - for(const officeRole of officeRoles) { + public async duplicateOfficeRoles(officeRoles: OfficeRole[], office: Office) { + for (const officeRole of officeRoles) { officeRole.office = office; await this.officeRolesService.create(officeRole); - }; + } } - - public async updateUsers() { const usersReq = await this.userService.getUsersToBeChecked(); const users = User.hydrateArray(usersReq); diff --git a/src/services/notary/CustomersService/CustomersService.ts b/src/services/notary/CustomersService/CustomersService.ts index 72e6923d..880d7c8d 100644 --- a/src/services/notary/CustomersService/CustomersService.ts +++ b/src/services/notary/CustomersService/CustomersService.ts @@ -1,12 +1,14 @@ -import { Customers, Prisma } from "@prisma/client"; +import EmailBuilder from "@Common/emails/EmailBuilder"; +import { Customers, Documents, Prisma } from "@prisma/client"; import CustomersRepository from "@Repositories/CustomersRepository"; import BaseService from "@Services/BaseService"; -import { Customer } from "le-coffre-resources/dist/Notary"; +import { Customer, DocumentReminder } from "le-coffre-resources/dist/Notary"; import { Service } from "typedi"; +import DocumentsReminderService from "../DocumentsReminder/DocumentsReminder"; @Service() export default class CustomersService extends BaseService { - constructor(private customerRepository: CustomersRepository) { + constructor(private customerRepository: CustomersRepository, private emailBuilder: EmailBuilder, private documentsReminderService : DocumentsReminderService) { super(); } @@ -49,4 +51,17 @@ export default class CustomersService extends BaseService { public async getByContact(contactUid: string): Promise { return this.customerRepository.findOneByContact(contactUid); } + + public async sendDocumentsReminder(customer: Customer, documents: Documents[]): Promise { + //Call email builder to send mail + const email = this.emailBuilder.sendReminder(customer, documents); + //Call DocumentsReminder service to create add the reminder in database + if (!email) return; + for (const document of documents) { + //Create document reminder + const documentReminder = new DocumentReminder(); + documentReminder.document = document; + await this.documentsReminderService.create(documentReminder); + } + } } diff --git a/src/services/notary/DocumentsNotaryService/DocumentsNotaryService.ts b/src/services/notary/DocumentsNotaryService/DocumentsNotaryService.ts new file mode 100644 index 00000000..865752ca --- /dev/null +++ b/src/services/notary/DocumentsNotaryService/DocumentsNotaryService.ts @@ -0,0 +1,61 @@ +import { DocumentsNotary, Prisma } from "@prisma/client"; +import { Document, DocumentNotary } from "le-coffre-resources/dist/Notary"; +import DocumentsNotaryRepository from "@Repositories/DocumentsNotaryRepository"; +import BaseService from "@Services/BaseService"; +import { Service } from "typedi"; + +@Service() +export default class DocumentsService extends BaseService { + constructor(private documentsNotaryRepository: DocumentsNotaryRepository) { + super(); + } + + /** + * @description : Get all documents + * @throws {Error} If documents cannot be get + */ + public async get(query: Prisma.DocumentsNotaryFindManyArgs) { + return this.documentsNotaryRepository.findMany(query); + } + + /** + * @description : Create a new document + * @throws {Error} If document cannot be created + */ + public async create(document: DocumentNotary): Promise { + return this.documentsNotaryRepository.create(document); + } + + /** + * @description : Delete a document + * @throws {Error} If document cannot be deleted + */ + public async delete(uid: string): Promise { + const documentEntity = await this.documentsNotaryRepository.findOneByUid(uid, { files: true }); + if (!documentEntity) throw new Error("document not found"); + const document = Document.hydrate(documentEntity, { strategy: "excludeAll" }); + + const isDocumentEmpty = document.files && !document!.files.find((file) => file.archived_at === null); + + if (!isDocumentEmpty && document.document_status !== "REFUSED") { + throw new Error("Can't delete a document with file"); + } + return this.documentsNotaryRepository.delete(uid); + } + + /** + * @description : Get a document by uid + * @throws {Error} If document cannot be get by uid + */ + public async getByUid(uid: string, query?: Prisma.DocumentsNotaryInclude): Promise { + return this.documentsNotaryRepository.findOneByUid(uid, query); + } + + /** + * @description : Get a document by uid + * @throws {Error} If document cannot be get by uid + */ + public async getByUidWithOffice(uid: string) { + return this.documentsNotaryRepository.findOneByUidWithOffice(uid); + } +} diff --git a/src/services/notary/DocumentsReminder/DocumentsReminder.ts b/src/services/notary/DocumentsReminder/DocumentsReminder.ts new file mode 100644 index 00000000..3ee1f823 --- /dev/null +++ b/src/services/notary/DocumentsReminder/DocumentsReminder.ts @@ -0,0 +1,28 @@ +import { DocumentsReminder, Prisma } from "@prisma/client"; +import { DocumentReminder } from "le-coffre-resources/dist/Notary"; +import BaseService from "@Services/BaseService"; +import { Service } from "typedi"; +import DocumentsReminderRepository from "@Repositories/DocumentsReminderRepository"; + +@Service() +export default class DocumentsReminderService extends BaseService { + constructor(private documentsReminderRepository: DocumentsReminderRepository) { + super(); + } + + /** + * @description : Get all documents + * @throws {Error} If documents cannot be get + */ + public async get(query: Prisma.DocumentsReminderFindManyArgs) { + return this.documentsReminderRepository.findMany(query); + } + + /** + * @description : Create a new document + * @throws {Error} If document cannot be created + */ + public async create(document: DocumentReminder): Promise { + return this.documentsReminderRepository.create(document); + } +} From 6db6ce3f75b3527470e182e18be1b5bb2ac097bf Mon Sep 17 00:00:00 2001 From: Vins Date: Mon, 9 Sep 2024 10:11:11 +0200 Subject: [PATCH 05/16] endpoint entites --- src/services/common/IdNotService/IdNotService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/common/IdNotService/IdNotService.ts b/src/services/common/IdNotService/IdNotService.ts index f18721ad..647ae465 100644 --- a/src/services/common/IdNotService/IdNotService.ts +++ b/src/services/common/IdNotService/IdNotService.ts @@ -297,7 +297,7 @@ export default class IdNotService extends BaseService { const searchParams = new URLSearchParams({ key: this.variables.IDNOT_API_KEY, }); - const officeRawData = await fetch(`${this.variables.IDNOT_API_BASE_URL}/api/pp/v2/entities/${office.idNot}?` + searchParams, { + const officeRawData = await fetch(`${this.variables.IDNOT_API_BASE_URL}/api/pp/v2/entites/${office.idNot}?` + searchParams, { method: "GET", }); if (officeRawData.status === 404) { From 8f5974ab59f74a98ce95a34a89f49d549aaaa673 Mon Sep 17 00:00:00 2001 From: Vins Date: Mon, 9 Sep 2024 11:15:28 +0200 Subject: [PATCH 06/16] Fixed Notary Documents --- .../api/notary/DocumentsNotaryController.ts | 130 +++++++++--------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/src/app/api/notary/DocumentsNotaryController.ts b/src/app/api/notary/DocumentsNotaryController.ts index d6cf82e2..1552646e 100644 --- a/src/app/api/notary/DocumentsNotaryController.ts +++ b/src/app/api/notary/DocumentsNotaryController.ts @@ -1,13 +1,11 @@ import { Response, Request } from "express"; -import { Controller, Delete, Get, Post } from "@ControllerPattern/index"; +import { Controller, Get, Post } from "@ControllerPattern/index"; import ApiController from "@Common/system/controller-pattern/ApiController"; import { Service } from "typedi"; import DocumentsNotaryService from "@Services/notary/DocumentsNotaryService/DocumentsNotaryService"; -import { DocumentsNotary, Prisma } from "@prisma/client"; -import { Document, DocumentNotary, FileNotary } from "le-coffre-resources/dist/Notary"; +import { Prisma } from "@prisma/client"; +import { DocumentNotary, FileNotary } from "le-coffre-resources/dist/Notary"; import authHandler from "@App/middlewares/AuthHandler"; -import ruleHandler from "@App/middlewares/RulesHandler"; -import documentHandler from "@App/middlewares/OfficeMembershipHandlers/DocumentHandler"; import OfficeFoldersService from "@Services/notary/OfficeFoldersService/OfficeFoldersService"; import CustomersService from "@Services/admin/CustomersService/CustomersService"; import UsersService from "@Services/notary/UsersService/UsersService"; @@ -31,7 +29,7 @@ export default class DocumentsNotaryController extends ApiController { * @description Get all documents * @returns IDocument[] list of documents */ - @Get("/api/v1/notary/documents_notary", [authHandler, ruleHandler]) + @Get("/api/v1/notary/documents_notary", [authHandler]) protected async get(req: Request, response: Response) { try { //get query @@ -52,7 +50,7 @@ export default class DocumentsNotaryController extends ApiController { const documentEntities = await this.documentsNotaryService.get(query); //Hydrate ressource with prisma entity - const documents = Document.hydrateArray(documentEntities, { strategy: "excludeAll" }); + const documents = DocumentNotary.hydrateArray(documentEntities, { strategy: "excludeAll" }); //success this.httpSuccess(response, documents); @@ -69,6 +67,8 @@ export default class DocumentsNotaryController extends ApiController { @Post("/api/v1/notary/documents_notary", [authHandler]) protected async post(req: Request, response: Response) { try { + console.log("req.file", req.file); + if(!req.file) return; const customer = await this.customerService.getByUid(req.body.customerUid); @@ -95,7 +95,7 @@ export default class DocumentsNotaryController extends ApiController { const fileEntity = FileNotary.hydrate(JSON.parse(query)); - const fileEntityCreated = await this.filesService.createFileNotary(fileEntity, req.file); + const fileEntityCreated = await this.filesService.createFileNotary(fileEntity, req.file!); console.log("fileEntityCreated", fileEntityCreated); @@ -137,71 +137,71 @@ export default class DocumentsNotaryController extends ApiController { } } - /** - * @description Delete a specific document - */ - @Delete("/api/v1/notary/documents_notary/:uid", [authHandler, ruleHandler, documentHandler]) - protected async delete(req: Request, response: Response) { - try { - const uid = req.params["uid"]; - if (!uid) { - this.httpBadRequest(response, "No uid provided"); - return; - } + // /** + // * @description Delete a specific document + // */ + // @Delete("/api/v1/notary/documents_notary/:uid", [authHandler, ruleHandler, documentHandler]) + // protected async delete(req: Request, response: Response) { + // try { + // const uid = req.params["uid"]; + // if (!uid) { + // this.httpBadRequest(response, "No uid provided"); + // return; + // } - const documentFound = await this.documentsNotaryService.getByUid(uid); + // const documentFound = await this.documentsNotaryService.getByUid(uid); - if (!documentFound) { - this.httpNotFoundRequest(response, "document not found"); - return; - } + // if (!documentFound) { + // this.httpNotFoundRequest(response, "document not found"); + // return; + // } - //call service to get prisma entity - const documentEntity: DocumentsNotary = await this.documentsNotaryService.delete(uid); + // //call service to get prisma entity + // const documentEntity: DocumentsNotary = await this.documentsNotaryService.delete(uid); - //Hydrate ressource with prisma entity - const document = Document.hydrate(documentEntity, { strategy: "excludeAll" }); + // //Hydrate ressource with prisma entity + // const document = Document.hydrate(documentEntity, { strategy: "excludeAll" }); - //success - this.httpSuccess(response, document); - } catch (error) { - this.httpInternalError(response, error); - return; - } - } + // //success + // this.httpSuccess(response, document); + // } catch (error) { + // this.httpInternalError(response, error); + // return; + // } + // } - /** - * @description Get a specific document by uid - */ - @Get("/api/v1/notary/documents_notary/:uid", [authHandler, ruleHandler, documentHandler]) - protected async getOneByUid(req: Request, response: Response) { - try { - const uid = req.params["uid"]; - if (!uid) { - this.httpBadRequest(response, "No uid provided"); - return; - } - //get query - let query; - if (req.query["q"]) { - query = JSON.parse(req.query["q"] as string); - } + // /** + // * @description Get a specific document by uid + // */ + // @Get("/api/v1/notary/documents_notary/:uid", [authHandler, ruleHandler, documentHandler]) + // protected async getOneByUid(req: Request, response: Response) { + // try { + // const uid = req.params["uid"]; + // if (!uid) { + // this.httpBadRequest(response, "No uid provided"); + // return; + // } + // //get query + // let query; + // if (req.query["q"]) { + // query = JSON.parse(req.query["q"] as string); + // } - const documentEntity = await this.documentsNotaryService.getByUid(uid, query); + // const documentEntity = await this.documentsNotaryService.getByUid(uid, query); - if (!documentEntity) { - this.httpNotFoundRequest(response, "document not found"); - return; - } + // if (!documentEntity) { + // this.httpNotFoundRequest(response, "document not found"); + // return; + // } - //Hydrate ressource with prisma entity - const document = Document.hydrate(documentEntity, { strategy: "excludeAll" }); + // //Hydrate ressource with prisma entity + // const document = Document.hydrate(documentEntity, { strategy: "excludeAll" }); - //success - this.httpSuccess(response, document); - } catch (error) { - this.httpInternalError(response, error); - return; - } - } + // //success + // this.httpSuccess(response, document); + // } catch (error) { + // this.httpInternalError(response, error); + // return; + // } + // } } From a68d8b817a70d315b154cbde25f66066e4818343 Mon Sep 17 00:00:00 2001 From: Vins Date: Mon, 9 Sep 2024 15:10:24 +0200 Subject: [PATCH 07/16] Endpoint delete document notary --- .../api/customer/DocumentsNotaryController.ts | 50 +++++++ .../api/notary/DocumentsNotaryController.ts | 138 +++++------------- 2 files changed, 87 insertions(+), 101 deletions(-) create mode 100644 src/app/api/customer/DocumentsNotaryController.ts diff --git a/src/app/api/customer/DocumentsNotaryController.ts b/src/app/api/customer/DocumentsNotaryController.ts new file mode 100644 index 00000000..4fd90e4e --- /dev/null +++ b/src/app/api/customer/DocumentsNotaryController.ts @@ -0,0 +1,50 @@ +import { Response, Request } from "express"; +import { Controller, Get } from "@ControllerPattern/index"; +import ApiController from "@Common/system/controller-pattern/ApiController"; +import { Service } from "typedi"; +import DocumentsNotaryService from "@Services/notary/DocumentsNotaryService/DocumentsNotaryService"; +import { Prisma } from "@prisma/client"; +import { DocumentNotary } from "le-coffre-resources/dist/Notary"; +import authHandler from "@App/middlewares/AuthHandler"; +// import NotificationBuilder from "@Common/notifications/NotificationBuilder"; + +@Controller() +@Service() +export default class DocumentsNotaryController extends ApiController { + constructor( + private documentsNotaryService: DocumentsNotaryService, + ) { + super(); + } + + /** + * @description Get all documents + * @returns IDocument[] list of documents + */ + @Get("/api/v1/customer/documents_notary", [authHandler]) + protected async get(req: Request, response: Response) { + try { + //get query + let query: Prisma.DocumentsNotaryFindManyArgs = {}; + if (req.query["q"]) { + query = JSON.parse(req.query["q"] as string); + if (query.where?.uid) { + this.httpBadRequest(response, "You can't filter by uid"); + return; + } + } + + //call service to get prisma entity + const documentEntities = await this.documentsNotaryService.get(query); + + //Hydrate ressource with prisma entity + const documents = DocumentNotary.hydrateArray(documentEntities, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, documents); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } +} diff --git a/src/app/api/notary/DocumentsNotaryController.ts b/src/app/api/notary/DocumentsNotaryController.ts index 1552646e..afcd826e 100644 --- a/src/app/api/notary/DocumentsNotaryController.ts +++ b/src/app/api/notary/DocumentsNotaryController.ts @@ -1,9 +1,9 @@ import { Response, Request } from "express"; -import { Controller, Get, Post } from "@ControllerPattern/index"; +import { Controller, Delete, Get, Post } from "@ControllerPattern/index"; import ApiController from "@Common/system/controller-pattern/ApiController"; import { Service } from "typedi"; import DocumentsNotaryService from "@Services/notary/DocumentsNotaryService/DocumentsNotaryService"; -import { Prisma } from "@prisma/client"; +import { DocumentsNotary, Prisma } from "@prisma/client"; import { DocumentNotary, FileNotary } from "le-coffre-resources/dist/Notary"; import authHandler from "@App/middlewares/AuthHandler"; import OfficeFoldersService from "@Services/notary/OfficeFoldersService/OfficeFoldersService"; @@ -41,10 +41,6 @@ export default class DocumentsNotaryController extends ApiController { return; } } - const officeId: string = req.body.user.office_Id; - const officeWhereInput: Prisma.OfficesWhereInput = { uid: officeId }; - if (!query.where) query.where = { folder: { office: officeWhereInput } }; - query.where.folder!.office = officeWhereInput; //call service to get prisma entity const documentEntities = await this.documentsNotaryService.get(query); @@ -96,112 +92,52 @@ export default class DocumentsNotaryController extends ApiController { const fileEntity = FileNotary.hydrate(JSON.parse(query)); const fileEntityCreated = await this.filesService.createFileNotary(fileEntity, req.file!); - console.log("fileEntityCreated", fileEntityCreated); - - + if(!fileEntityCreated) { + this.httpBadRequest(response, "File could not be created"); + return; + }; - // //init Document resource with request body values - // const documentNotaryEntity = DocumentNotary.hydrate(req.body); - // console.log(documentNotaryEntity); + const documentNotary = await this.documentsNotaryService.getByUid(documentNotaryEntityCreated.uid); - // const folder = await this.officeFoldersService.getByUid(documentNotaryEntity.folder?.uid!, { - // folder_anchor: true, - // }); - // if (!folder) { - // this.httpBadRequest(response, "Folder not found"); - // return; - // } - - // const folderRessource = OfficeFolder.hydrate(folder); - // if (folderRessource.folder_anchor) { - // this.httpBadRequest(response, "Cannot add document on an anchored or anchoring folder"); - // return; - // } - - // //validate document - // await validateOrReject(documentNotaryEntity, { groups: ["createDocument"], forbidUnknownValues: false }); - - // //call service to get prisma entity - // const documentNotaryEntityCreated = await this.documentsNotaryService.create(documentNotaryEntity); - - // //Hydrate ressource with prisma entity - // const documentNotary = DocumentNotary.hydrate(documentNotaryEntityCreated, { - // strategy: "excludeAll", - // }); - - // //success - // this.httpCreated(response, documentNotary); + const document = DocumentNotary.hydrate(documentNotary!); + //success + this.httpCreated(response, document); } catch (error) { this.httpInternalError(response, error); return; } } - // /** - // * @description Delete a specific document - // */ - // @Delete("/api/v1/notary/documents_notary/:uid", [authHandler, ruleHandler, documentHandler]) - // protected async delete(req: Request, response: Response) { - // try { - // const uid = req.params["uid"]; - // if (!uid) { - // this.httpBadRequest(response, "No uid provided"); - // return; - // } + /** + * @description Delete a specific document + */ + @Delete("/api/v1/notary/documents_notary/:uid", [authHandler]) + protected async delete(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + this.httpBadRequest(response, "No uid provided"); + return; + } - // const documentFound = await this.documentsNotaryService.getByUid(uid); + const documentNotaryFound = await this.documentsNotaryService.getByUid(uid); - // if (!documentFound) { - // this.httpNotFoundRequest(response, "document not found"); - // return; - // } + if (!documentNotaryFound) { + this.httpNotFoundRequest(response, "document not found"); + return; + } - // //call service to get prisma entity - // const documentEntity: DocumentsNotary = await this.documentsNotaryService.delete(uid); + //call service to get prisma entity + const documentNotaryEntity: DocumentsNotary = await this.documentsNotaryService.delete(uid); - // //Hydrate ressource with prisma entity - // const document = Document.hydrate(documentEntity, { strategy: "excludeAll" }); + //Hydrate ressource with prisma entity + const documentNotary = DocumentNotary.hydrate(documentNotaryEntity, { strategy: "excludeAll" }); - // //success - // this.httpSuccess(response, document); - // } catch (error) { - // this.httpInternalError(response, error); - // return; - // } - // } - - // /** - // * @description Get a specific document by uid - // */ - // @Get("/api/v1/notary/documents_notary/:uid", [authHandler, ruleHandler, documentHandler]) - // protected async getOneByUid(req: Request, response: Response) { - // try { - // const uid = req.params["uid"]; - // if (!uid) { - // this.httpBadRequest(response, "No uid provided"); - // return; - // } - // //get query - // let query; - // if (req.query["q"]) { - // query = JSON.parse(req.query["q"] as string); - // } - - // const documentEntity = await this.documentsNotaryService.getByUid(uid, query); - - // if (!documentEntity) { - // this.httpNotFoundRequest(response, "document not found"); - // return; - // } - - // //Hydrate ressource with prisma entity - // const document = Document.hydrate(documentEntity, { strategy: "excludeAll" }); - - // //success - // this.httpSuccess(response, document); - // } catch (error) { - // this.httpInternalError(response, error); - // return; - // } - // } + //success + this.httpSuccess(response, documentNotary); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } } From 982be25e5b0ef5c653e869a7cbaab1b63edd371c Mon Sep 17 00:00:00 2001 From: Vins Date: Tue, 10 Sep 2024 10:14:53 +0200 Subject: [PATCH 08/16] document_name --- .../migrations/20240910081445_document_name/migration.sql | 2 ++ src/common/databases/schema.prisma | 1 + 2 files changed, 3 insertions(+) create mode 100644 src/common/databases/migrations/20240910081445_document_name/migration.sql diff --git a/src/common/databases/migrations/20240910081445_document_name/migration.sql b/src/common/databases/migrations/20240910081445_document_name/migration.sql new file mode 100644 index 00000000..97e5f5c0 --- /dev/null +++ b/src/common/databases/migrations/20240910081445_document_name/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "documents_notary" ADD COLUMN "document_name" VARCHAR(255) NOT NULL DEFAULT ''; diff --git a/src/common/databases/schema.prisma b/src/common/databases/schema.prisma index a88f4839..52aa353e 100644 --- a/src/common/databases/schema.prisma +++ b/src/common/databases/schema.prisma @@ -234,6 +234,7 @@ model DocumentsNotary { files FilesNotary[] customer Customers @relation(fields: [customer_uid], references: [uid], onDelete: Cascade) customer_uid String @db.VarChar(255) + document_name String @db.VarChar(255) @default("") @@map("documents_notary") } From 41efb43543bdfc5063e5a1b13ce8b97cf5fb4afe Mon Sep 17 00:00:00 2001 From: Vins Date: Tue, 10 Sep 2024 10:15:53 +0200 Subject: [PATCH 09/16] name --- .../migrations/20240910081547_name/migration.sql | 9 +++++++++ src/common/databases/schema.prisma | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 src/common/databases/migrations/20240910081547_name/migration.sql diff --git a/src/common/databases/migrations/20240910081547_name/migration.sql b/src/common/databases/migrations/20240910081547_name/migration.sql new file mode 100644 index 00000000..18e8d895 --- /dev/null +++ b/src/common/databases/migrations/20240910081547_name/migration.sql @@ -0,0 +1,9 @@ +/* + Warnings: + + - You are about to drop the column `document_name` on the `documents_notary` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "documents_notary" DROP COLUMN "document_name", +ADD COLUMN "document" VARCHAR(255) NOT NULL DEFAULT ''; diff --git a/src/common/databases/schema.prisma b/src/common/databases/schema.prisma index 52aa353e..5007857f 100644 --- a/src/common/databases/schema.prisma +++ b/src/common/databases/schema.prisma @@ -234,7 +234,7 @@ model DocumentsNotary { files FilesNotary[] customer Customers @relation(fields: [customer_uid], references: [uid], onDelete: Cascade) customer_uid String @db.VarChar(255) - document_name String @db.VarChar(255) @default("") + document String @db.VarChar(255) @default("") @@map("documents_notary") } From 5bd5e096cac95fbe38c1b1695bc48035c6b130e6 Mon Sep 17 00:00:00 2001 From: Max S Date: Tue, 10 Sep 2024 18:11:10 +0200 Subject: [PATCH 10/16] add filesNotaryController download route --- src/app/api/notary/CustomersController.ts | 5 - .../api/notary/DocumentsNotaryController.ts | 36 +++--- src/app/api/notary/FilesController.ts | 6 +- src/app/api/notary/FilesNotaryController.ts | 109 ++++++++++++++++++ src/app/index.ts | 6 +- .../FilesNotaryHandler.ts | 48 ++++++++ .../common/FilesNotaryService/FilesService.ts | 90 +++++++++++++++ .../common/FilesService/FilesService.ts | 35 +----- 8 files changed, 275 insertions(+), 60 deletions(-) create mode 100644 src/app/api/notary/FilesNotaryController.ts create mode 100644 src/app/middlewares/OfficeMembershipHandlers/FilesNotaryHandler.ts create mode 100644 src/services/common/FilesNotaryService/FilesService.ts diff --git a/src/app/api/notary/CustomersController.ts b/src/app/api/notary/CustomersController.ts index 2df0e275..8dc1e1c9 100644 --- a/src/app/api/notary/CustomersController.ts +++ b/src/app/api/notary/CustomersController.ts @@ -238,9 +238,7 @@ export default class CustomersController extends ApiController { const documentEntities : Documents[] = []; //For each document uid, use DocumentsService.getByUid to get the document entity and add it to the documents array for (const documentUid of documentsUid) { - console.log("documentUid", documentUid); const documentEntity = await this.documentsService.getByUid(documentUid, { document_type : true, folder : true }); - console.log("documentEntity", documentEntity); if (!documentEntity) { this.httpBadRequest(response, "Document not found"); @@ -250,7 +248,6 @@ export default class CustomersController extends ApiController { } const customerEntity = await this.customersService.getByUid(uid, { contact: true, office : true }); - console.log("customerEntity", customerEntity); if (!customerEntity) { @@ -260,13 +257,11 @@ export default class CustomersController extends ApiController { //Hydrate ressource with prisma entity const customer = Customer.hydrate(customerEntity, { strategy: "excludeAll" }); - console.log("customer", customer); // Call service to send reminder with documents const reminder = await this.customersService.sendDocumentsReminder(customer, documentEntities); - console.log("Reminder sent", reminder); // Success diff --git a/src/app/api/notary/DocumentsNotaryController.ts b/src/app/api/notary/DocumentsNotaryController.ts index afcd826e..56ea9cdf 100644 --- a/src/app/api/notary/DocumentsNotaryController.ts +++ b/src/app/api/notary/DocumentsNotaryController.ts @@ -1,16 +1,15 @@ -import { Response, Request } from "express"; -import { Controller, Delete, Get, Post } from "@ControllerPattern/index"; -import ApiController from "@Common/system/controller-pattern/ApiController"; -import { Service } from "typedi"; -import DocumentsNotaryService from "@Services/notary/DocumentsNotaryService/DocumentsNotaryService"; -import { DocumentsNotary, Prisma } from "@prisma/client"; -import { DocumentNotary, FileNotary } from "le-coffre-resources/dist/Notary"; import authHandler from "@App/middlewares/AuthHandler"; -import OfficeFoldersService from "@Services/notary/OfficeFoldersService/OfficeFoldersService"; +import ApiController from "@Common/system/controller-pattern/ApiController"; +import { Controller, Delete, Get, Post } from "@ControllerPattern/index"; +import { DocumentsNotary, Prisma } from "@prisma/client"; import CustomersService from "@Services/admin/CustomersService/CustomersService"; +import FilesNotaryService from "@Services/common/FilesNotaryService/FilesService"; +import DocumentsNotaryService from "@Services/notary/DocumentsNotaryService/DocumentsNotaryService"; +import OfficeFoldersService from "@Services/notary/OfficeFoldersService/OfficeFoldersService"; import UsersService from "@Services/notary/UsersService/UsersService"; -import FilesService from "@Services/common/FilesService/FilesService"; -// import NotificationBuilder from "@Common/notifications/NotificationBuilder"; +import { Request, Response } from "express"; +import { DocumentNotary, FileNotary } from "le-coffre-resources/dist/Notary"; +import { Service } from "typedi"; @Controller() @Service() @@ -20,7 +19,7 @@ export default class DocumentsNotaryController extends ApiController { private officeFoldersService: OfficeFoldersService, private customerService: CustomersService, private userService: UsersService, - private filesService: FilesService, + private filesNotaryService: FilesNotaryService, ) { super(); } @@ -63,17 +62,13 @@ export default class DocumentsNotaryController extends ApiController { @Post("/api/v1/notary/documents_notary", [authHandler]) protected async post(req: Request, response: Response) { try { - console.log("req.file", req.file); - - if(!req.file) return; + if (!req.file) return; const customer = await this.customerService.getByUid(req.body.customerUid); if (!customer) return; - console.log("customer", customer); const folder = await this.officeFoldersService.getByUid(req.body.folderUid); if (!folder) return; - console.log("folder", folder); const user = await this.userService.getByUid(req.body.user.userId); if (!user) return; @@ -82,20 +77,19 @@ export default class DocumentsNotaryController extends ApiController { customer: customer, folder: folder, depositor: user, - }); + }); const documentNotaryEntityCreated = await this.documentsNotaryService.create(documentNotaryEntity); - console.log("documentNotaryEntityCreated", documentNotaryEntityCreated); const query = JSON.stringify({ document: { uid: documentNotaryEntityCreated.uid } }); const fileEntity = FileNotary.hydrate(JSON.parse(query)); - const fileEntityCreated = await this.filesService.createFileNotary(fileEntity, req.file!); - if(!fileEntityCreated) { + const fileEntityCreated = await this.filesNotaryService.create(fileEntity, req.file!); + if (!fileEntityCreated) { this.httpBadRequest(response, "File could not be created"); return; - }; + } const documentNotary = await this.documentsNotaryService.getByUid(documentNotaryEntityCreated.uid); diff --git a/src/app/api/notary/FilesController.ts b/src/app/api/notary/FilesController.ts index 46a4969b..a630218c 100644 --- a/src/app/api/notary/FilesController.ts +++ b/src/app/api/notary/FilesController.ts @@ -27,14 +27,14 @@ export default class FilesController extends ApiController { let query: Prisma.FilesFindManyArgs = {}; if (req.query["q"]) { query = JSON.parse(req.query["q"] as string); - if(query.where?.uid) { + if (query.where?.uid) { this.httpBadRequest(response, "You can't filter by uid"); return; } } const officeId: string = req.body.user.office_Id; - const officeWhereInput: Prisma.OfficesWhereInput = { uid: officeId } ; - if(!query.where) query.where = { document: { folder: {office: officeWhereInput}}}; + const officeWhereInput: Prisma.OfficesWhereInput = { uid: officeId }; + if (!query.where) query.where = { document: { folder: { office: officeWhereInput } } }; query.where.document!.folder!.office = officeWhereInput; //call service to get prisma entity const fileEntities = await this.filesService.get(query); diff --git a/src/app/api/notary/FilesNotaryController.ts b/src/app/api/notary/FilesNotaryController.ts new file mode 100644 index 00000000..2fb116e2 --- /dev/null +++ b/src/app/api/notary/FilesNotaryController.ts @@ -0,0 +1,109 @@ +import authHandler from "@App/middlewares/AuthHandler"; +import FilesNotaryHandler from "@App/middlewares/OfficeMembershipHandlers/FilesNotaryHandler"; +import ApiController from "@Common/system/controller-pattern/ApiController"; +import { Controller, Get } from "@ControllerPattern/index"; +import { Prisma } from "@prisma/client"; +import FilesNotaryService from "@Services/common/FilesNotaryService/FilesService"; +import { Request, Response } from "express"; +import { File } from "le-coffre-resources/dist/Notary"; +import { Service } from "typedi"; + +@Controller() +@Service() +export default class FilesNotaryController extends ApiController { + constructor(private filesNotaryService: FilesNotaryService) { + super(); + } + + /** + * @description Get all Files + * @returns File[] list of Files + */ + @Get("/api/v1/notary/files-notary", [authHandler]) + protected async get(req: Request, response: Response) { + try { + //get query + let query: Prisma.FilesFindManyArgs = {}; + if (req.query["q"]) { + query = JSON.parse(req.query["q"] as string); + if (query.where?.uid) { + this.httpBadRequest(response, "You can't filter by uid"); + return; + } + } + const officeId: string = req.body.user.office_Id; + const officeWhereInput: Prisma.OfficesWhereInput = { uid: officeId }; + if (!query.where) query.where = { document: { folder: { office: officeWhereInput } } }; + query.where.document!.folder!.office = officeWhereInput; + //call service to get prisma entity + const fileEntities = await this.filesNotaryService.get(query); + + //Hydrate ressource with prisma entity + const files = File.hydrateArray(fileEntities, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, files); + } catch (error) { + this.httpBadRequest(response, error); + return; + } + } + + /** + * @description Get a specific File by uid + */ + @Get("/api/v1/notary/files-notary/download/:uid", [authHandler, FilesNotaryHandler]) + protected async download(req: Request, response: Response) { + const uid = req.params["uid"]; + if (!uid) { + this.httpBadRequest(response, "uid not found"); + return; + } + try { + const fileInfo = await this.filesNotaryService.download(uid); + + if (!fileInfo) { + this.httpNotFoundRequest(response, "file not found"); + return; + } + + response.setHeader("Content-Type", fileInfo.file.mimetype); + response.setHeader("Content-Disposition", `inline; filename=${encodeURIComponent(fileInfo.file.file_name)}`); + + this.httpSuccess(response, fileInfo.buffer); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } + + /** + * @description Get a specific File by uid + */ + @Get("/api/v1/notary/files-notary/:uid", [authHandler, FilesNotaryHandler]) + protected async getOneByUid(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + this.httpBadRequest(response, "No uid provided"); + return; + } + + const fileEntity = await this.filesNotaryService.getByUid(uid); + + if (!fileEntity) { + this.httpNotFoundRequest(response, "file not found"); + return; + } + + //Hydrate ressource with prisma entity + const file = File.hydrate(fileEntity, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, file); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } +} diff --git a/src/app/index.ts b/src/app/index.ts index 56b47310..f2bcaf1c 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -57,6 +57,7 @@ import NotesController from "./api/customer/NotesController"; import MailchimpController from "./api/notary/MailchimpController"; import DocumentsReminderController from "./api/notary/DocumentsReminderController"; import DocumentsNotaryController from "./api/notary/DocumentsNotaryController"; +import FilesNotaryController from "./api/notary/FilesNotaryController"; /** * @description This allow to declare all controllers used in the application @@ -120,7 +121,8 @@ export default { Container.get(RulesGroupsController); Container.get(NotesController); Container.get(MailchimpController); - Container.get(DocumentsReminderController) - Container.get(DocumentsNotaryController) + Container.get(DocumentsReminderController); + Container.get(DocumentsNotaryController); + Container.get(FilesNotaryController); }, }; diff --git a/src/app/middlewares/OfficeMembershipHandlers/FilesNotaryHandler.ts b/src/app/middlewares/OfficeMembershipHandlers/FilesNotaryHandler.ts new file mode 100644 index 00000000..b3453183 --- /dev/null +++ b/src/app/middlewares/OfficeMembershipHandlers/FilesNotaryHandler.ts @@ -0,0 +1,48 @@ +import HttpCodes from "@Common/system/controller-pattern/HttpCodes"; +import FilesNotaryService from "@Services/common/FilesNotaryService/FilesService"; +import DocumentsNotaryService from "@Services/notary/DocumentsNotaryService/DocumentsNotaryService"; +import { NextFunction, Request, Response } from "express"; +import Container from "typedi"; + +export default async function FilesNotaryHandler(req: Request, response: Response, next: NextFunction) { + try { + const officeId = req.body.user.office_Id; + let uid = req.path && req.path.split("/")[5]; + const document = req.body.document; + + if (document) { + const documentService = Container.get(DocumentsNotaryService); + const documentWithOffice = await documentService.getByUidWithOffice(document.uid!); + if (!documentWithOffice) { + response.status(HttpCodes.NOT_FOUND).send("Document not found"); + return; + } + if (documentWithOffice.folder.office?.uid != officeId) { + response.status(HttpCodes.UNAUTHORIZED).send("Unauthorized with this office"); + return; + } + } + + if (uid === "download") uid = req.path && req.path.split("/")[6]; + + if (uid) { + const fileService = Container.get(FilesNotaryService); + const file = await fileService.getByUidWithOffice(uid!); + + if (!file) { + response.status(HttpCodes.NOT_FOUND).send("File not found"); + return; + } + if (file.document_notary.folder.office.uid != officeId) { + response.status(HttpCodes.UNAUTHORIZED).send("Unauthorized with this office"); + return; + } + } + + next(); + } catch (error) { + console.error(error); + response.status(HttpCodes.INTERNAL_ERROR).send("Internal server error"); + return; + } +} diff --git a/src/services/common/FilesNotaryService/FilesService.ts b/src/services/common/FilesNotaryService/FilesService.ts new file mode 100644 index 00000000..7089a374 --- /dev/null +++ b/src/services/common/FilesNotaryService/FilesService.ts @@ -0,0 +1,90 @@ +import { BackendVariables } from "@Common/config/variables/Variables"; +import { Prisma } from "@prisma/client"; +import FilesNotaryRepository from "@Repositories/FilesNotaryRepository"; +import FilesRepository from "@Repositories/FilesRepository"; +import BaseService from "@Services/BaseService"; +import { FileNotary } from "le-coffre-resources/dist/Notary"; +import { Readable } from "stream"; +import { Service } from "typedi"; +import { v4 } from "uuid"; + +import CryptoService from "../CryptoService/CryptoService"; +import IpfsService from "../IpfsService/IpfsService"; + +@Service() +export default class FilesNotaryService extends BaseService { + constructor( + private filesRepository: FilesRepository, + private ipfsService: IpfsService, + private variables: BackendVariables, + private cryptoService: CryptoService, + private filesNotaryRepository: FilesNotaryRepository, + ) { + super(); + } + + /** + * @description : Get all files + * @throws {Error} If files cannot be ge + */ + public async get(query: Prisma.FilesFindManyArgs) { + return this.filesRepository.findMany(query); + } + + /** + * @description : Get a file by uid + * @throws {Error} If project cannot be created + */ + public async getByUid(uid: string) { + return this.filesNotaryRepository.findOneByUid(uid); + } + + /** + * @description : Get a file by uid with office + * @throws {Error} If project cannot be created + */ + public async getByUidWithOffice(uid: string) { + return this.filesNotaryRepository.findOneByUidWithOffice(uid); + } + + /** + * @description : Get a file by uid with document + * @throws {Error} If project cannot be created + */ + public async getByUidWithDocument(uid: string) { + return this.filesNotaryRepository.findOneByUidWithDocument(uid); + } + + /** + * @description : view a file + * @throws {Error} If file cannot be deleted + */ + public async download(uid: string) { + const file = await this.filesNotaryRepository.findOneByUid(uid); + if (!file?.uid) return null; + const fileResult = await fetch(file.file_path.concat("?pinataGatewayToken=").concat(this.variables.PINATA_GATEWAY_TOKEN)); + const fileArrayBuffer = await fileResult.arrayBuffer(); + return { file: file, buffer: await this.cryptoService.decrypt(Buffer.from(fileArrayBuffer), file.uid) }; + } + + /** + * @description : Create a new file + * @throws {Error} If file cannot be created + */ + public async create(file: FileNotary, fileData: Express.Multer.File) { + const key = v4(); + const encryptedFile = await this.cryptoService.encrypt(fileData.buffer, key); + const hash = await this.cryptoService.getHash(fileData.buffer); + + const upload = await this.ipfsService.pinFile(Readable.from(encryptedFile), fileData.originalname); + let fileToCreate: FileNotary = file; + fileToCreate.file_name = fileData.originalname; + fileToCreate.file_path = this.variables.PINATA_GATEWAY.concat(upload.IpfsHash); + fileToCreate.mimetype = fileData.mimetype; + fileToCreate.size = fileData.size; + fileToCreate.hash = hash; + fileToCreate.archived_at = null; + + return this.filesNotaryRepository.create(fileToCreate, key); + } +} diff --git a/src/services/common/FilesService/FilesService.ts b/src/services/common/FilesService/FilesService.ts index 0e41dc52..a05b2ae1 100644 --- a/src/services/common/FilesService/FilesService.ts +++ b/src/services/common/FilesService/FilesService.ts @@ -1,15 +1,14 @@ +import { BackendVariables } from "@Common/config/variables/Variables"; +import { Files, Prisma } from "@prisma/client"; import FilesRepository from "@Repositories/FilesRepository"; import BaseService from "@Services/BaseService"; -import { Service } from "typedi"; import { File } from "le-coffre-resources/dist/SuperAdmin"; +import { Readable } from "stream"; +import { Service } from "typedi"; +import { v4 } from "uuid"; + import CryptoService from "../CryptoService/CryptoService"; import IpfsService from "../IpfsService/IpfsService"; -import { BackendVariables } from "@Common/config/variables/Variables"; -import { Readable } from "stream"; -import { v4 } from "uuid"; -import { Files, Prisma } from "@prisma/client"; -import { FileNotary } from "le-coffre-resources/dist/Notary"; -import FilesNotaryRepository from "@Repositories/FilesNotaryRepository"; @Service() export default class FilesService extends BaseService { @@ -18,7 +17,6 @@ export default class FilesService extends BaseService { private ipfsService: IpfsService, private variables: BackendVariables, private cryptoService: CryptoService, - private filesNotaryRepository: FilesNotaryRepository ) { super(); } @@ -88,27 +86,6 @@ export default class FilesService extends BaseService { return this.filesRepository.create(fileToCreate, key); } - /** - * @description : Create a new file - * @throws {Error} If file cannot be created - */ - public async createFileNotary(file: FileNotary, fileData: Express.Multer.File) { - const key = v4(); - const encryptedFile = await this.cryptoService.encrypt(fileData.buffer, key); - const hash = await this.cryptoService.getHash(fileData.buffer); - - const upload = await this.ipfsService.pinFile(Readable.from(encryptedFile), fileData.originalname); - let fileToCreate: FileNotary = file; - fileToCreate.file_name = fileData.originalname; - fileToCreate.file_path = this.variables.PINATA_GATEWAY.concat(upload.IpfsHash); - fileToCreate.mimetype = fileData.mimetype; - fileToCreate.size = fileData.size; - fileToCreate.hash = hash; - fileToCreate.archived_at = null; - - return this.filesNotaryRepository.create(fileToCreate, key); - } - /** * @description : Modify a new file * @throws {Error} If file cannot be modified From bcd12b5317a3816235e613bb71030b14625b3a88 Mon Sep 17 00:00:00 2001 From: Max S Date: Tue, 10 Sep 2024 18:16:32 +0200 Subject: [PATCH 11/16] remove console log --- src/app/api/notary/CustomersController.ts | 24 ++++++++++------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/app/api/notary/CustomersController.ts b/src/app/api/notary/CustomersController.ts index 8dc1e1c9..ebb1b541 100644 --- a/src/app/api/notary/CustomersController.ts +++ b/src/app/api/notary/CustomersController.ts @@ -220,7 +220,7 @@ export default class CustomersController extends ApiController { /** * @description Send a reminder to a specific customer by uid to signe specific documents */ - @Post("/api/v1/notary/customers/:uid/send_reminder", [authHandler, ruleHandler, customerHandler]) + @Post("/api/v1/notary/customers/:uid/send_reminder", [authHandler, ruleHandler, customerHandler]) protected async sendReminder(req: Request, response: Response) { try { const uid = req.params["uid"]; @@ -228,18 +228,18 @@ export default class CustomersController extends ApiController { this.httpBadRequest(response, "No uid provided"); return; } - + const documentsUid = req.body.documentsUid; if (!documentsUid || !Array.isArray(documentsUid)) { this.httpBadRequest(response, "Invalid or missing documents"); return; } - const documentEntities : Documents[] = []; + const documentEntities: Documents[] = []; //For each document uid, use DocumentsService.getByUid to get the document entity and add it to the documents array for (const documentUid of documentsUid) { - const documentEntity = await this.documentsService.getByUid(documentUid, { document_type : true, folder : true }); - + const documentEntity = await this.documentsService.getByUid(documentUid, { document_type: true, folder: true }); + if (!documentEntity) { this.httpBadRequest(response, "Document not found"); return; @@ -247,9 +247,8 @@ export default class CustomersController extends ApiController { documentEntities.push(documentEntity); } - const customerEntity = await this.customersService.getByUid(uid, { contact: true, office : true }); - - + const customerEntity = await this.customersService.getByUid(uid, { contact: true, office: true }); + if (!customerEntity) { this.httpNotFoundRequest(response, "customer not found"); return; @@ -257,13 +256,10 @@ export default class CustomersController extends ApiController { //Hydrate ressource with prisma entity const customer = Customer.hydrate(customerEntity, { strategy: "excludeAll" }); - - - // Call service to send reminder with documents - const reminder = await this.customersService.sendDocumentsReminder(customer, documentEntities); - - + // Call service to send reminder with documents + await this.customersService.sendDocumentsReminder(customer, documentEntities); + // Success this.httpSuccess(response, customer); } catch (error) { From 95f2c8a8bc04f638e3f2806d6271005504dc3915 Mon Sep 17 00:00:00 2001 From: Max S Date: Tue, 10 Sep 2024 18:38:15 +0200 Subject: [PATCH 12/16] :truck: delete filesNotaryHandler middleware --- src/app/api/notary/FilesNotaryController.ts | 5 +- .../FilesNotaryHandler.ts | 48 ------------------- 2 files changed, 2 insertions(+), 51 deletions(-) delete mode 100644 src/app/middlewares/OfficeMembershipHandlers/FilesNotaryHandler.ts diff --git a/src/app/api/notary/FilesNotaryController.ts b/src/app/api/notary/FilesNotaryController.ts index 2fb116e2..9b7e6742 100644 --- a/src/app/api/notary/FilesNotaryController.ts +++ b/src/app/api/notary/FilesNotaryController.ts @@ -1,5 +1,4 @@ import authHandler from "@App/middlewares/AuthHandler"; -import FilesNotaryHandler from "@App/middlewares/OfficeMembershipHandlers/FilesNotaryHandler"; import ApiController from "@Common/system/controller-pattern/ApiController"; import { Controller, Get } from "@ControllerPattern/index"; import { Prisma } from "@prisma/client"; @@ -52,7 +51,7 @@ export default class FilesNotaryController extends ApiController { /** * @description Get a specific File by uid */ - @Get("/api/v1/notary/files-notary/download/:uid", [authHandler, FilesNotaryHandler]) + @Get("/api/v1/notary/files-notary/download/:uid", [authHandler]) protected async download(req: Request, response: Response) { const uid = req.params["uid"]; if (!uid) { @@ -80,7 +79,7 @@ export default class FilesNotaryController extends ApiController { /** * @description Get a specific File by uid */ - @Get("/api/v1/notary/files-notary/:uid", [authHandler, FilesNotaryHandler]) + @Get("/api/v1/notary/files-notary/:uid", [authHandler]) protected async getOneByUid(req: Request, response: Response) { try { const uid = req.params["uid"]; diff --git a/src/app/middlewares/OfficeMembershipHandlers/FilesNotaryHandler.ts b/src/app/middlewares/OfficeMembershipHandlers/FilesNotaryHandler.ts deleted file mode 100644 index b3453183..00000000 --- a/src/app/middlewares/OfficeMembershipHandlers/FilesNotaryHandler.ts +++ /dev/null @@ -1,48 +0,0 @@ -import HttpCodes from "@Common/system/controller-pattern/HttpCodes"; -import FilesNotaryService from "@Services/common/FilesNotaryService/FilesService"; -import DocumentsNotaryService from "@Services/notary/DocumentsNotaryService/DocumentsNotaryService"; -import { NextFunction, Request, Response } from "express"; -import Container from "typedi"; - -export default async function FilesNotaryHandler(req: Request, response: Response, next: NextFunction) { - try { - const officeId = req.body.user.office_Id; - let uid = req.path && req.path.split("/")[5]; - const document = req.body.document; - - if (document) { - const documentService = Container.get(DocumentsNotaryService); - const documentWithOffice = await documentService.getByUidWithOffice(document.uid!); - if (!documentWithOffice) { - response.status(HttpCodes.NOT_FOUND).send("Document not found"); - return; - } - if (documentWithOffice.folder.office?.uid != officeId) { - response.status(HttpCodes.UNAUTHORIZED).send("Unauthorized with this office"); - return; - } - } - - if (uid === "download") uid = req.path && req.path.split("/")[6]; - - if (uid) { - const fileService = Container.get(FilesNotaryService); - const file = await fileService.getByUidWithOffice(uid!); - - if (!file) { - response.status(HttpCodes.NOT_FOUND).send("File not found"); - return; - } - if (file.document_notary.folder.office.uid != officeId) { - response.status(HttpCodes.UNAUTHORIZED).send("Unauthorized with this office"); - return; - } - } - - next(); - } catch (error) { - console.error(error); - response.status(HttpCodes.INTERNAL_ERROR).send("Internal server error"); - return; - } -} From bb9547be9bb8761650fadb2cd31869b4b927bc96 Mon Sep 17 00:00:00 2001 From: Max S Date: Wed, 11 Sep 2024 10:51:18 +0200 Subject: [PATCH 13/16] rename filesNotaryService --- src/app/api/notary/DocumentsNotaryController.ts | 2 +- src/app/api/notary/FilesNotaryController.ts | 3 ++- .../{FilesService.ts => FilesNotaryService.ts} | 0 3 files changed, 3 insertions(+), 2 deletions(-) rename src/services/common/FilesNotaryService/{FilesService.ts => FilesNotaryService.ts} (100%) diff --git a/src/app/api/notary/DocumentsNotaryController.ts b/src/app/api/notary/DocumentsNotaryController.ts index 56ea9cdf..900a54d5 100644 --- a/src/app/api/notary/DocumentsNotaryController.ts +++ b/src/app/api/notary/DocumentsNotaryController.ts @@ -3,7 +3,7 @@ import ApiController from "@Common/system/controller-pattern/ApiController"; import { Controller, Delete, Get, Post } from "@ControllerPattern/index"; import { DocumentsNotary, Prisma } from "@prisma/client"; import CustomersService from "@Services/admin/CustomersService/CustomersService"; -import FilesNotaryService from "@Services/common/FilesNotaryService/FilesService"; +import FilesNotaryService from "@Services/common/FilesNotaryService/FilesNotaryService"; import DocumentsNotaryService from "@Services/notary/DocumentsNotaryService/DocumentsNotaryService"; import OfficeFoldersService from "@Services/notary/OfficeFoldersService/OfficeFoldersService"; import UsersService from "@Services/notary/UsersService/UsersService"; diff --git a/src/app/api/notary/FilesNotaryController.ts b/src/app/api/notary/FilesNotaryController.ts index 9b7e6742..acda6176 100644 --- a/src/app/api/notary/FilesNotaryController.ts +++ b/src/app/api/notary/FilesNotaryController.ts @@ -2,7 +2,8 @@ import authHandler from "@App/middlewares/AuthHandler"; import ApiController from "@Common/system/controller-pattern/ApiController"; import { Controller, Get } from "@ControllerPattern/index"; import { Prisma } from "@prisma/client"; -import FilesNotaryService from "@Services/common/FilesNotaryService/FilesService"; +import FilesNotaryService from "@Services/common/FilesNotaryService/FilesNotaryService"; + import { Request, Response } from "express"; import { File } from "le-coffre-resources/dist/Notary"; import { Service } from "typedi"; diff --git a/src/services/common/FilesNotaryService/FilesService.ts b/src/services/common/FilesNotaryService/FilesNotaryService.ts similarity index 100% rename from src/services/common/FilesNotaryService/FilesService.ts rename to src/services/common/FilesNotaryService/FilesNotaryService.ts From 2eddba94be946fcd96f5ded5bec1709c89761b57 Mon Sep 17 00:00:00 2001 From: Max S Date: Thu, 12 Sep 2024 12:13:49 +0200 Subject: [PATCH 14/16] chore: Add key column key to files_notary table --- .../migration.sql | 2 + src/common/databases/schema.prisma | 312 +++++++++--------- .../repositories/FilesNotaryRepository.ts | 1 + .../FilesNotaryService/FilesNotaryService.ts | 4 +- 4 files changed, 166 insertions(+), 153 deletions(-) create mode 100644 src/common/databases/migrations/20240912095804_add_key_for_files_notary/migration.sql diff --git a/src/common/databases/migrations/20240912095804_add_key_for_files_notary/migration.sql b/src/common/databases/migrations/20240912095804_add_key_for_files_notary/migration.sql new file mode 100644 index 00000000..c44e5fda --- /dev/null +++ b/src/common/databases/migrations/20240912095804_add_key_for_files_notary/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "files_notary" ADD COLUMN "key" VARCHAR(255); diff --git a/src/common/databases/schema.prisma b/src/common/databases/schema.prisma index 5007857f..8e58f283 100644 --- a/src/common/databases/schema.prisma +++ b/src/common/databases/schema.prisma @@ -37,7 +37,7 @@ model Contacts { last_name String @db.VarChar(255) email String @db.VarChar(255) phone_number String? @db.VarChar(50) - cell_phone_number String? @db.VarChar(50) + cell_phone_number String? @db.VarChar(50) civility ECivility @default(MALE) address Addresses? @relation(fields: [address_uid], references: [uid], onDelete: Cascade) address_uid String? @unique @db.VarChar(255) @@ -51,25 +51,25 @@ model Contacts { } model Users { - uid String @id @unique @default(uuid()) @map("uid") - idNot String @unique @db.VarChar(255) - contact Contacts @relation(fields: [contact_uid], references: [uid], onDelete: Cascade) - contact_uid String @unique @db.VarChar(255) - role Roles @relation(fields: [roles_uid], references: [uid], onDelete: Cascade) - roles_uid String @db.VarChar(255) - office_role OfficeRoles? @relation(fields: [office_role_uid], references: [uid], onDelete: Cascade) - office_role_uid String? @db.VarChar(255) - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt - checked_at DateTime? - office_membership Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade) - office_uid String @db.VarChar(255) - office_folders OfficeFolders[] @relation("OfficeFolderHasStakeholders") - appointment Appointments[] - votes Votes[] + uid String @id @unique @default(uuid()) @map("uid") + idNot String @unique @db.VarChar(255) + contact Contacts @relation(fields: [contact_uid], references: [uid], onDelete: Cascade) + contact_uid String @unique @db.VarChar(255) + role Roles @relation(fields: [roles_uid], references: [uid], onDelete: Cascade) + roles_uid String @db.VarChar(255) + office_role OfficeRoles? @relation(fields: [office_role_uid], references: [uid], onDelete: Cascade) + office_role_uid String? @db.VarChar(255) + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + checked_at DateTime? + office_membership Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade) + office_uid String @db.VarChar(255) + office_folders OfficeFolders[] @relation("OfficeFolderHasStakeholders") + appointment Appointments[] + votes Votes[] user_notifications UserNotifications[] - seats Seats[] - documents_notary DocumentsNotary[] + seats Seats[] + documents_notary DocumentsNotary[] @@map("users") } @@ -80,6 +80,7 @@ model Whitelist { active Boolean @default(true) created_at DateTime? @default(now()) updated_at DateTime? @updatedAt + @@map("whitelist") } @@ -89,6 +90,7 @@ model UserWhitelist { active Boolean @default(true) created_at DateTime? @default(now()) updated_at DateTime? @updatedAt + @@map("user_whitelist") } @@ -117,135 +119,136 @@ model Offices { } model Customers { - uid String @id @unique @default(uuid()) - status ECustomerStatus @default(PENDING) - contact Contacts @relation(fields: [contact_uid], references: [uid], onDelete: Cascade) - contact_uid String @unique @db.VarChar(255) - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt - office_folders OfficeFolders[] @relation("OfficeFolderHasCustomers") - documents Documents[] - password String? @db.VarChar(255) - totpCodes TotpCodes[] - office Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade) - office_uid String @db.VarChar(255) - notes Notes[] + uid String @id @unique @default(uuid()) + status ECustomerStatus @default(PENDING) + contact Contacts @relation(fields: [contact_uid], references: [uid], onDelete: Cascade) + contact_uid String @unique @db.VarChar(255) + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + office_folders OfficeFolders[] @relation("OfficeFolderHasCustomers") + documents Documents[] + password String? @db.VarChar(255) + totpCodes TotpCodes[] + office Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade) + office_uid String @db.VarChar(255) + notes Notes[] documents_notary DocumentsNotary[] + @@map("customers") } model Notifications { - uid String @id @unique @default(uuid()) - message String @db.VarChar(1000) - redirection_url String @db.VarChar(255) - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt - userNotifications UserNotifications[] + uid String @id @unique @default(uuid()) + message String @db.VarChar(1000) + redirection_url String @db.VarChar(255) + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + userNotifications UserNotifications[] @@map("notifications") } model UserNotifications { - uid String @id @unique @default(uuid()) - user Users @relation(fields: [user_uid], references: [uid], onDelete: Cascade) - user_uid String @db.VarChar(255) - read Boolean @default(false) - notification Notifications @relation(fields: [notification_uid], references: [uid], onDelete: Cascade) - notification_uid String @db.VarChar(255) + uid String @id @unique @default(uuid()) + user Users @relation(fields: [user_uid], references: [uid], onDelete: Cascade) + user_uid String @db.VarChar(255) + read Boolean @default(false) + notification Notifications @relation(fields: [notification_uid], references: [uid], onDelete: Cascade) + notification_uid String @db.VarChar(255) @@map("user_notifications") } model OfficeFolders { - uid String @id @unique @default(uuid()) - folder_number String @db.VarChar(255) - name String @db.VarChar(255) - description String? @db.VarChar(1000) - archived_description String? @db.VarChar(255) - status EFolderStatus @default(LIVE) - deed Deeds @relation(fields: [deed_uid], references: [uid], onDelete: Cascade) - deed_uid String @unique @db.VarChar(255) - office Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade) - office_uid String @db.VarChar(255) - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt - stakeholders Users[] @relation("OfficeFolderHasStakeholders") - customers Customers[] @relation("OfficeFolderHasCustomers") + uid String @id @unique @default(uuid()) + folder_number String @db.VarChar(255) + name String @db.VarChar(255) + description String? @db.VarChar(1000) + archived_description String? @db.VarChar(255) + status EFolderStatus @default(LIVE) + deed Deeds @relation(fields: [deed_uid], references: [uid], onDelete: Cascade) + deed_uid String @unique @db.VarChar(255) + office Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade) + office_uid String @db.VarChar(255) + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + stakeholders Users[] @relation("OfficeFolderHasStakeholders") + customers Customers[] @relation("OfficeFolderHasCustomers") documents Documents[] documents_notary DocumentsNotary[] - folder_anchor OfficeFolderAnchors? @relation(fields: [folder_anchor_uid], references: [uid]) - folder_anchor_uid String? @unique @db.VarChar(255) - notes Notes[] + folder_anchor OfficeFolderAnchors? @relation(fields: [folder_anchor_uid], references: [uid]) + folder_anchor_uid String? @unique @db.VarChar(255) + notes Notes[] @@unique([folder_number, office_uid]) @@map("office_folders") } model OfficeFolderAnchors { - uid String @id @unique @default(uuid()) + uid String @id @unique @default(uuid()) - hash_sources String[] - root_hash String @db.VarChar(255) + hash_sources String[] + root_hash String @db.VarChar(255) - blockchain EBlockchainName @default(TEZOS) - status EAnchoringStatus @default(QUEUED) - anchor_nb_try Int @default(0) + blockchain EBlockchainName @default(TEZOS) + status EAnchoringStatus @default(QUEUED) + anchor_nb_try Int @default(0) - anchored_at DateTime? - tx_id String? @db.VarChar(255) - tx_link String? @db.VarChar(255) - tx_hash String? @db.VarChar(255) + anchored_at DateTime? + tx_id String? @db.VarChar(255) + tx_link String? @db.VarChar(255) + tx_hash String? @db.VarChar(255) - folder OfficeFolders? + folder OfficeFolders? - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt @@map("office_folder_anchors") } model Documents { - uid String @id @unique @default(uuid()) - document_status EDocumentStatus @default(ASKED) - document_type DocumentTypes @relation(fields: [document_type_uid], references: [uid]) - document_type_uid String @db.VarChar(255) - folder OfficeFolders @relation(fields: [folder_uid], references: [uid]) - folder_uid String @db.VarChar(255) - depositor Customers @relation(fields: [depositor_uid], references: [uid], onDelete: Cascade) - depositor_uid String @db.VarChar(255) - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt - files Files[] - document_history DocumentHistory[] - reminders DocumentsReminder[] + uid String @id @unique @default(uuid()) + document_status EDocumentStatus @default(ASKED) + document_type DocumentTypes @relation(fields: [document_type_uid], references: [uid]) + document_type_uid String @db.VarChar(255) + folder OfficeFolders @relation(fields: [folder_uid], references: [uid]) + folder_uid String @db.VarChar(255) + depositor Customers @relation(fields: [depositor_uid], references: [uid], onDelete: Cascade) + depositor_uid String @db.VarChar(255) + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + files Files[] + document_history DocumentHistory[] + reminders DocumentsReminder[] @@map("documents") } model DocumentsNotary { - uid String @id @unique @default(uuid()) - folder OfficeFolders @relation(fields: [folder_uid], references: [uid]) - folder_uid String @db.VarChar(255) - depositor Users @relation(fields: [depositor_uid], references: [uid], onDelete: Cascade) - depositor_uid String @db.VarChar(255) - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt - files FilesNotary[] - customer Customers @relation(fields: [customer_uid], references: [uid], onDelete: Cascade) - customer_uid String @db.VarChar(255) - document String @db.VarChar(255) @default("") + uid String @id @unique @default(uuid()) + folder OfficeFolders @relation(fields: [folder_uid], references: [uid]) + folder_uid String @db.VarChar(255) + depositor Users @relation(fields: [depositor_uid], references: [uid], onDelete: Cascade) + depositor_uid String @db.VarChar(255) + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + files FilesNotary[] + customer Customers @relation(fields: [customer_uid], references: [uid], onDelete: Cascade) + customer_uid String @db.VarChar(255) + document String @default("") @db.VarChar(255) @@map("documents_notary") } model DocumentsReminder { - uid String @id @unique @default(uuid()) - document Documents @relation(fields: [document_uid], references: [uid], onDelete: Cascade) - document_uid String @db.VarChar(255) - reminder_date DateTime - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt + uid String @id @unique @default(uuid()) + document Documents @relation(fields: [document_uid], references: [uid], onDelete: Cascade) + document_uid String @db.VarChar(255) + reminder_date DateTime + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt @@map("documents_reminder") } @@ -280,16 +283,17 @@ model Files { } model FilesNotary { - uid String @id @unique @default(uuid()) - document_notary DocumentsNotary @relation(fields: [document_uid], references: [uid], onDelete: Cascade) - document_uid String @db.VarChar(255) - file_path String @unique @db.VarChar(255) - file_name String @db.VarChar(255) - mimetype String @db.VarChar(255) - hash String @db.VarChar(255) - size Int - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt + uid String @id @unique @default(uuid()) + document_notary DocumentsNotary @relation(fields: [document_uid], references: [uid], onDelete: Cascade) + document_uid String @db.VarChar(255) + file_path String @unique @db.VarChar(255) + file_name String @db.VarChar(255) + mimetype String @db.VarChar(255) + hash String @db.VarChar(255) + size Int + key String? @db.VarChar(255) + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt @@map("files_notary") } @@ -373,8 +377,9 @@ model Rules { updated_at DateTime? @updatedAt role Roles[] @relation("RolesHasRules") office_roles OfficeRoles[] @relation("OfficeRolesHasRules") - namespace String @db.VarChar(255) @default("notary") + namespace String @default("notary") @db.VarChar(255) groups RulesGroups[] @relation("RulesGroupsHasRules") + @@map("rules") } @@ -384,8 +389,9 @@ model RulesGroups { created_at DateTime? @default(now()) updated_at DateTime? @updatedAt rules Rules[] @relation("RulesGroupsHasRules") + @@map("rules_groups") -} +} model Emails { uid String @id @unique @default(uuid()) @@ -426,54 +432,58 @@ model Votes { } model TotpCodes { - uid String @id @unique @default(uuid()) - customer Customers @relation(fields: [customer_uid], references: [uid], onDelete: Cascade) - customer_uid String @db.VarChar(255) - code String @db.VarChar(255) - reason TotpCodesReasons @default(LOGIN) - resent Boolean @default(false) - expire_at DateTime? @default(now()) - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt + uid String @id @unique @default(uuid()) + customer Customers @relation(fields: [customer_uid], references: [uid], onDelete: Cascade) + customer_uid String @db.VarChar(255) + code String @db.VarChar(255) + reason TotpCodesReasons @default(LOGIN) + resent Boolean @default(false) + expire_at DateTime? @default(now()) + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + @@map("totp_codes") } model Subscriptions { - uid String @id @unique @default(uuid()) - type ESubscriptionType - status ESubscriptionStatus @default(ACTIVE) - stripe_subscription_id String @db.VarChar(255) - start_date DateTime @default(now()) - end_date DateTime - nb_seats Int - office Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade) - office_uid String @db.VarChar(255) - seats Seats[] - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt + uid String @id @unique @default(uuid()) + type ESubscriptionType + status ESubscriptionStatus @default(ACTIVE) + stripe_subscription_id String @db.VarChar(255) + start_date DateTime @default(now()) + end_date DateTime + nb_seats Int + office Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade) + office_uid String @db.VarChar(255) + seats Seats[] + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + @@map("subscriptions") } model Seats { - uid String @id @unique @default(uuid()) - subscription Subscriptions @relation(fields: [subscription_uid], references: [uid], onDelete: Cascade) - subscription_uid String @db.VarChar(255) - user Users @relation(fields: [user_uid], references: [uid], onDelete: Cascade) - user_uid String @db.VarChar(255) - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt + uid String @id @unique @default(uuid()) + subscription Subscriptions @relation(fields: [subscription_uid], references: [uid], onDelete: Cascade) + subscription_uid String @db.VarChar(255) + user Users @relation(fields: [user_uid], references: [uid], onDelete: Cascade) + user_uid String @db.VarChar(255) + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + @@map("seats") } model Notes { - uid String @id @unique @default(uuid()) - content String @db.VarChar(1000) - created_at DateTime? @default(now()) - updated_at DateTime? @updatedAt - customer Customers @relation(fields: [customer_uid], references: [uid], onDelete: Cascade) - customer_uid String @db.VarChar(255) + uid String @id @unique @default(uuid()) + content String @db.VarChar(1000) + created_at DateTime? @default(now()) + updated_at DateTime? @updatedAt + customer Customers @relation(fields: [customer_uid], references: [uid], onDelete: Cascade) + customer_uid String @db.VarChar(255) folder OfficeFolders @relation(fields: [folder_uid], references: [uid], onDelete: Cascade) - folder_uid String @db.VarChar(255) + folder_uid String @db.VarChar(255) + @@map("notes") } diff --git a/src/common/repositories/FilesNotaryRepository.ts b/src/common/repositories/FilesNotaryRepository.ts index 6f05e843..3319b372 100644 --- a/src/common/repositories/FilesNotaryRepository.ts +++ b/src/common/repositories/FilesNotaryRepository.ts @@ -40,6 +40,7 @@ export default class FilesNotaryRepository extends BaseRepository { mimetype: file.mimetype, hash: file.hash, size: file.size, + key: key, }, }; return this.model.create({ ...createArgs, include: { document_notary: true } }); diff --git a/src/services/common/FilesNotaryService/FilesNotaryService.ts b/src/services/common/FilesNotaryService/FilesNotaryService.ts index 7089a374..cd508b4a 100644 --- a/src/services/common/FilesNotaryService/FilesNotaryService.ts +++ b/src/services/common/FilesNotaryService/FilesNotaryService.ts @@ -61,10 +61,10 @@ export default class FilesNotaryService extends BaseService { */ public async download(uid: string) { const file = await this.filesNotaryRepository.findOneByUid(uid); - if (!file?.uid) return null; + if (!file?.key) return null; const fileResult = await fetch(file.file_path.concat("?pinataGatewayToken=").concat(this.variables.PINATA_GATEWAY_TOKEN)); const fileArrayBuffer = await fileResult.arrayBuffer(); - return { file: file, buffer: await this.cryptoService.decrypt(Buffer.from(fileArrayBuffer), file.uid) }; + return { file: file, buffer: await this.cryptoService.decrypt(Buffer.from(fileArrayBuffer), file.key) }; } /** From 97ccd8deeea34b6f6f5bad45a59c158942d36e29 Mon Sep 17 00:00:00 2001 From: Max S Date: Fri, 13 Sep 2024 10:47:41 +0200 Subject: [PATCH 15/16] :sparkles: add documents_notary files to anchor --- .../notary/OfficeFolderAnchorsController.ts | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/app/api/notary/OfficeFolderAnchorsController.ts b/src/app/api/notary/OfficeFolderAnchorsController.ts index 96bba906..da4cc236 100644 --- a/src/app/api/notary/OfficeFolderAnchorsController.ts +++ b/src/app/api/notary/OfficeFolderAnchorsController.ts @@ -90,20 +90,20 @@ export default class OfficeFoldersController extends ApiController { const sortedHashes = [...folderHashes].sort(); const anchoringProof = await this.secureService.download(sortedHashes, officeFolder.office!.name); - const addFileToZip = (zip: Zip) => (uid: string): Promise => - (async () => { - const data = await this.filesService.download(uid); - if (!data?.buffer) return; - zip.addFile(`Documents du client/${uid}-${data.file.file_name}`, data.buffer); - })() + const addFileToZip = + (zip: Zip) => + (uid: string): Promise => + (async () => { + const data = await this.filesService.download(uid); + if (!data?.buffer) return; + zip.addFile(`Documents du client/${uid}-${data.file.file_name}`, data.buffer); + })(); const uids: string[] = folderFilesUid.filter((uid): uid is string => uid !== undefined); const zip = new Zip(); zip.addFile("Certificat de dépôt du dossier.pdf", anchoringProof); - await Promise.allSettled( - uids.map(addFileToZip(zip)) - ) + await Promise.allSettled(uids.map(addFileToZip(zip))); response.setHeader("Content-Type", "application/zip"); this.httpSuccess(response, zip.toBuffer()); @@ -132,6 +132,11 @@ export default class OfficeFoldersController extends ApiController { files: true, }, }, + documents_notary: { + include: { + files: true, + }, + }, folder_anchor: true, }; @@ -276,7 +281,7 @@ export default class OfficeFoldersController extends ApiController { let query: Prisma.OfficeFolderAnchorsFindManyArgs = {}; if (req.query["q"]) { query = JSON.parse(req.query["q"] as string); - if(query.where?.uid) { + if (query.where?.uid) { this.httpBadRequest(response, "You can't filter by uid"); return; } From 510fe26f1e9fc0c8f32e28c2772097bf15c7e3ac Mon Sep 17 00:00:00 2001 From: Max S Date: Fri, 13 Sep 2024 13:08:08 +0200 Subject: [PATCH 16/16] :hammer: fix wrong concatenation of query where in get documents --- src/app/api/notary/DocumentsController.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/app/api/notary/DocumentsController.ts b/src/app/api/notary/DocumentsController.ts index 7e866c8e..f4af409c 100644 --- a/src/app/api/notary/DocumentsController.ts +++ b/src/app/api/notary/DocumentsController.ts @@ -35,15 +35,15 @@ export default class DocumentsController extends ApiController { let query: Prisma.DocumentsFindManyArgs = {}; if (req.query["q"]) { query = JSON.parse(req.query["q"] as string); - if(query.where?.uid) { + if (query.where?.uid) { this.httpBadRequest(response, "You can't filter by uid"); return; } } const officeId: string = req.body.user.office_Id; const officeWhereInput: Prisma.OfficesWhereInput = { uid: officeId }; - if (!query.where) query.where = { document_type: { office: officeWhereInput } }; - query.where.document_type!.office = officeWhereInput; + + query.where = { ...query.where, document_type: { office: officeWhereInput } }; //call service to get prisma entity const documentEntities = await this.documentsService.get(query); @@ -124,7 +124,7 @@ export default class DocumentsController extends ApiController { return; } - if(documentFound.document_status === EDocumentStatus.REFUSED || documentFound.document_status === EDocumentStatus.VALIDATED) { + if (documentFound.document_status === EDocumentStatus.REFUSED || documentFound.document_status === EDocumentStatus.VALIDATED) { this.httpForbidden(response, "You are not allowed to update a VALIDATED or REFUSED document"); return; } @@ -186,11 +186,9 @@ export default class DocumentsController extends ApiController { //call service to get prisma entity const documentEntityUpdated: Documents = await this.documentsService.refuse(uid, documentEntity, req.body.refused_reason); - //create email for asked document await this.emailBuilder.sendDocumentEmails(documentEntityUpdated); - //Hydrate ressource with prisma entity const document = Document.hydrate(documentEntityUpdated, { strategy: "excludeAll" });