From 19db48373951f3161d3e020b9ef48b808259e8fa Mon Sep 17 00:00:00 2001 From: Max S Date: Fri, 20 Sep 2024 13:12:40 +0200 Subject: [PATCH 1/3] =?UTF-8?q?:sparkles=20add=20status=20t=C3=A9l=C3=A9ch?= =?UTF-8?q?arg=C3=A9=20in=20documents=20notary=20table?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- .../api/customer/DocumentsNotaryController.ts | 8 +-- src/app/api/customer/FilesNotaryController.ts | 54 +++++++++++++++++++ src/app/index.ts | 4 ++ .../migration.sql | 5 ++ src/common/databases/schema.prisma | 28 ++++++---- .../repositories/DocumentsNotaryRepository.ts | 13 ++++- .../DocumentsNotaryService.ts | 8 ++- 8 files changed, 103 insertions(+), 19 deletions(-) create mode 100644 src/app/api/customer/FilesNotaryController.ts create mode 100644 src/common/databases/migrations/20240920084958_add_document_notary_status/migration.sql diff --git a/package.json b/package.json index 0483be4f..8bfdfe86 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.160", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.167", "module-alias": "^2.2.2", "monocle-ts": "^2.3.13", "multer": "^1.4.5-lts.1", diff --git a/src/app/api/customer/DocumentsNotaryController.ts b/src/app/api/customer/DocumentsNotaryController.ts index 4fd90e4e..e1626b2c 100644 --- a/src/app/api/customer/DocumentsNotaryController.ts +++ b/src/app/api/customer/DocumentsNotaryController.ts @@ -4,16 +4,16 @@ 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 DocumentNotary from "le-coffre-resources/dist/Notary/DocumentNotary"; + // import NotificationBuilder from "@Common/notifications/NotificationBuilder"; @Controller() @Service() export default class DocumentsNotaryController extends ApiController { - constructor( - private documentsNotaryService: DocumentsNotaryService, - ) { + constructor(private documentsNotaryService: DocumentsNotaryService) { super(); } diff --git a/src/app/api/customer/FilesNotaryController.ts b/src/app/api/customer/FilesNotaryController.ts new file mode 100644 index 00000000..52185ea4 --- /dev/null +++ b/src/app/api/customer/FilesNotaryController.ts @@ -0,0 +1,54 @@ +import authHandler from "@App/middlewares/AuthHandler"; +import ApiController from "@Common/system/controller-pattern/ApiController"; +import { Controller, Get } from "@ControllerPattern/index"; +import { EDocumentNotaryStatus } from "@prisma/client"; +import FilesNotaryService from "@Services/common/FilesNotaryService/FilesNotaryService"; +import DocumentsNotaryService from "@Services/notary/DocumentsNotaryService/DocumentsNotaryService"; +import { Request, Response } from "express"; +import { Service } from "typedi"; + +@Controller() +@Service() +export default class FilesNotaryController extends ApiController { + constructor(private filesNotaryService: FilesNotaryService, private documentsNotaryService: DocumentsNotaryService) { + super(); + } + + /** + * @description Get a specific File by uid + */ + @Get("/api/v1/customer/files-notary/:filesNotaryUid/documents-notary/:documentsNotaryUid/download", [authHandler]) + protected async download(req: Request, response: Response) { + const filesNotaryUid = req.params["filesNotaryUid"]; + const documentsNotaryUid = req.params["documentsNotaryUid"]; + + if (!filesNotaryUid) { + this.httpBadRequest(response, "filesNotaryUid not found"); + return; + } + + if (!documentsNotaryUid) { + this.httpBadRequest(response, "documentsNotaryUid not found"); + return; + } + + try { + const fileInfo = await this.filesNotaryService.download(filesNotaryUid); + + 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)}`); + + await this.documentsNotaryService.changeStatus(documentsNotaryUid, EDocumentNotaryStatus.DOWNLOADED); + + this.httpSuccess(response, fileInfo.buffer); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } +} diff --git a/src/app/index.ts b/src/app/index.ts index f2bcaf1c..da077bf2 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -57,7 +57,9 @@ 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 DocumentsNotaryControllerCustomer from "./api/customer/DocumentsNotaryController"; import FilesNotaryController from "./api/notary/FilesNotaryController"; +import FilesNotaryControllerCustomer from "./api/customer/FilesNotaryController"; /** * @description This allow to declare all controllers used in the application @@ -124,5 +126,7 @@ export default { Container.get(DocumentsReminderController); Container.get(DocumentsNotaryController); Container.get(FilesNotaryController); + Container.get(DocumentsNotaryControllerCustomer); + Container.get(FilesNotaryControllerCustomer); }, }; diff --git a/src/common/databases/migrations/20240920084958_add_document_notary_status/migration.sql b/src/common/databases/migrations/20240920084958_add_document_notary_status/migration.sql new file mode 100644 index 00000000..9a79cec8 --- /dev/null +++ b/src/common/databases/migrations/20240920084958_add_document_notary_status/migration.sql @@ -0,0 +1,5 @@ +-- CreateEnum +CREATE TYPE "EDocumentNotaryStatus" AS ENUM ('SENT', 'DOWNLOADED'); + +-- AlterTable +ALTER TABLE "documents_notary" ADD COLUMN "document_status" "EDocumentNotaryStatus" NOT NULL DEFAULT 'SENT'; diff --git a/src/common/databases/schema.prisma b/src/common/databases/schema.prisma index 8e58f283..2058c232 100644 --- a/src/common/databases/schema.prisma +++ b/src/common/databases/schema.prisma @@ -227,17 +227,18 @@ model 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 @default("") @db.VarChar(255) + 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) + document_status EDocumentNotaryStatus @default(SENT) @@map("documents_notary") } @@ -558,3 +559,8 @@ enum EAnchoringStatus { VERIFYING_ON_CHAIN ABANDONED } + +enum EDocumentNotaryStatus { + SENT + DOWNLOADED +} diff --git a/src/common/repositories/DocumentsNotaryRepository.ts b/src/common/repositories/DocumentsNotaryRepository.ts index 4d785adb..b2021b83 100644 --- a/src/common/repositories/DocumentsNotaryRepository.ts +++ b/src/common/repositories/DocumentsNotaryRepository.ts @@ -1,7 +1,7 @@ import Database from "@Common/databases/database"; import BaseRepository from "@Repositories/BaseRepository"; import { Service } from "typedi"; -import { DocumentsNotary, Prisma } from "@prisma/client"; +import { DocumentsNotary, EDocumentNotaryStatus, Prisma } from "@prisma/client"; import { DocumentNotary } from "le-coffre-resources/dist/Notary"; @Service() @@ -99,4 +99,15 @@ export default class DocumentsNotaryRepository extends BaseRepository { include: { files: true }, }); } + + public async changeStatus(uid: string, status: EDocumentNotaryStatus) { + return this.model.update({ + where: { + uid: uid, + }, + data: { + document_status: status, + }, + }); + } } diff --git a/src/services/notary/DocumentsNotaryService/DocumentsNotaryService.ts b/src/services/notary/DocumentsNotaryService/DocumentsNotaryService.ts index 865752ca..dc0432db 100644 --- a/src/services/notary/DocumentsNotaryService/DocumentsNotaryService.ts +++ b/src/services/notary/DocumentsNotaryService/DocumentsNotaryService.ts @@ -1,11 +1,11 @@ -import { DocumentsNotary, Prisma } from "@prisma/client"; +import { DocumentsNotary, EDocumentNotaryStatus, 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 { +export default class DocumentsNotaryService extends BaseService { constructor(private documentsNotaryRepository: DocumentsNotaryRepository) { super(); } @@ -58,4 +58,8 @@ export default class DocumentsService extends BaseService { public async getByUidWithOffice(uid: string) { return this.documentsNotaryRepository.findOneByUidWithOffice(uid); } + + public changeStatus(uid: string, status: EDocumentNotaryStatus) { + return this.documentsNotaryRepository.changeStatus(uid, status); + } } From 51721456e8bbf45cb718de52e0d1d7d3112586ec Mon Sep 17 00:00:00 2001 From: Max S Date: Mon, 23 Sep 2024 11:43:40 +0200 Subject: [PATCH 2/3] refacto and update svg anchoring proof --- .../AnchoringProofService.ts | 119 ++---------------- .../AnchoringProofService/proofTemplate.ts | 111 ++++++++++++++++ 2 files changed, 121 insertions(+), 109 deletions(-) create mode 100644 src/services/common/AnchoringProofService/proofTemplate.ts diff --git a/src/services/common/AnchoringProofService/AnchoringProofService.ts b/src/services/common/AnchoringProofService/AnchoringProofService.ts index 4487f75c..38f9e1d6 100644 --- a/src/services/common/AnchoringProofService/AnchoringProofService.ts +++ b/src/services/common/AnchoringProofService/AnchoringProofService.ts @@ -1,6 +1,7 @@ import BaseService from "@Services/BaseService"; import { Service } from "typedi"; import puppeteer from "puppeteer"; +import proofTemplate from "./proofTemplate"; export interface AnchoringProofData { rootHash: string; @@ -15,107 +16,6 @@ export default class AnchoringProofService extends BaseService { super(); } - // private static svgTemplateDocument: string = ` - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // [[ANCHORING_TIME]] - // Certificat de dépôt international - // Hash : - // [[ROOT_HASH]] - // - // Déposant(s) - // Auteur : - // Not.IT - // Partenaire technique : - // Smart-Chain - // - // Explorateur blockchain - // [[TX_LINK]] - // - // - // `; - - private static svgTemplateDocumentBis: string = ` - - - - - - - - - - - - - - - - - -Certificat de dépôt international -[[ANCHORING_TIME]] -Nom de l'office -[[OFFICE_NAME]] - -Déposant(s) -LEcoffre.io - -Hash -[[ROOT_HASH]] - -Explorateur blockchain -[[TX_LINK]] - - -À quoi ça sert ? -Un certificat d'ancrage sur la blockchain permet d'établir de manière irréfutable l'enregistrement de données spécifiques à une date et une heure déterminées. Il constitue une preuve immuable et juridiquement opposable de l'existence et de l'intégrité des informations. En cas de litige, ce certificat peut démontrer de manière incontestable que les données n'ont pas été altérées depuis leur enregistrement. - -Qu'est-ce qu'un explorateur blockchain ? -Un explorateur blockchain est un outil numérique permettant d'accéder et de consulter les transactions et les données enregistrées sur la blockchain. Il assure une transparence totale, permettant à toute partie prenante de vérifier les opérations et les enregistrements effectués sur la chaîne de blocs. - -Qu'est-ce qu'un hash ? -Un hash est une empreinte cryptographique unique générée par un algorithme à partir de données spécifiques. Il agit comme une signature numérique permettant de vérifier l'intégrité des données : toute modification, même infime, des données d'origine entraîne la production d'un hash distinct, garantissant ainsi l'authenticité et l'intégrité des informations. - - - - - - `; - /** * @description : Generate a PDF file from a SVG template from anchoring proof data */ @@ -127,20 +27,21 @@ export default class AnchoringProofService extends BaseService { }); const page = await browser.newPage(); + const proofTemplateSvg = proofTemplate({ + rootHash: data.rootHash, + anchoringTime: data.anchoringTime, + officeName: data.office_name, + txLink: data.txLink, + }); + var htmlContent = ` - ${AnchoringProofService.svgTemplateDocumentBis} + ${proofTemplateSvg} `; - htmlContent = htmlContent - .replace("[[ROOT_HASH]]", data.rootHash) - .replace("[[ANCHORING_TIME]]", data.anchoringTime) - .replace("[[OFFICE_NAME]]", data.office_name) - .replace(/\[\[TX_LINK\]\]/g, data.txLink); - await page.setContent(htmlContent); await page.addStyleTag({ content: ` @@ -176,4 +77,4 @@ export default class AnchoringProofService extends BaseService { return buffer; } -} \ No newline at end of file +} diff --git a/src/services/common/AnchoringProofService/proofTemplate.ts b/src/services/common/AnchoringProofService/proofTemplate.ts new file mode 100644 index 00000000..3a344b22 --- /dev/null +++ b/src/services/common/AnchoringProofService/proofTemplate.ts @@ -0,0 +1,111 @@ +export default function proofTemplate({ + rootHash, + anchoringTime, + officeName, + txLink, +}: { + rootHash: string; + anchoringTime: string; + officeName: string; + txLink: string; +}) { + return ` + + + + + + + + + + + + + + + + + +Certificat de dépôt international +${anchoringTime} +Nom de l'office +${officeName} + +Déposant(s) +LEcoffre.io + +Hash +${rootHash} + +Explorateur blockchain +${txLink} + + + + + +À quoi ça sert ? +Un certificat d'ancrage sur la blockchain permet d'établir de manière irréfutable l'enregistrement de données spécifiques à une date et une heure déterminées. Il constitue une preuve immuable et juridiquement opposable de l'existence et de l'intégrité des informations. En cas de litige, ce certificat peut démontrer de manière incontestable que les données n'ont pas été altérées depuis leur enregistrement. + + + + +Qu'est-ce qu'un explorateur blockchain ? +Un explorateur blockchain est un outil numérique permettant d'accéder et de consulter les transactions et les données enregistrées sur la blockchain. Il assure une transparence totale, permettant à toute partie prenante de vérifier les opérations et les enregistrements effectués sur la chaîne de blocs. + + + + +Qu'est-ce qu'un hash ? +Un hash est une empreinte cryptographique unique générée par un algorithme à partir de données spécifiques. Il agit comme une signature numérique permettant de vérifier l'intégrité des données : toute modification, même infime, des données d'origine entraîne la production d'un hash distinct, garantissant ainsi l'authenticité et l'intégrité des informations. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; +} From eafe7b4d0156770b1fc4391303518dfdf3055acd Mon Sep 17 00:00:00 2001 From: Max S Date: Mon, 23 Sep 2024 14:45:14 +0200 Subject: [PATCH 3/3] fix puppetter lanch for downloading anchor proof --- src/app/api/notary/OfficeFolderAnchorsController.ts | 1 + .../common/AnchoringProofService/AnchoringProofService.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/app/api/notary/OfficeFolderAnchorsController.ts b/src/app/api/notary/OfficeFolderAnchorsController.ts index da4cc236..bf8141ed 100644 --- a/src/app/api/notary/OfficeFolderAnchorsController.ts +++ b/src/app/api/notary/OfficeFolderAnchorsController.ts @@ -90,6 +90,7 @@ 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 => diff --git a/src/services/common/AnchoringProofService/AnchoringProofService.ts b/src/services/common/AnchoringProofService/AnchoringProofService.ts index 38f9e1d6..7077ce45 100644 --- a/src/services/common/AnchoringProofService/AnchoringProofService.ts +++ b/src/services/common/AnchoringProofService/AnchoringProofService.ts @@ -22,9 +22,9 @@ export default class AnchoringProofService extends BaseService { public async generate(data: AnchoringProofData): Promise { const browser = await puppeteer.launch({ headless: "new", - executablePath: `/usr/bin/chromium`, args: ["--no-sandbox", "--disable-setuid-sandbox"], - }); + }); + const page = await browser.newPage(); const proofTemplateSvg = proofTemplate({