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/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/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/common/AnchoringProofService/AnchoringProofService.ts b/src/services/common/AnchoringProofService/AnchoringProofService.ts index 4487f75c..7077ce45 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,132 +16,32 @@ 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 */ 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({ + 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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +`; +} 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); + } }