From 8ca13569122b5468a4e783111fddd60258185f09 Mon Sep 17 00:00:00 2001 From: Vincent Alamelle Date: Thu, 4 May 2023 17:22:30 +0200 Subject: [PATCH 01/16] new dev script --- src/app/api/super-admin/OfficeFoldersController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/api/super-admin/OfficeFoldersController.ts b/src/app/api/super-admin/OfficeFoldersController.ts index dc273d35..38b46e47 100644 --- a/src/app/api/super-admin/OfficeFoldersController.ts +++ b/src/app/api/super-admin/OfficeFoldersController.ts @@ -75,7 +75,7 @@ export default class OfficeFoldersController extends ApiController { const officeFolderEntity = OfficeFolder.hydrate(req.body); //validate folder - await validateOrReject(officeFolderEntity, { groups: ["updateFolder"] }); + await validateOrReject(officeFolderEntity, { groups: ["updateFolder"], forbidUnknownValues: false }); //call service to get prisma entity const prismaEntityUpdated = await this.officeFoldersService.update(uid, officeFolderEntity); From 855334dbd8655dfb38b884aadaac4ff16642b438 Mon Sep 17 00:00:00 2001 From: Vincent Alamelle Date: Thu, 4 May 2023 17:48:56 +0200 Subject: [PATCH 02/16] Fixed create Document --- package-lock.json | 4 ++-- package.json | 2 +- src/app/api/super-admin/DocumentsController.ts | 2 ++ src/common/repositories/FilesRepository.ts | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 67d17234..c72c56fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "cors": "^2.8.5", "express": "^4.18.2", "jsonwebtoken": "^9.0.0", - "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.33", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.36", "module-alias": "^2.2.2", "next": "^13.1.5", "node-cache": "^5.1.2", @@ -3615,7 +3615,7 @@ } }, "node_modules/le-coffre-resources": { - "resolved": "git+ssh://git@github.com/smart-chain-fr/leCoffre-resources.git#cabb4c6c05258d5725cb5747f9cb8335915edf10", + "resolved": "git+ssh://git@github.com/smart-chain-fr/leCoffre-resources.git#90ed41d1239cb915defec195cf0e1363ea157d36", "license": "MIT", "dependencies": { "class-transformer": "^0.5.1", diff --git a/package.json b/package.json index 5efcdb77..8a3c77e1 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "cors": "^2.8.5", "express": "^4.18.2", "jsonwebtoken": "^9.0.0", - "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.35", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.37", "module-alias": "^2.2.2", "next": "^13.1.5", "node-cache": "^5.1.2", diff --git a/src/app/api/super-admin/DocumentsController.ts b/src/app/api/super-admin/DocumentsController.ts index cfe412ec..6dc9904d 100644 --- a/src/app/api/super-admin/DocumentsController.ts +++ b/src/app/api/super-admin/DocumentsController.ts @@ -47,6 +47,8 @@ export default class DocumentsController extends ApiController { try { //init Document resource with request body values const documentEntity = Document.hydrate(req.body); + console.log(documentEntity); + //validate document await validateOrReject(documentEntity, { groups: ["createDocument"], forbidUnknownValues: false }); diff --git a/src/common/repositories/FilesRepository.ts b/src/common/repositories/FilesRepository.ts index ebc0fb77..7c7d99d8 100644 --- a/src/common/repositories/FilesRepository.ts +++ b/src/common/repositories/FilesRepository.ts @@ -32,7 +32,7 @@ export default class FilesRepository extends BaseRepository { data: { document: { connect: { - uid: file.document.uid, + uid: file.document!.uid, }, }, file_path: file.file_path, From 266501fd79fced2595e81df70b46bc525bd98f65 Mon Sep 17 00:00:00 2001 From: Vincent Alamelle Date: Thu, 4 May 2023 17:57:27 +0200 Subject: [PATCH 03/16] Feature update document --- src/app/api/super-admin/DocumentsController.ts | 4 ++-- src/common/repositories/DocumentsRepository.ts | 7 +------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/app/api/super-admin/DocumentsController.ts b/src/app/api/super-admin/DocumentsController.ts index 6dc9904d..769ef36b 100644 --- a/src/app/api/super-admin/DocumentsController.ts +++ b/src/app/api/super-admin/DocumentsController.ts @@ -78,13 +78,13 @@ export default class DocumentsController extends ApiController { const uid = req.params["uid"]; if (!uid) { throw new Error("No uid provided"); - } + } //init Document resource with request body values const documentEntity = Document.hydrate(req.body); //validate document - await validateOrReject(documentEntity, { groups: ["createDocument"] }); + await validateOrReject(documentEntity, { groups: ["updateDocument"] }); //call service to get prisma entity const prismaEntityUpdated: Documents = await this.documentsService.update(uid, documentEntity); diff --git a/src/common/repositories/DocumentsRepository.ts b/src/common/repositories/DocumentsRepository.ts index f48a0030..1e1aa340 100644 --- a/src/common/repositories/DocumentsRepository.ts +++ b/src/common/repositories/DocumentsRepository.ts @@ -90,12 +90,7 @@ export default class DocumentsRepository extends BaseRepository { document_status: EDocumentStatus[document.document_status as keyof typeof EDocumentStatus], refused_reason: refusedReason, }, - }, - depositor: { - connect: { - uid: document.depositor!.uid, - }, - }, + } }, }); } From fe879584f29e43b77f54dd17e621dc11c3928698 Mon Sep 17 00:00:00 2001 From: OxSaitama Date: Tue, 2 May 2023 15:12:12 +0200 Subject: [PATCH 04/16] add IPFS service --- package.json | 1 + src/app/api/super-admin/FilesController.ts | 150 ++++++++++++++++++ src/app/index.ts | 4 +- src/common/config/variables/Variables.ts | 16 ++ .../20230505075245_v1/migration.sql | 8 + src/common/databases/schema.prisma | 1 + src/common/databases/seeders/seeder.ts | 2 + src/common/helpers/ObjectHydrate.ts | 10 ++ src/common/repositories/FilesRepository.ts | 1 + .../CryptoService/CryptoService.ts | 74 +++++++++ .../FilesService/FilesService.ts | 21 ++- .../IpfsService/IpfsService.ts | 30 ++++ 12 files changed, 314 insertions(+), 4 deletions(-) create mode 100644 src/app/api/super-admin/FilesController.ts create mode 100644 src/common/databases/migrations/20230505075245_v1/migration.sql create mode 100644 src/services/private-services/CryptoService/CryptoService.ts create mode 100644 src/services/private-services/IpfsService/IpfsService.ts diff --git a/package.json b/package.json index 8a3c77e1..22753401 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ }, "homepage": "https://github.com/smart-chain-fr/leCoffre-back#readme", "dependencies": { + "@pinata/sdk": "^2.1.0", "@prisma/client": "^4.11.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", diff --git a/src/app/api/super-admin/FilesController.ts b/src/app/api/super-admin/FilesController.ts new file mode 100644 index 00000000..1fdd1696 --- /dev/null +++ b/src/app/api/super-admin/FilesController.ts @@ -0,0 +1,150 @@ +import { Response, Request } from "express"; +import { Controller, Delete, Get, Post, Put } from "@ControllerPattern/index"; +import ApiController from "@Common/system/controller-pattern/ApiController"; +import { Service } from "typedi"; +import FilesService from "@Services/private-services/FilesService/FilesService"; +import { Files } from "@prisma/client"; +import ObjectHydrate from "@Common/helpers/ObjectHydrate"; +import { File } from "le-coffre-resources/dist/SuperAdmin"; +import { validateOrReject } from "class-validator"; + +@Controller() +@Service() +export default class FilesController extends ApiController { + constructor(private filesService: FilesService) { + super(); + } + + /** + * @description Get all Files + * @returns File[] list of Files + */ + @Get("/api/v1/super-admin/files") + protected async get(req: Request, response: Response) { + try { + //get query + const query = JSON.parse(req.query["q"] as string); + + //call service to get prisma entity + const prismaEntity = await this.filesService.get(query); + + //Hydrate ressource with prisma entity + const files = File.map(File, prismaEntity, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, files); + } catch (error) { + this.httpBadRequest(response, error); + return; + } + } + + /** + * @description Create a new File + * @returns File created + */ + @Post("/api/v1/super-admin/files") + protected async post(req: Request, response: Response) { + try { + //init File resource with request body values + const fileEntity = File.hydrate(req.body); + + //validate File + await validateOrReject(fileEntity, { groups: ["createFile"] }); + + //call service to get prisma entity + const prismaEntityCreated = await this.filesService.create(fileEntity); + + //Hydrate ressource with prisma entity + const fileEntityCreated = File.hydrate(prismaEntityCreated, { + strategy: "excludeAll", + }); + + //success + this.httpSuccess(response, fileEntityCreated); + } catch (error) { + this.httpBadRequest(response, error); + return; + } + } + + /** + * @description Update a specific file + */ + @Put("/api/v1/super-admin/files/:uid") + protected async update(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + throw new Error("No uid provided"); + } + + //init File resource with request body values + const fileEntity = File.hydrate(req.body); + + //validate file + await validateOrReject(fileEntity, { groups: ["create"] }); + + //call service to get prisma entity + const prismaEntityUpdated: Files = await this.filesService.update(uid, fileEntity); + + //Hydrate ressource with prisma entity + const file = File.hydrate(prismaEntityUpdated, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, file); + } catch (error) { + this.httpBadRequest(response, error); + return; + } + } + + /** + * @description Delete a specific File + */ + @Delete("/api/v1/super-admin/files/:uid") + protected async delete(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + throw new Error("No uid provided"); + } + + //call service to get prisma entity + const fileEntity: Files = await this.filesService.delete(uid); + + //Hydrate ressource with prisma entity + const file = ObjectHydrate.hydrate(new File(), fileEntity, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, file); + } catch (error) { + this.httpBadRequest(response, error); + return; + } + } + + /** + * @description Get a specific File by uid + */ + @Get("/api/v1/super-admin/Files/:uid") + protected async getOneByUid(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + throw new Error("No uid provided"); + } + + const fileEntity = await this.filesService.getByUid(uid); + + //Hydrate ressource with prisma entity + const file = ObjectHydrate.hydrate(new File(), fileEntity, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, file); + } catch (error) { + this.httpBadRequest(response, error); + return; + } + } +} diff --git a/src/app/index.ts b/src/app/index.ts index 01e173ed..e2b0a51f 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -9,8 +9,8 @@ import DeedTypesController from "./api/super-admin/DeedTypesController"; import DocumentsController from "./api/super-admin/DocumentsController"; import DocumentTypesController from "./api/super-admin/DocumentTypesController"; import IdNotUserInfoController from "./api/idnot-user/UserInfoController"; - import DocumentsControllerCustomer from "./api/customer/DocumentsController"; +import FilesController from "./api/super-admin/FilesController"; /** @@ -28,7 +28,7 @@ export default { Container.get(DocumentsController); Container.get(DocumentTypesController); Container.get(IdNotUserInfoController); - + Container.get(FilesController); Container.get(DocumentsControllerCustomer); }, }; diff --git a/src/common/config/variables/Variables.ts b/src/common/config/variables/Variables.ts index 8624a386..43646886 100644 --- a/src/common/config/variables/Variables.ts +++ b/src/common/config/variables/Variables.ts @@ -45,6 +45,18 @@ export class BackendVariables { @IsNotEmpty() public readonly IDNOT_REDIRECT_URL!: string; + @IsNotEmpty() + public readonly PINATA_API_KEY!: string; + + @IsNotEmpty() + public readonly PINATA_API_SECRET!: string; + + @IsNotEmpty() + public readonly PINATA_GATEWAY!: string; + + @IsNotEmpty() + public readonly KEY_DATA!: string; + public constructor() { dotenv.config(); this.DATABASE_PORT = process.env["DATABASE_PORT"]!; @@ -60,6 +72,10 @@ export class BackendVariables { this.IDNOT_CLIENT_ID = process.env["IDNOT_CLIENT_ID"]!; this.IDNOT_CLIENT_SECRET = process.env["IDNOT_CLIENT_SECRET"]!; this.IDNOT_REDIRECT_URL = process.env["IDNOT_REDIRECT_URL"]!; + this.PINATA_API_KEY = process.env["PINATA_API_KEY"]!; + this.PINATA_API_SECRET = process.env["PINATA_API_SECRET"]!; + this.PINATA_GATEWAY = process.env["PINATA_GATEWAY"]!; + this.KEY_DATA = process.env["KEY_DATA"]!; } public async validate() { await validateOrReject(this); diff --git a/src/common/databases/migrations/20230505075245_v1/migration.sql b/src/common/databases/migrations/20230505075245_v1/migration.sql new file mode 100644 index 00000000..c212eab0 --- /dev/null +++ b/src/common/databases/migrations/20230505075245_v1/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - Added the required column `iv` to the `files` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "files" ADD COLUMN "iv" VARCHAR(255) NOT NULL; diff --git a/src/common/databases/schema.prisma b/src/common/databases/schema.prisma index 08317b33..9f156af6 100644 --- a/src/common/databases/schema.prisma +++ b/src/common/databases/schema.prisma @@ -203,6 +203,7 @@ model Files { document Documents @relation(fields: [document_uid], references: [uid], onDelete: Cascade) document_uid String @db.VarChar(255) file_path String @unique @db.VarChar(255) + iv String @db.VarChar(255) created_at DateTime? @default(now()) updated_at DateTime? @updatedAt diff --git a/src/common/databases/seeders/seeder.ts b/src/common/databases/seeders/seeder.ts index 35df0bbd..ab450e9f 100644 --- a/src/common/databases/seeders/seeder.ts +++ b/src/common/databases/seeders/seeder.ts @@ -380,6 +380,7 @@ import { uid: uidFiles1, document_uid: uidDocument1, file_path: "https://www.google1.com", + iv: "randomIv1", created_at: new Date(), updated_at: new Date(), }, @@ -387,6 +388,7 @@ import { uid: uidFiles2, document_uid: uidDocument2, file_path: "https://www.google2.com", + iv: "randomIv2", created_at: new Date(), updated_at: new Date(), }, diff --git a/src/common/helpers/ObjectHydrate.ts b/src/common/helpers/ObjectHydrate.ts index 199b8733..92cb3194 100644 --- a/src/common/helpers/ObjectHydrate.ts +++ b/src/common/helpers/ObjectHydrate.ts @@ -10,4 +10,14 @@ export default abstract class ObjectHydrate { return plainToInstance(ClassEntity, from, options); }); } + + // public static fromTypeToRessource(ClassEntity: { new (): T }, from: Record): T { + // const properties = Object.getOwnPropertyNames(ClassEntity); + // const classInstance = new ClassEntity() as T; + // properties.forEach((property) => { + // if (property in from) { + // classInstance[property] = from[property] as T[keyof T]; + // } + // }); + // } } diff --git a/src/common/repositories/FilesRepository.ts b/src/common/repositories/FilesRepository.ts index 7c7d99d8..54bf3bef 100644 --- a/src/common/repositories/FilesRepository.ts +++ b/src/common/repositories/FilesRepository.ts @@ -36,6 +36,7 @@ export default class FilesRepository extends BaseRepository { }, }, file_path: file.file_path, + iv: file.iv }, }); } diff --git a/src/services/private-services/CryptoService/CryptoService.ts b/src/services/private-services/CryptoService/CryptoService.ts new file mode 100644 index 00000000..c0ac9d5e --- /dev/null +++ b/src/services/private-services/CryptoService/CryptoService.ts @@ -0,0 +1,74 @@ +import BaseService from "@Services/BaseService"; +import { Service } from "typedi"; +import { BackendVariables } from "@Common/config/variables/Variables"; +import crypto from "crypto"; + +@Service() +export default class CryptoService extends BaseService { + private key: CryptoKey; + private jwkKey: any; + constructor(protected variables: BackendVariables) { + super(); + this.key = new CryptoKey(); + this.jwkKey = { + kty: "oct", + k: variables.KEY_DATA, + alg: "A256GCM", + ext: true, + }; + } + + private async getKey() { + if (!this.key) this.key = await crypto.subtle.importKey("jwk", this.jwkKey, {name: "AES-GCM"}, false, ["encrypt", "decrypt"]); + return this.key; + } + + public getTextEncoderDecoder() { + return { encoder: new TextEncoder(), decoder: new TextDecoder("utf-8") } + } + + /** + * @description : encrypt data + * @throws {Error} If data cannot be encrypted + */ + public async encrypt(data: any) { + const { encoder, decoder } = this.getTextEncoderDecoder(); + const encodedData = encoder.encode(data); + const iv = crypto.getRandomValues(new Uint8Array(16)); + const key = await this.getKey(); + const cipherData = await crypto.subtle.encrypt( + { + name: "AES-GCM", + iv, + }, + key, + encodedData, + ); + + const cipherText = decoder.decode(cipherData); + const ivStringified = decoder.decode(iv); + + return { cipherText, ivStringified }; + } + + /** + * @description : decrypt data with an initialization vector + * @throws {Error} If data cannot be decrypted + */ + public async decrypt(cipherText: string, ivStringified: string): Promise { + const { encoder, decoder } = this.getTextEncoderDecoder(); + const cipherData = encoder.encode(cipherText); + const iv = encoder.encode(ivStringified); + const key = await this.getKey(); + const decryptedData = await crypto.subtle.decrypt( + { + name: "AES-GCM", + iv, + }, + key, + cipherData, + ); + + return decoder.decode(decryptedData); + } +} diff --git a/src/services/private-services/FilesService/FilesService.ts b/src/services/private-services/FilesService/FilesService.ts index 67656f16..ea97b2fc 100644 --- a/src/services/private-services/FilesService/FilesService.ts +++ b/src/services/private-services/FilesService/FilesService.ts @@ -2,10 +2,14 @@ import FilesRepository from "@Repositories/FilesRepository"; import BaseService from "@Services/BaseService"; import { Service } from "typedi"; import { File } from "le-coffre-resources/dist/SuperAdmin" +import CryptoService from "../CryptoService/CryptoService"; +import IpfsService from "../IpfsService/IpfsService"; +import fs from "fs"; +import { BackendVariables } from "@Common/config/variables/Variables"; @Service() export default class FilesService extends BaseService { - constructor(private filesRepository: FilesRepository) { + constructor(private filesRepository: FilesRepository, private ipfsService: IpfsService, private variables: BackendVariables, private cryptoService: CryptoService) { super(); } @@ -22,6 +26,11 @@ export default class FilesService extends BaseService { * @throws {Error} If file cannot be created */ public async create(file: File) { + const stream = fs.createReadStream('./login.png'); + const upload = await this.ipfsService.pinFile(stream, 'login.png'); + const encryptedPath = await this.cryptoService.encrypt(this.variables.PINATA_GATEWAY.concat(upload.IpfsHash)); + file.file_path = encryptedPath.cipherText; + file.iv = encryptedPath.ivStringified; return this.filesRepository.create(file); } @@ -29,10 +38,18 @@ export default class FilesService extends BaseService { * @description : Modify a new file * @throws {Error} If file cannot be modified */ - public async put(uid: string, file: File) { + public async update(uid: string, file: File) { return this.filesRepository.update(uid, file); } + /** + * @description : Delete a file + * @throws {Error} If file cannot be deleted + */ + public async delete(uid: string) { + return this.filesRepository.delete(uid); + } + /** * @description : Get a file by uid * @throws {Error} If project cannot be created diff --git a/src/services/private-services/IpfsService/IpfsService.ts b/src/services/private-services/IpfsService/IpfsService.ts new file mode 100644 index 00000000..95a1c4d1 --- /dev/null +++ b/src/services/private-services/IpfsService/IpfsService.ts @@ -0,0 +1,30 @@ +import BaseService from "@Services/BaseService"; +import { Service } from "typedi"; +import pinataSDK from "@pinata/sdk"; +import { BackendVariables } from "@Common/config/variables/Variables"; +import fs from "fs"; + +@Service() +export default class FilesService extends BaseService { + private ipfsClient: pinataSDK; + constructor(protected variables: BackendVariables) { + super(); + this.ipfsClient = new pinataSDK({ pinataApiKey: variables.PINATA_API_KEY, pinataSecretApiKey: variables.PINATA_API_SECRET }) + } + + /** + * @description : pin a file + * @throws {Error} If file cannot be pinned + */ + public async pinFile(stream: fs.ReadStream, fileName: string) { + return this.ipfsClient.pinFileToIPFS(stream, {pinataMetadata : {name: fileName}}); + } + + /** + * @description : unpin a file + * @throws {Error} If file cannot be unpinned + */ + public async unpinFile(hashToUnpin: string) { + return this.ipfsClient.unpin(hashToUnpin); + } +} From d893fe69062a6e45f40d759ab445f32b0aaa9668 Mon Sep 17 00:00:00 2001 From: OxSaitama Date: Mon, 8 May 2023 22:26:35 +0200 Subject: [PATCH 05/16] add formdata controller and delete file service --- package.json | 4 ++- src/app/api/super-admin/FilesController.ts | 15 +++++---- src/app/middlewares/FileHandler.ts | 22 +++++++++++++ .../20230505131655_v2/migration.sql | 8 +++++ src/common/databases/schema.prisma | 1 + src/common/databases/seeders/seeder.ts | 2 ++ src/common/repositories/FilesRepository.ts | 2 ++ src/entries/App.ts | 3 +- .../CryptoService/CryptoService.ts | 32 +++++++------------ .../FilesService/FilesService.ts | 13 +++++--- .../IpfsService/IpfsService.ts | 4 +-- 11 files changed, 72 insertions(+), 34 deletions(-) create mode 100644 src/app/middlewares/FileHandler.ts create mode 100644 src/common/databases/migrations/20230505131655_v2/migration.sql diff --git a/package.json b/package.json index 22753401..a286b223 100644 --- a/package.json +++ b/package.json @@ -48,8 +48,9 @@ "cors": "^2.8.5", "express": "^4.18.2", "jsonwebtoken": "^9.0.0", - "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.37", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.38", "module-alias": "^2.2.2", + "multer": "^1.4.5-lts.1", "next": "^13.1.5", "node-cache": "^5.1.2", "node-schedule": "^2.1.1", @@ -66,6 +67,7 @@ "@types/express": "^4.17.16", "@types/jest": "^29.5.0", "@types/jsonwebtoken": "^9.0.1", + "@types/multer": "^1.4.7", "@types/node": "^18.11.18", "@types/node-schedule": "^2.1.0", "@types/uuid": "^9.0.0", diff --git a/src/app/api/super-admin/FilesController.ts b/src/app/api/super-admin/FilesController.ts index 1fdd1696..ff0c393c 100644 --- a/src/app/api/super-admin/FilesController.ts +++ b/src/app/api/super-admin/FilesController.ts @@ -4,7 +4,6 @@ import ApiController from "@Common/system/controller-pattern/ApiController"; import { Service } from "typedi"; import FilesService from "@Services/private-services/FilesService/FilesService"; import { Files } from "@prisma/client"; -import ObjectHydrate from "@Common/helpers/ObjectHydrate"; import { File } from "le-coffre-resources/dist/SuperAdmin"; import { validateOrReject } from "class-validator"; @@ -46,14 +45,18 @@ export default class FilesController extends ApiController { @Post("/api/v1/super-admin/files") protected async post(req: Request, response: Response) { try { - //init File resource with request body values - const fileEntity = File.hydrate(req.body); + //get file + if(!req.file) throw new Error('No file provided') + + //init File resource with request body values + const fileEntity = File.hydrate(JSON.parse(req.body["q"])); + //validate File await validateOrReject(fileEntity, { groups: ["createFile"] }); //call service to get prisma entity - const prismaEntityCreated = await this.filesService.create(fileEntity); + const prismaEntityCreated = await this.filesService.create(fileEntity, req.file); //Hydrate ressource with prisma entity const fileEntityCreated = File.hydrate(prismaEntityCreated, { @@ -114,7 +117,7 @@ export default class FilesController extends ApiController { const fileEntity: Files = await this.filesService.delete(uid); //Hydrate ressource with prisma entity - const file = ObjectHydrate.hydrate(new File(), fileEntity, { strategy: "excludeAll" }); + const file = File.hydrate(fileEntity, { strategy: "excludeAll" }); //success this.httpSuccess(response, file); @@ -138,7 +141,7 @@ export default class FilesController extends ApiController { const fileEntity = await this.filesService.getByUid(uid); //Hydrate ressource with prisma entity - const file = ObjectHydrate.hydrate(new File(), fileEntity, { strategy: "excludeAll" }); + const file = File.hydrate(fileEntity, { strategy: "excludeAll" }); //success this.httpSuccess(response, file); diff --git a/src/app/middlewares/FileHandler.ts b/src/app/middlewares/FileHandler.ts new file mode 100644 index 00000000..3a6138ac --- /dev/null +++ b/src/app/middlewares/FileHandler.ts @@ -0,0 +1,22 @@ +import { NextFunction, Request, Response } from "express"; +import multer from "multer"; + +export default function fileHandler(req: Request, response: Response, next: NextFunction) { + const storage = multer.memoryStorage() + const upload = multer({storage:storage}).single('file'); + + // Here call the upload middleware of multer + upload(req, response, function (err) { + if (err instanceof multer.MulterError) { + // A Multer error occurred when uploading. + const err = new Error('Multer error'); + return next(err) + } else if (err) { + // An unknown error occurred when uploading. + const err = new Error('Server Error') + return next(err) + } + next() + }) +} + diff --git a/src/common/databases/migrations/20230505131655_v2/migration.sql b/src/common/databases/migrations/20230505131655_v2/migration.sql new file mode 100644 index 00000000..a7f5aaa6 --- /dev/null +++ b/src/common/databases/migrations/20230505131655_v2/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - Added the required column `file_name` to the `files` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "files" ADD COLUMN "file_name" VARCHAR(255) NOT NULL; diff --git a/src/common/databases/schema.prisma b/src/common/databases/schema.prisma index 9f156af6..b1b04496 100644 --- a/src/common/databases/schema.prisma +++ b/src/common/databases/schema.prisma @@ -203,6 +203,7 @@ model Files { document Documents @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) iv String @db.VarChar(255) created_at DateTime? @default(now()) updated_at DateTime? @updatedAt diff --git a/src/common/databases/seeders/seeder.ts b/src/common/databases/seeders/seeder.ts index ab450e9f..25dba6f3 100644 --- a/src/common/databases/seeders/seeder.ts +++ b/src/common/databases/seeders/seeder.ts @@ -379,6 +379,7 @@ import { { uid: uidFiles1, document_uid: uidDocument1, + file_name: "fileName1", file_path: "https://www.google1.com", iv: "randomIv1", created_at: new Date(), @@ -387,6 +388,7 @@ import { { uid: uidFiles2, document_uid: uidDocument2, + file_name: "fileName2", file_path: "https://www.google2.com", iv: "randomIv2", created_at: new Date(), diff --git a/src/common/repositories/FilesRepository.ts b/src/common/repositories/FilesRepository.ts index 54bf3bef..af5b8044 100644 --- a/src/common/repositories/FilesRepository.ts +++ b/src/common/repositories/FilesRepository.ts @@ -35,9 +35,11 @@ export default class FilesRepository extends BaseRepository { uid: file.document!.uid, }, }, + file_name: file.file_name, file_path: file.file_path, iv: file.iv }, + include: { document: true } }); } diff --git a/src/entries/App.ts b/src/entries/App.ts index b520f7a0..da76f6ae 100644 --- a/src/entries/App.ts +++ b/src/entries/App.ts @@ -8,6 +8,7 @@ import bodyParser from "body-parser"; // import TezosLink from "@Common/databases/TezosLink"; import errorHandler from "@App/middlewares/ErrorHandler"; import { BackendVariables } from "@Common/config/variables/Variables"; +import fileHandler from "@App/middlewares/FileHandler"; (async () => { try { @@ -22,7 +23,7 @@ import { BackendVariables } from "@Common/config/variables/Variables"; label, port: parseInt(port), rootUrl, - middlwares: [cors({ origin: "*" }), bodyParser.urlencoded({ extended: true }), bodyParser.json()], + middlwares: [cors({ origin: "*" }), fileHandler, bodyParser.urlencoded({ extended: true }), bodyParser.json()], errorHandler, }); diff --git a/src/services/private-services/CryptoService/CryptoService.ts b/src/services/private-services/CryptoService/CryptoService.ts index c0ac9d5e..23c7624a 100644 --- a/src/services/private-services/CryptoService/CryptoService.ts +++ b/src/services/private-services/CryptoService/CryptoService.ts @@ -5,11 +5,10 @@ import crypto from "crypto"; @Service() export default class CryptoService extends BaseService { - private key: CryptoKey; - private jwkKey: any; + private jwkKey: JsonWebKey; + private subtle: SubtleCrypto = crypto.webcrypto.subtle constructor(protected variables: BackendVariables) { super(); - this.key = new CryptoKey(); this.jwkKey = { kty: "oct", k: variables.KEY_DATA, @@ -19,24 +18,18 @@ export default class CryptoService extends BaseService { } private async getKey() { - if (!this.key) this.key = await crypto.subtle.importKey("jwk", this.jwkKey, {name: "AES-GCM"}, false, ["encrypt", "decrypt"]); - return this.key; - } - - public getTextEncoderDecoder() { - return { encoder: new TextEncoder(), decoder: new TextDecoder("utf-8") } + return await this.subtle.importKey("jwk", this.jwkKey, {name: "AES-GCM"}, false, ["encrypt", "decrypt"]); } /** * @description : encrypt data * @throws {Error} If data cannot be encrypted */ - public async encrypt(data: any) { - const { encoder, decoder } = this.getTextEncoderDecoder(); - const encodedData = encoder.encode(data); + public async encrypt(data: string) { + const encodedData = Buffer.from(data); const iv = crypto.getRandomValues(new Uint8Array(16)); const key = await this.getKey(); - const cipherData = await crypto.subtle.encrypt( + const cipherData = await this.subtle.encrypt( { name: "AES-GCM", iv, @@ -45,8 +38,8 @@ export default class CryptoService extends BaseService { encodedData, ); - const cipherText = decoder.decode(cipherData); - const ivStringified = decoder.decode(iv); + const cipherText = Buffer.from(cipherData).toString('base64'); + const ivStringified = Buffer.from(iv).toString('base64'); return { cipherText, ivStringified }; } @@ -56,11 +49,10 @@ export default class CryptoService extends BaseService { * @throws {Error} If data cannot be decrypted */ public async decrypt(cipherText: string, ivStringified: string): Promise { - const { encoder, decoder } = this.getTextEncoderDecoder(); - const cipherData = encoder.encode(cipherText); - const iv = encoder.encode(ivStringified); + const cipherData = Buffer.from(cipherText, 'base64'); + const iv = Buffer.from(ivStringified, 'base64'); const key = await this.getKey(); - const decryptedData = await crypto.subtle.decrypt( + const decryptedData = await this.subtle.decrypt( { name: "AES-GCM", iv, @@ -69,6 +61,6 @@ export default class CryptoService extends BaseService { cipherData, ); - return decoder.decode(decryptedData); + return Buffer.from(decryptedData).toString('utf-8'); } } diff --git a/src/services/private-services/FilesService/FilesService.ts b/src/services/private-services/FilesService/FilesService.ts index ea97b2fc..ce6373df 100644 --- a/src/services/private-services/FilesService/FilesService.ts +++ b/src/services/private-services/FilesService/FilesService.ts @@ -4,8 +4,9 @@ import { Service } from "typedi"; import { File } from "le-coffre-resources/dist/SuperAdmin" import CryptoService from "../CryptoService/CryptoService"; import IpfsService from "../IpfsService/IpfsService"; -import fs from "fs"; +//import fs from "fs"; import { BackendVariables } from "@Common/config/variables/Variables"; +import { Readable } from "stream"; @Service() export default class FilesService extends BaseService { @@ -25,10 +26,10 @@ export default class FilesService extends BaseService { * @description : Create a new file * @throws {Error} If file cannot be created */ - public async create(file: File) { - const stream = fs.createReadStream('./login.png'); - const upload = await this.ipfsService.pinFile(stream, 'login.png'); + public async create(file: File, fileData: Express.Multer.File) { + const upload = await this.ipfsService.pinFile(Readable.from(fileData.buffer), fileData.originalname); const encryptedPath = await this.cryptoService.encrypt(this.variables.PINATA_GATEWAY.concat(upload.IpfsHash)); + file.file_name = fileData.originalname; file.file_path = encryptedPath.cipherText; file.iv = encryptedPath.ivStringified; return this.filesRepository.create(file); @@ -47,6 +48,10 @@ export default class FilesService extends BaseService { * @throws {Error} If file cannot be deleted */ public async delete(uid: string) { + const fileToUnpin = await this.filesRepository.findOneByUid(uid); + const decryptedFilePath = await this.cryptoService.decrypt(fileToUnpin.file_path, fileToUnpin.iv); + const fileHash= decryptedFilePath.substring(this.variables.PINATA_GATEWAY.length); + await this.ipfsService.unpinFile(fileHash) return this.filesRepository.delete(uid); } diff --git a/src/services/private-services/IpfsService/IpfsService.ts b/src/services/private-services/IpfsService/IpfsService.ts index 95a1c4d1..cc0f3df7 100644 --- a/src/services/private-services/IpfsService/IpfsService.ts +++ b/src/services/private-services/IpfsService/IpfsService.ts @@ -2,7 +2,7 @@ import BaseService from "@Services/BaseService"; import { Service } from "typedi"; import pinataSDK from "@pinata/sdk"; import { BackendVariables } from "@Common/config/variables/Variables"; -import fs from "fs"; +import { Readable } from "stream"; @Service() export default class FilesService extends BaseService { @@ -16,7 +16,7 @@ export default class FilesService extends BaseService { * @description : pin a file * @throws {Error} If file cannot be pinned */ - public async pinFile(stream: fs.ReadStream, fileName: string) { + public async pinFile(stream: Readable, fileName: string) { return this.ipfsClient.pinFileToIPFS(stream, {pinataMetadata : {name: fileName}}); } From 52ac22fe211c57e0821d2861d793d4e4ceefa872 Mon Sep 17 00:00:00 2001 From: Vincent Alamelle Date: Tue, 9 May 2023 11:01:42 +0200 Subject: [PATCH 06/16] delete folder done --- .../super-admin/OfficeFoldersController.ts | 27 ++++++++++++++++++- .../repositories/OfficeFoldersRepository.ts | 11 ++++++++ .../OfficeFoldersService.ts | 12 +++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/app/api/super-admin/OfficeFoldersController.ts b/src/app/api/super-admin/OfficeFoldersController.ts index 38b46e47..989ea9b5 100644 --- a/src/app/api/super-admin/OfficeFoldersController.ts +++ b/src/app/api/super-admin/OfficeFoldersController.ts @@ -1,5 +1,5 @@ import { Response, Request } from "express"; -import { Controller, Get, Post, Put } from "@ControllerPattern/index"; +import { Controller, Delete, Get, Post, Put } from "@ControllerPattern/index"; import ApiController from "@Common/system/controller-pattern/ApiController"; import OfficeFoldersService from "@Services/super-admin/OfficeFoldersService/OfficeFoldersService"; import { Service } from "typedi"; @@ -126,4 +126,29 @@ export default class OfficeFoldersController extends ApiController { } this.httpSuccess(response, await this.officeFoldersService.getByUid("uid")); } + + /** + * @description Delete a specific folder + */ + @Delete("/api/v1/super-admin/folders/:uid") + protected async delete(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + throw new Error("No uid provided"); + } + + //call service to get prisma entity + const officeFoldertEntity: OfficeFolders = await this.officeFoldersService.delete(uid); + + //Hydrate ressource with prisma entity + const officeFolder = OfficeFolder.hydrate(officeFoldertEntity, { strategy: "excludeAll" }); + + //success + this.httpSuccess(response, officeFolder); + } catch (error) { + this.httpBadRequest(response, error); + return; + } + } } diff --git a/src/common/repositories/OfficeFoldersRepository.ts b/src/common/repositories/OfficeFoldersRepository.ts index 2a752d7f..46205472 100644 --- a/src/common/repositories/OfficeFoldersRepository.ts +++ b/src/common/repositories/OfficeFoldersRepository.ts @@ -143,4 +143,15 @@ export default class OfficeFoldersRepository extends BaseRepository { return officeFolderEntity; } + + /** + * @description : Delete a folder + */ + public async delete(uid: string): Promise { + return this.model.delete({ + where: { + uid: uid, + }, + }); + } } diff --git a/src/services/super-admin/OfficeFoldersService/OfficeFoldersService.ts b/src/services/super-admin/OfficeFoldersService/OfficeFoldersService.ts index 78e86a84..a2a23e89 100644 --- a/src/services/super-admin/OfficeFoldersService/OfficeFoldersService.ts +++ b/src/services/super-admin/OfficeFoldersService/OfficeFoldersService.ts @@ -48,4 +48,16 @@ export default class OfficeFoldersService extends BaseService { public async getByUid(uid: string, query?: any) { return this.officeFoldersRepository.findOneByUid(uid, query); } + + /** + * @description : Delete a folder + * @throws {Error} If document cannot be deleted + */ + public async delete(uid: string): Promise { + const officeFolder = await this.officeFoldersRepository.findOneByUid(uid); + if (officeFolder.status !== "ARCHIVED") { + throw new Error("Cannot delete a folder that is not archived"); + } + return this.officeFoldersRepository.delete(uid); + } } From 9d12381ba84b43cabb2ef8d61bce9b94a35aeb69 Mon Sep 17 00:00:00 2001 From: VincentAlamelle <107845815+VincentAlamelle@users.noreply.github.com> Date: Tue, 9 May 2023 11:34:33 +0200 Subject: [PATCH 07/16] Delete folder only when no customers linked (#37) Delete folder only if no customers linked in office_folder_has_customers --- package-lock.json | 638 ++++++++++++++++-- .../OfficeFoldersService.ts | 8 +- 2 files changed, 580 insertions(+), 66 deletions(-) diff --git a/package-lock.json b/package-lock.json index c72c56fe..edba19f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@pinata/sdk": "^2.1.0", "@prisma/client": "^4.11.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", @@ -16,8 +17,9 @@ "cors": "^2.8.5", "express": "^4.18.2", "jsonwebtoken": "^9.0.0", - "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.36", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.38", "module-alias": "^2.2.2", + "multer": "^1.4.5-lts.1", "next": "^13.1.5", "node-cache": "^5.1.2", "node-schedule": "^2.1.1", @@ -34,6 +36,7 @@ "@types/express": "^4.17.16", "@types/jest": "^29.5.0", "@types/jsonwebtoken": "^9.0.1", + "@types/multer": "^1.4.7", "@types/node": "^18.11.18", "@types/node-schedule": "^2.1.0", "@types/uuid": "^9.0.0", @@ -1039,14 +1042,14 @@ "dev": true }, "node_modules/@next/env": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.3.4.tgz", - "integrity": "sha512-oTK/wRV2qga86m/4VdrR1+/56UA6U1Qv3sIgowB+bZjahniZLEG5BmmQjfoGv7ZuLXBZ8Eec6hkL9BqJcrEL2g==" + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.1.tgz", + "integrity": "sha512-eD6WCBMFjLFooLM19SIhSkWBHtaFrZFfg2Cxnyl3vS3DAdFRfnx5TY2RxlkuKXdIRCC0ySbtK9JXXt8qLCqzZg==" }, "node_modules/@next/swc-darwin-arm64": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.3.4.tgz", - "integrity": "sha512-vux7RWfzxy1lD21CMwZsy9Ej+0+LZdIIj1gEhVmzOQqQZ5N56h8JamrjIVCfDL+Lpj8KwOmFZbPHE8qaYnL2pg==", + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.1.tgz", + "integrity": "sha512-eF8ARHtYfnoYtDa6xFHriUKA/Mfj/cCbmKb3NofeKhMccs65G6/loZ15a6wYCCx4rPAd6x4t1WmVYtri7EdeBg==", "cpu": [ "arm64" ], @@ -1059,9 +1062,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.3.4.tgz", - "integrity": "sha512-1tb+6JT98+t7UIhVQpKL7zegKnCs9RKU6cKNyj+DYKuC/NVl49/JaIlmwCwK8Ibl+RXxJrK7uSXSIO71feXsgw==", + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.1.tgz", + "integrity": "sha512-7cmDgF9tGWTgn5Gw+vP17miJbH4wcraMHDCOHTYWkO/VeKT73dUWG23TNRLfgtCNSPgH4V5B4uLHoZTanx9bAw==", "cpu": [ "x64" ], @@ -1074,9 +1077,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.3.4.tgz", - "integrity": "sha512-UqcKkYTKslf5YAJNtZ5XV1D5MQJIkVtDHL8OehDZERHzqOe7jvy41HFto33IDPPU8gJiP5eJb3V9U26uifqHjw==", + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.1.tgz", + "integrity": "sha512-qwJqmCri2ie8aTtE5gjTSr8S6O8B67KCYgVZhv9gKH44yvc/zXbAY8u23QGULsYOyh1islWE5sWfQNLOj9iryg==", "cpu": [ "arm64" ], @@ -1089,9 +1092,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.3.4.tgz", - "integrity": "sha512-HE/FmE8VvstAfehyo/XsrhGgz97cEr7uf9IfkgJ/unqSXE0CDshDn/4as6rRid74eDR8/exi7c2tdo49Tuqxrw==", + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.1.tgz", + "integrity": "sha512-qcC54tWNGDv/VVIFkazxhqH1Bnagjfs4enzELVRlUOoJPD2BGJTPI7z08pQPbbgxLtRiu8gl2mXvpB8WlOkMeA==", "cpu": [ "arm64" ], @@ -1104,9 +1107,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.3.4.tgz", - "integrity": "sha512-xU+ugaupGA4SL5aK1ZYEqVHrW3TPOhxVcpaJLfpANm2443J4GfxCmOacu9XcSgy5c51Mq7C9uZ1LODKHfZosRQ==", + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.1.tgz", + "integrity": "sha512-9TeWFlpLsBosZ+tsm/rWBaMwt5It9tPH8m3nawZqFUUrZyGRfGcI67js774vtx0k3rL9qbyY6+3pw9BCVpaYUA==", "cpu": [ "x64" ], @@ -1119,9 +1122,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.3.4.tgz", - "integrity": "sha512-cZvmf5KcYeTfIK6bCypfmxGUjme53Ep7hx94JJtGrYgCA1VwEuYdh+KouubJaQCH3aqnNE7+zGnVEupEKfoaaA==", + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.1.tgz", + "integrity": "sha512-sNDGaWmSqTS4QRUzw61wl4mVPeSqNIr1OOjLlQTRuyInxMxtqImRqdvzDvFTlDfdeUMU/DZhWGYoHrXLlZXe6A==", "cpu": [ "x64" ], @@ -1134,9 +1137,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.3.4.tgz", - "integrity": "sha512-7dL+CAUAjmgnVbjXPIpdj7/AQKFqEUL3bKtaOIE1JzJ5UMHHAXCPwzQtibrsvQpf9MwcAmiv8aburD3xH1xf8w==", + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.1.tgz", + "integrity": "sha512-+CXZC7u1iXdLRudecoUYbhbsXpglYv8KFYsFxKBPn7kg+bk7eJo738wAA4jXIl8grTF2mPdmO93JOQym+BlYGA==", "cpu": [ "arm64" ], @@ -1149,9 +1152,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.3.4.tgz", - "integrity": "sha512-qplTyzEl1vPkS+/DRK3pKSL0HeXrPHkYsV7U6gboHYpfqoHY+bcLUj3gwVUa9PEHRIoq4vXvPzx/WtzE6q52ng==", + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.1.tgz", + "integrity": "sha512-vIoXVVc7UYO68VwVMDKwJC2+HqAZQtCYiVlApyKEeIPIQpz2gpufzGxk1z3/gwrJt/kJ5CDZjlhYDCzd3hdz+g==", "cpu": [ "ia32" ], @@ -1164,9 +1167,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.3.4.tgz", - "integrity": "sha512-usdvZT7JHrTuXC+4OKN5mCzUkviFkCyJJTkEz8jhBpucg+T7s83e7owm3oNFzmj5iKfvxU2St6VkcnSgpFvEYA==", + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.1.tgz", + "integrity": "sha512-n8V5ImLQZibKTu10UUdI3nIeTLkliEXe628qxqW9v8My3BAH2a7H0SaCqkV2OgqFnn8sG1wxKYw9/SNJ632kSA==", "cpu": [ "x64" ], @@ -1178,6 +1181,17 @@ "node": ">= 10" } }, + "node_modules/@pinata/sdk": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@pinata/sdk/-/sdk-2.1.0.tgz", + "integrity": "sha512-hkS0tcKtsjf9xhsEBs2Nbey5s+Db7x5rlOH9TaWHBXkJ7IwwOs2xnEDigNaxAHKjYAwcw+m2hzpO5QgOfeF7Zw==", + "dependencies": { + "axios": "^0.21.1", + "form-data": "^2.3.3", + "is-ipfs": "^0.6.0", + "path": "^0.12.7" + } + }, "node_modules/@prisma/client": { "version": "4.13.0", "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.13.0.tgz", @@ -1413,10 +1427,19 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", "dev": true }, + "node_modules/@types/multer": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz", + "integrity": "sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/node": { - "version": "18.16.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.3.tgz", - "integrity": "sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q==" + "version": "18.16.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.6.tgz", + "integrity": "sha512-N7KINmeB8IN3vRR8dhgHEp+YpWvGFcpDoh5XZ8jB5a00AdFKCKEyyGTOPTddUf4JqU1ZKTVxkOxakDvchNVI2Q==" }, "node_modules/@types/node-schedule": { "version": "2.1.0", @@ -1478,9 +1501,9 @@ "dev": true }, "node_modules/@types/validator": { - "version": "13.7.15", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.15.tgz", - "integrity": "sha512-yeinDVQunb03AEP8luErFcyf/7Lf7AzKCD0NXfgVoGCCQDNpZET8Jgq74oBgqKld3hafLbfzt/3inUdQvaFeXQ==" + "version": "13.7.16", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.16.tgz", + "integrity": "sha512-VyKmLktUHYLbrSbsRi241MSUlGYomQgK/tfCNpej3Gt5qDOM10AZ3nU2aR2s5JritClXuOBu4K7MkywVW/Y6Ow==" }, "node_modules/@types/yargs": { "version": "17.0.24", @@ -1586,6 +1609,11 @@ "node": ">= 8" } }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -1605,6 +1633,19 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, "node_modules/babel-jest": { "version": "29.5.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz", @@ -1702,6 +1743,33 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1796,6 +1864,14 @@ "node": ">= 6" } }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dependencies": { + "base-x": "^3.0.2" + } + }, "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -1805,6 +1881,29 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -1813,8 +1912,7 @@ "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, "node_modules/busboy": { "version": "1.6.0", @@ -1866,9 +1964,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001482", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001482.tgz", - "integrity": "sha512-F1ZInsg53cegyjroxLNW9DmrEQ1SuGRTO1QlpA0o2/6OpQ0gFeDRoq1yFmnr8Sakn9qwwt9DmbxHB6w167OSuQ==", + "version": "1.0.30001486", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001486.tgz", + "integrity": "sha512-uv7/gXuHi10Whlj0pp5q/tsK/32J2QSqVRKQhs2j8VsDCjgyruAh/eEXHF822VqO9yT6iZKw3nRwZRSPBE9OQg==", "funding": [ { "type": "opencollective", @@ -1951,12 +2049,34 @@ "node": ">=8" } }, + "node_modules/cids": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz", + "integrity": "sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "buffer": "^5.5.0", + "class-is": "^1.1.0", + "multibase": "~0.6.0", + "multicodec": "^1.0.0", + "multihashes": "~0.4.15" + }, + "engines": { + "node": ">=4.0.0", + "npm": ">=3.0.0" + } + }, "node_modules/cjs-module-lexer": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", "dev": true }, + "node_modules/class-is": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", + "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==" + }, "node_modules/class-transformer": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", @@ -2038,12 +2158,37 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -2082,6 +2227,11 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -2147,6 +2297,14 @@ "node": ">=0.10.0" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -2213,9 +2371,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.382", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.382.tgz", - "integrity": "sha512-czMavlW52VIPgutbVL9JnZIZuFijzsG1ww/1z2Otu1r1q+9Qe2bTsH3My3sZarlvwyqHM6+mnZfEnt2Vr4dsIg==", + "version": "1.4.387", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.387.tgz", + "integrity": "sha512-tutLf+alr1/0YqJwKPdstVvDLmxmLb5xNyDLNS0RZmenHcEYk9qKfpKDCVZEKJ00JVbnayJm1MZAbYhYDFpcOw==", "dev": true }, "node_modules/emittery": { @@ -2443,6 +2601,38 @@ "node": ">=8" } }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -2655,6 +2845,25 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", @@ -2704,6 +2913,14 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "engines": { + "node": ">=8" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -2781,6 +2998,30 @@ "node": ">=0.10.0" } }, + "node_modules/is-ip": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", + "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", + "dependencies": { + "ip-regex": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-ipfs": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/is-ipfs/-/is-ipfs-0.6.3.tgz", + "integrity": "sha512-HyRot1dvLcxImtDqPxAaY1miO6WsiP/z7Yxpg2qpaLWv5UdhAPtLvHJ4kMLM0w8GSl8AFsVF23PHe1LzuWrUlQ==", + "dependencies": { + "bs58": "^4.0.1", + "cids": "~0.7.0", + "mafmt": "^7.0.0", + "multiaddr": "^7.2.1", + "multibase": "~0.6.0", + "multihashes": "~0.4.13" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -2802,6 +3043,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3615,7 +3861,7 @@ } }, "node_modules/le-coffre-resources": { - "resolved": "git+ssh://git@github.com/smart-chain-fr/leCoffre-resources.git#90ed41d1239cb915defec195cf0e1363ea157d36", + "resolved": "git+ssh://git@github.com/smart-chain-fr/leCoffre-resources.git#b3d3495c0e65afc0b149d6c07fd5741e144e2aaa", "license": "MIT", "dependencies": { "class-transformer": "^0.5.1", @@ -3632,9 +3878,9 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.10.28", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.28.tgz", - "integrity": "sha512-1eAgjLrZA0+2Wgw4hs+4Q/kEBycxQo8ZLYnmOvZ3AlM8ImAVAJgDPlZtISLEzD1vunc2q8s2Pn7XwB7I8U3Kzw==" + "version": "1.10.30", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.30.tgz", + "integrity": "sha512-PLGc+xfrQrkya/YK2/5X+bPpxRmyJBHM+xxz9krUdSgk4Vs2ZwxX5/Ow0lv3r9PDlDtNWb4u+it8MY5rZ0IyGw==" }, "node_modules/lines-and-columns": { "version": "1.2.4", @@ -3699,6 +3945,14 @@ "node": ">=12" } }, + "node_modules/mafmt": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/mafmt/-/mafmt-7.1.0.tgz", + "integrity": "sha512-vpeo9S+hepT3k2h5iFxzEHvvR0GPBx9uKaErmnRzYNcaKb03DgOArjEMlgG4a9LcuZZ89a3I8xbeto487n26eA==", + "dependencies": { + "multiaddr": "^7.3.0" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -3819,6 +4073,25 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/module-alias": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", @@ -3829,6 +4102,146 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/multiaddr": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/multiaddr/-/multiaddr-7.5.0.tgz", + "integrity": "sha512-GvhHsIGDULh06jyb6ev+VfREH9evJCFIRnh3jUt9iEZ6XDbyoisZRFEI9bMvK/AiR6y66y6P+eoBw9mBYMhMvw==", + "deprecated": "This module is deprecated, please upgrade to @multiformats/multiaddr", + "dependencies": { + "buffer": "^5.5.0", + "cids": "~0.8.0", + "class-is": "^1.1.0", + "is-ip": "^3.1.0", + "multibase": "^0.7.0", + "varint": "^5.0.0" + } + }, + "node_modules/multiaddr/node_modules/cids": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/cids/-/cids-0.8.3.tgz", + "integrity": "sha512-yoXTbV3llpm+EBGWKeL9xKtksPE/s6DPoDSY4fn8I8TEW1zehWXPSB0pwAXVDlLaOlrw+sNynj995uD9abmPhA==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "buffer": "^5.6.0", + "class-is": "^1.1.0", + "multibase": "^1.0.0", + "multicodec": "^1.0.1", + "multihashes": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0", + "npm": ">=3.0.0" + } + }, + "node_modules/multiaddr/node_modules/cids/node_modules/multibase": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-1.0.1.tgz", + "integrity": "sha512-KcCxpBVY8fdVKu4dJMAahq4F/2Z/9xqEjIiR7PiMe7LRGeorFn2NLmicN6nLBCqQvft6MG2Lc9X5P0IdyvnxEw==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + }, + "engines": { + "node": ">=10.0.0", + "npm": ">=6.0.0" + } + }, + "node_modules/multiaddr/node_modules/multibase": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", + "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + }, + "node_modules/multiaddr/node_modules/multihashes": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-1.0.1.tgz", + "integrity": "sha512-S27Tepg4i8atNiFaU5ZOm3+gl3KQlUanLs/jWcBxQHFttgq+5x1OgbQmf2d8axJ/48zYGBd/wT9d723USMFduw==", + "dependencies": { + "buffer": "^5.6.0", + "multibase": "^1.0.1", + "varint": "^5.0.0" + }, + "engines": { + "node": ">=10.0.0", + "npm": ">=6.0.0" + } + }, + "node_modules/multiaddr/node_modules/multihashes/node_modules/multibase": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-1.0.1.tgz", + "integrity": "sha512-KcCxpBVY8fdVKu4dJMAahq4F/2Z/9xqEjIiR7PiMe7LRGeorFn2NLmicN6nLBCqQvft6MG2Lc9X5P0IdyvnxEw==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + }, + "engines": { + "node": ">=10.0.0", + "npm": ">=6.0.0" + } + }, + "node_modules/multibase": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz", + "integrity": "sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + }, + "node_modules/multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + }, + "node_modules/multihashes": { + "version": "0.4.21", + "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz", + "integrity": "sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==", + "dependencies": { + "buffer": "^5.5.0", + "multibase": "^0.7.0", + "varint": "^5.0.0" + } + }, + "node_modules/multihashes/node_modules/multibase": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", + "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + }, "node_modules/nanoid": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", @@ -3861,16 +4274,17 @@ } }, "node_modules/next": { - "version": "13.3.4", - "resolved": "https://registry.npmjs.org/next/-/next-13.3.4.tgz", - "integrity": "sha512-sod7HeokBSvH5QV0KB+pXeLfcXUlLrGnVUXxHpmhilQ+nQYT3Im2O8DswD5e4uqbR8Pvdu9pcWgb1CbXZQZlmQ==", + "version": "13.4.1", + "resolved": "https://registry.npmjs.org/next/-/next-13.4.1.tgz", + "integrity": "sha512-JBw2kAIyhKDpjhEWvNVoFeIzNp9xNxg8wrthDOtMctfn3EpqGCmW0FSviNyGgOSOSn6zDaX48pmvbdf6X2W9xA==", "dependencies": { - "@next/env": "13.3.4", + "@next/env": "13.4.1", "@swc/helpers": "0.5.1", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001406", "postcss": "8.4.14", - "styled-jsx": "5.1.1" + "styled-jsx": "5.1.1", + "zod": "3.21.4" }, "bin": { "next": "dist/bin/next" @@ -3879,15 +4293,15 @@ "node": ">=16.8.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "13.3.4", - "@next/swc-darwin-x64": "13.3.4", - "@next/swc-linux-arm64-gnu": "13.3.4", - "@next/swc-linux-arm64-musl": "13.3.4", - "@next/swc-linux-x64-gnu": "13.3.4", - "@next/swc-linux-x64-musl": "13.3.4", - "@next/swc-win32-arm64-msvc": "13.3.4", - "@next/swc-win32-ia32-msvc": "13.3.4", - "@next/swc-win32-x64-msvc": "13.3.4" + "@next/swc-darwin-arm64": "13.4.1", + "@next/swc-darwin-x64": "13.4.1", + "@next/swc-linux-arm64-gnu": "13.4.1", + "@next/swc-linux-arm64-musl": "13.4.1", + "@next/swc-linux-x64-gnu": "13.4.1", + "@next/swc-linux-x64-musl": "13.4.1", + "@next/swc-win32-arm64-msvc": "13.4.1", + "@next/swc-win32-ia32-msvc": "13.4.1", + "@next/swc-win32-x64-msvc": "13.4.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -4185,6 +4599,15 @@ "node": ">= 0.8" } }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4347,6 +4770,19 @@ "resolved": "https://registry.npmjs.org/prisma-query/-/prisma-query-2.0.0.tgz", "integrity": "sha512-+5eneJrgTFxW48j4JaWJ8iBwFSH+YQRtA1N+QEzqsREnTEAbs1Bq85xoZP7ZNEXDsoLOoIo4rYfCYRozuVOB9Q==" }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -4461,6 +4897,25 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -4765,6 +5220,19 @@ "node": ">=10.0.0" } }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -5097,6 +5565,11 @@ "node": ">= 0.6" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, "node_modules/typedi": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/typedi/-/typedi-0.10.0.tgz", @@ -5158,6 +5631,24 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -5207,6 +5698,11 @@ "node": ">= 0.10" } }, + "node_modules/varint": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", + "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==" + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -5275,6 +5771,14 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -5336,6 +5840,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", + "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/src/services/super-admin/OfficeFoldersService/OfficeFoldersService.ts b/src/services/super-admin/OfficeFoldersService/OfficeFoldersService.ts index a2a23e89..671b0c3b 100644 --- a/src/services/super-admin/OfficeFoldersService/OfficeFoldersService.ts +++ b/src/services/super-admin/OfficeFoldersService/OfficeFoldersService.ts @@ -54,9 +54,11 @@ export default class OfficeFoldersService extends BaseService { * @throws {Error} If document cannot be deleted */ public async delete(uid: string): Promise { - const officeFolder = await this.officeFoldersRepository.findOneByUid(uid); - if (officeFolder.status !== "ARCHIVED") { - throw new Error("Cannot delete a folder that is not archived"); + const officeFolderEntity = await this.officeFoldersRepository.findOneByUid(uid, { office_folder_has_customers: true }); + const officeFolder = OfficeFolder.hydrate(officeFolderEntity, { strategy: "excludeAll" }); + + if (officeFolder.office_folder_has_customers && officeFolder.office_folder_has_customers.length !== 0) { + throw new Error("This folder is used by customers"); } return this.officeFoldersRepository.delete(uid); } From 9a261188394f4f226e983a3dfcbde37043b58956 Mon Sep 17 00:00:00 2001 From: Vincent Alamelle Date: Tue, 9 May 2023 12:22:24 +0200 Subject: [PATCH 08/16] Added refused_reason to update document + check if file on delete document --- src/app/api/super-admin/DocumentsController.ts | 4 ++-- .../super-admin/DocumentsService/DocumentsService.ts | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/app/api/super-admin/DocumentsController.ts b/src/app/api/super-admin/DocumentsController.ts index 769ef36b..4cbd9767 100644 --- a/src/app/api/super-admin/DocumentsController.ts +++ b/src/app/api/super-admin/DocumentsController.ts @@ -81,13 +81,13 @@ export default class DocumentsController extends ApiController { } //init Document resource with request body values - const documentEntity = Document.hydrate(req.body); + const documentEntity = Document.hydrate(req.body); //validate document await validateOrReject(documentEntity, { groups: ["updateDocument"] }); //call service to get prisma entity - const prismaEntityUpdated: Documents = await this.documentsService.update(uid, documentEntity); + const prismaEntityUpdated: Documents = await this.documentsService.update(uid, documentEntity, req.body.refused_reason); //Hydrate ressource with prisma entity const document = Document.hydrate(prismaEntityUpdated, { strategy: "excludeAll" }); diff --git a/src/services/super-admin/DocumentsService/DocumentsService.ts b/src/services/super-admin/DocumentsService/DocumentsService.ts index 4e466535..5b6e7217 100644 --- a/src/services/super-admin/DocumentsService/DocumentsService.ts +++ b/src/services/super-admin/DocumentsService/DocumentsService.ts @@ -38,8 +38,8 @@ export default class DocumentsService extends BaseService { * @description : Modify a document * @throws {Error} If document cannot be modified */ - public async update(uid: string, document: Document): Promise { - return this.documentsRepository.update(uid, document); + public async update(uid: string, document: Document, refused_reason?: string): Promise { + return this.documentsRepository.update(uid, document, refused_reason); } /** @@ -55,6 +55,12 @@ export default class DocumentsService extends BaseService { * @throws {Error} If document cannot be get by uid */ public async getByUid(uid: string, query?: any) { + const documentEntity = await this.documentsRepository.findOneByUid(uid, { office_folder_has_customers: true }); + const document = Document.hydrate(documentEntity, { strategy: "excludeAll" }); + + if (document.files && document.files.length !== 0) { + throw new Error("Can't delete a document with file"); + } return this.documentsRepository.findOneByUid(uid, query); } } From 20a5041fd2f0a37281cfb2050781f1fae0b07a95 Mon Sep 17 00:00:00 2001 From: Vincent Alamelle Date: Tue, 9 May 2023 14:43:06 +0200 Subject: [PATCH 09/16] wip --- src/app/api/super-admin/DeedsController.ts | 35 +++++++++++++++++++++- src/app/api/super-admin/FilesController.ts | 2 +- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/app/api/super-admin/DeedsController.ts b/src/app/api/super-admin/DeedsController.ts index 382c9e7d..8dc14408 100644 --- a/src/app/api/super-admin/DeedsController.ts +++ b/src/app/api/super-admin/DeedsController.ts @@ -1,10 +1,11 @@ import { Response, Request } from "express"; -import { Controller, Get } from "@ControllerPattern/index"; +import { Controller, Get, Put } from "@ControllerPattern/index"; import ApiController from "@Common/system/controller-pattern/ApiController"; import DeedsService from "@Services/super-admin/DeedsService/DeedsService"; import { Service } from "typedi"; import { Deeds } from "@prisma/client"; import { Deed } from "le-coffre-resources/dist/SuperAdmin"; +import { validateOrReject } from "class-validator"; @Controller() @Service() @@ -62,4 +63,36 @@ export default class DeedsController extends ApiController { } this.httpSuccess(response, await this.deedsService.getByUid("uid")); } + + /** + * @description Modify a specific deed by uid + */ + @Put("/api/v1/super-admin/deeds/:uid") + protected async put(req: Request, response: Response) { + try { + const uid = req.params["uid"]; + if (!uid) { + throw new Error("No uid provided"); + } + //init OfficeFolder resource with request body values + const deedEntity = Deed.hydrate(req.body); + + //validate folder + await validateOrReject(deedEntity, { groups: ["updateDeed"], forbidUnknownValues: false }); + + //call service to get prisma entity + const prismaEntityUpdated = await this.deedsService.update(uid, deedEntity); + + //Hydrate ressource with prisma entity + const deedEntityUpdated = Deed.hydrate(prismaEntityUpdated, { + strategy: "excludeAll", + }); + + //success + this.httpSuccess(response, deedEntityUpdated); + } catch (error) { + this.httpBadRequest(response, error); + return; + } + } } diff --git a/src/app/api/super-admin/FilesController.ts b/src/app/api/super-admin/FilesController.ts index ff0c393c..2ca5f7b1 100644 --- a/src/app/api/super-admin/FilesController.ts +++ b/src/app/api/super-admin/FilesController.ts @@ -86,7 +86,7 @@ export default class FilesController extends ApiController { const fileEntity = File.hydrate(req.body); //validate file - await validateOrReject(fileEntity, { groups: ["create"] }); + await validateOrReject(fileEntity, { groups: ["updateFile"] }); //call service to get prisma entity const prismaEntityUpdated: Files = await this.filesService.update(uid, fileEntity); From da5108bd8f695ad9220964a6d67357ca5021a37f Mon Sep 17 00:00:00 2001 From: Vincent Alamelle Date: Tue, 9 May 2023 14:54:10 +0200 Subject: [PATCH 10/16] wip --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index edba19f7..b8d008e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "cors": "^2.8.5", "express": "^4.18.2", "jsonwebtoken": "^9.0.0", - "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.38", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.39", "module-alias": "^2.2.2", "multer": "^1.4.5-lts.1", "next": "^13.1.5", @@ -3861,7 +3861,7 @@ } }, "node_modules/le-coffre-resources": { - "resolved": "git+ssh://git@github.com/smart-chain-fr/leCoffre-resources.git#b3d3495c0e65afc0b149d6c07fd5741e144e2aaa", + "resolved": "git+ssh://git@github.com/smart-chain-fr/leCoffre-resources.git#d889471c72060fc1ba0d582431c8a4f0c27d441f", "license": "MIT", "dependencies": { "class-transformer": "^0.5.1", diff --git a/package.json b/package.json index a286b223..5ae106cc 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "cors": "^2.8.5", "express": "^4.18.2", "jsonwebtoken": "^9.0.0", - "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.38", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.39", "module-alias": "^2.2.2", "multer": "^1.4.5-lts.1", "next": "^13.1.5", From 385d886fb4d7560d13b8047dd6d56a8f05a65da5 Mon Sep 17 00:00:00 2001 From: Vincent Alamelle Date: Tue, 9 May 2023 14:54:56 +0200 Subject: [PATCH 11/16] hotfix delete document --- .../super-admin/DocumentsService/DocumentsService.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/services/super-admin/DocumentsService/DocumentsService.ts b/src/services/super-admin/DocumentsService/DocumentsService.ts index 5b6e7217..a2eefe95 100644 --- a/src/services/super-admin/DocumentsService/DocumentsService.ts +++ b/src/services/super-admin/DocumentsService/DocumentsService.ts @@ -47,6 +47,12 @@ export default class DocumentsService extends BaseService { * @throws {Error} If document cannot be deleted */ public async delete(uid: string): Promise { + const documentEntity = await this.documentsRepository.findOneByUid(uid, { office_folder_has_customers: true }); + const document = Document.hydrate(documentEntity, { strategy: "excludeAll" }); + + if (document.files && document.files.length !== 0) { + throw new Error("Can't delete a document with file"); + } return this.documentsRepository.delete(uid); } @@ -55,12 +61,6 @@ export default class DocumentsService extends BaseService { * @throws {Error} If document cannot be get by uid */ public async getByUid(uid: string, query?: any) { - const documentEntity = await this.documentsRepository.findOneByUid(uid, { office_folder_has_customers: true }); - const document = Document.hydrate(documentEntity, { strategy: "excludeAll" }); - - if (document.files && document.files.length !== 0) { - throw new Error("Can't delete a document with file"); - } return this.documentsRepository.findOneByUid(uid, query); } } From e6654979cd885ee3230b8503218ecf03f173fd85 Mon Sep 17 00:00:00 2001 From: Vincent Alamelle Date: Tue, 9 May 2023 15:37:27 +0200 Subject: [PATCH 12/16] update deed functional --- src/app/api/super-admin/DeedsController.ts | 16 +++++++++++----- src/common/repositories/DeedsRepository.ts | 12 +++++++++--- .../super-admin/DeedsService/DeedsService.ts | 4 ++-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/app/api/super-admin/DeedsController.ts b/src/app/api/super-admin/DeedsController.ts index 8dc14408..f8ff9da7 100644 --- a/src/app/api/super-admin/DeedsController.ts +++ b/src/app/api/super-admin/DeedsController.ts @@ -49,8 +49,15 @@ export default class DeedsController extends ApiController { throw new Error("No uid provided"); } - //call service to get prisma entity - const deedEntity: Deeds = await this.deedsService.getByUid(uid); + let deedEntity: Deeds; + //get query + if (req.query["q"]) { + const query = JSON.parse(req.query["q"] as string); + deedEntity = await this.deedsService.getByUid(uid, query); + } else { + //call service to get prisma entity + deedEntity = await this.deedsService.getByUid(uid); + } //Hydrate ressource with prisma entity const deed = Deed.hydrate(deedEntity, { strategy: "excludeAll" }); @@ -61,7 +68,6 @@ export default class DeedsController extends ApiController { this.httpBadRequest(response, error); return; } - this.httpSuccess(response, await this.deedsService.getByUid("uid")); } /** @@ -76,9 +82,9 @@ export default class DeedsController extends ApiController { } //init OfficeFolder resource with request body values const deedEntity = Deed.hydrate(req.body); - + //validate folder - await validateOrReject(deedEntity, { groups: ["updateDeed"], forbidUnknownValues: false }); + await validateOrReject(deedEntity, { groups: ["updateDeed"], forbidUnknownValues: false }); //call service to get prisma entity const prismaEntityUpdated = await this.deedsService.update(uid, deedEntity); diff --git a/src/common/repositories/DeedsRepository.ts b/src/common/repositories/DeedsRepository.ts index dcbe3203..79a836a8 100644 --- a/src/common/repositories/DeedsRepository.ts +++ b/src/common/repositories/DeedsRepository.ts @@ -72,6 +72,8 @@ export default class DeedsRepository extends BaseRepository { deed_has_document_types: true, }, }; + console.log(deed.deed_has_document_types); + if (deed.deed_has_document_types) { updateArgs.data.deed_has_document_types = { deleteMany: { deed_uid: uid }, @@ -89,12 +91,16 @@ export default class DeedsRepository extends BaseRepository { /** * @description : Find unique deed */ - public async findOneByUid(uid: string): Promise { - const deedTypeEntity = await this.model.findUnique({ + public async findOneByUid(uid: string, query?: any): Promise { + const findOneArgs: Prisma.DeedsFindUniqueArgs = { where: { uid: uid, }, - }); + }; + if (query) { + findOneArgs.include = query; + } + const deedTypeEntity = await this.model.findUnique(findOneArgs); if (!deedTypeEntity) { throw new Error("deed not found"); diff --git a/src/services/super-admin/DeedsService/DeedsService.ts b/src/services/super-admin/DeedsService/DeedsService.ts index bb133369..93a6c6bd 100644 --- a/src/services/super-admin/DeedsService/DeedsService.ts +++ b/src/services/super-admin/DeedsService/DeedsService.ts @@ -38,7 +38,7 @@ export default class DeedsService extends BaseService { * @description : Get a deed by uid * @throws {Error} If deed-type cannot be get by uid */ - public async getByUid(uid: string) { - return this.deedRepository.findOneByUid(uid); + public async getByUid(uid: string, query?: any) { + return this.deedRepository.findOneByUid(uid, query); } } From 4faa88a80fb84b3bd30ff40043d8d72297f9e2ab Mon Sep 17 00:00:00 2001 From: Vincent Alamelle Date: Tue, 9 May 2023 17:07:58 +0200 Subject: [PATCH 13/16] wip --- package-lock.json | 32 ++++++++++++++++---------------- package.json | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index b8d008e5..edf21b7d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "cors": "^2.8.5", "express": "^4.18.2", "jsonwebtoken": "^9.0.0", - "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.39", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.40", "module-alias": "^2.2.2", "multer": "^1.4.5-lts.1", "next": "^13.1.5", @@ -1193,12 +1193,12 @@ } }, "node_modules/@prisma/client": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.13.0.tgz", - "integrity": "sha512-YaiiICcRB2hatxsbnfB66uWXjcRw3jsZdlAVxmx0cFcTc/Ad/sKdHCcWSnqyDX47vAewkjRFwiLwrOUjswVvmA==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.14.0.tgz", + "integrity": "sha512-MK/XaA2sFdfaOa7I9MjNKz6dxeIEdeZlnpNRoF2w3JuRLlFJLkpp6cD3yaqw2nUUhbrn3Iqe3ZpVV+VuGGil7Q==", "hasInstallScript": true, "dependencies": { - "@prisma/engines-version": "4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a" + "@prisma/engines-version": "4.14.0-67.d9a4c5988f480fa576d43970d5a23641aa77bc9c" }, "engines": { "node": ">=14.17" @@ -1213,16 +1213,16 @@ } }, "node_modules/@prisma/engines": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.13.0.tgz", - "integrity": "sha512-HrniowHRZXHuGT9XRgoXEaP2gJLXM5RMoItaY2PkjvuZ+iHc0Zjbm/302MB8YsPdWozAPHHn+jpFEcEn71OgPw==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.14.0.tgz", + "integrity": "sha512-PDNlhP/1vyTgmNyiucGqGCdXIp7HIkkvKO50si3y3PcceeHvqtiKPaH1iJdz63jCWMVMbj2MElSxXPOeBvEVIQ==", "devOptional": true, "hasInstallScript": true }, "node_modules/@prisma/engines-version": { - "version": "4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a.tgz", - "integrity": "sha512-fsQlbkhPJf08JOzKoyoD9atdUijuGBekwoOPZC3YOygXEml1MTtgXVpnUNchQlRSY82OQ6pSGQ9PxUe4arcSLQ==" + "version": "4.14.0-67.d9a4c5988f480fa576d43970d5a23641aa77bc9c", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.14.0-67.d9a4c5988f480fa576d43970d5a23641aa77bc9c.tgz", + "integrity": "sha512-3jum8/YSudeSN0zGW5qkpz+wAN2V/NYCQ+BPjvHYDfWatLWlQkqy99toX0GysDeaUoBIJg1vaz2yKqiA3CFcQw==" }, "node_modules/@sinclair/typebox": { "version": "0.25.24", @@ -3861,7 +3861,7 @@ } }, "node_modules/le-coffre-resources": { - "resolved": "git+ssh://git@github.com/smart-chain-fr/leCoffre-resources.git#d889471c72060fc1ba0d582431c8a4f0c27d441f", + "resolved": "git+ssh://git@github.com/smart-chain-fr/leCoffre-resources.git#62639b8bfcd0f779357554a04cd40e8a3ba4e62b", "license": "MIT", "dependencies": { "class-transformer": "^0.5.1", @@ -4749,13 +4749,13 @@ } }, "node_modules/prisma": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.13.0.tgz", - "integrity": "sha512-L9mqjnSmvWIRCYJ9mQkwCtj4+JDYYTdhoyo8hlsHNDXaZLh/b4hR0IoKIBbTKxZuyHQzLopb/+0Rvb69uGV7uA==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.14.0.tgz", + "integrity": "sha512-+5dMl1uxMQb4RepndY6AwR9xi1cDcaGFICu+ws6/Nmgt93mFPNj8tYxSfTdmfg+rkNrUId9rk/Ac2vTgLe/oXA==", "devOptional": true, "hasInstallScript": true, "dependencies": { - "@prisma/engines": "4.13.0" + "@prisma/engines": "4.14.0" }, "bin": { "prisma": "build/index.js", diff --git a/package.json b/package.json index 5ae106cc..22364cb8 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "cors": "^2.8.5", "express": "^4.18.2", "jsonwebtoken": "^9.0.0", - "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.39", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.40", "module-alias": "^2.2.2", "multer": "^1.4.5-lts.1", "next": "^13.1.5", From 6f90174f5fc875a985fd916f21bb4652cfb2edb1 Mon Sep 17 00:00:00 2001 From: Vincent Alamelle Date: Tue, 9 May 2023 17:11:47 +0200 Subject: [PATCH 14/16] hotfix delete document --- src/services/super-admin/DocumentsService/DocumentsService.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/services/super-admin/DocumentsService/DocumentsService.ts b/src/services/super-admin/DocumentsService/DocumentsService.ts index a2eefe95..2a9825c1 100644 --- a/src/services/super-admin/DocumentsService/DocumentsService.ts +++ b/src/services/super-admin/DocumentsService/DocumentsService.ts @@ -47,7 +47,9 @@ export default class DocumentsService extends BaseService { * @throws {Error} If document cannot be deleted */ public async delete(uid: string): Promise { - const documentEntity = await this.documentsRepository.findOneByUid(uid, { office_folder_has_customers: true }); + const documentEntity = await this.documentsRepository.findOneByUid(uid, { files: true }); + console.log(documentEntity); + const document = Document.hydrate(documentEntity, { strategy: "excludeAll" }); if (document.files && document.files.length !== 0) { From 28e5b8a33f8c34cba4e80d66086c21948599ec49 Mon Sep 17 00:00:00 2001 From: Vincent Alamelle Date: Tue, 9 May 2023 17:48:25 +0200 Subject: [PATCH 15/16] Remove useless console log --- src/app/api/super-admin/DocumentsController.ts | 2 -- src/common/repositories/DeedsRepository.ts | 1 - src/services/private-services/AuthService/AuthService.ts | 1 - src/services/super-admin/DocumentsService/DocumentsService.ts | 1 - 4 files changed, 5 deletions(-) diff --git a/src/app/api/super-admin/DocumentsController.ts b/src/app/api/super-admin/DocumentsController.ts index 4cbd9767..ae60a9ab 100644 --- a/src/app/api/super-admin/DocumentsController.ts +++ b/src/app/api/super-admin/DocumentsController.ts @@ -47,8 +47,6 @@ export default class DocumentsController extends ApiController { try { //init Document resource with request body values const documentEntity = Document.hydrate(req.body); - console.log(documentEntity); - //validate document await validateOrReject(documentEntity, { groups: ["createDocument"], forbidUnknownValues: false }); diff --git a/src/common/repositories/DeedsRepository.ts b/src/common/repositories/DeedsRepository.ts index 79a836a8..945a181d 100644 --- a/src/common/repositories/DeedsRepository.ts +++ b/src/common/repositories/DeedsRepository.ts @@ -72,7 +72,6 @@ export default class DeedsRepository extends BaseRepository { deed_has_document_types: true, }, }; - console.log(deed.deed_has_document_types); if (deed.deed_has_document_types) { updateArgs.data.deed_has_document_types = { diff --git a/src/services/private-services/AuthService/AuthService.ts b/src/services/private-services/AuthService/AuthService.ts index 5470dba2..ab528ec4 100644 --- a/src/services/private-services/AuthService/AuthService.ts +++ b/src/services/private-services/AuthService/AuthService.ts @@ -47,7 +47,6 @@ export default class AuthService extends BaseService { const data = await res.json(); return data as IdNotTokens; } catch (error) { - console.log(error); throw new Error(); } } diff --git a/src/services/super-admin/DocumentsService/DocumentsService.ts b/src/services/super-admin/DocumentsService/DocumentsService.ts index 2a9825c1..81a0d8bc 100644 --- a/src/services/super-admin/DocumentsService/DocumentsService.ts +++ b/src/services/super-admin/DocumentsService/DocumentsService.ts @@ -48,7 +48,6 @@ export default class DocumentsService extends BaseService { */ public async delete(uid: string): Promise { const documentEntity = await this.documentsRepository.findOneByUid(uid, { files: true }); - console.log(documentEntity); const document = Document.hydrate(documentEntity, { strategy: "excludeAll" }); From 1f834ab79d8f95013ff39b7e9c53e88b827b3f74 Mon Sep 17 00:00:00 2001 From: Hugo Lextrait Date: Wed, 10 May 2023 11:00:02 +0200 Subject: [PATCH 16/16] add data to seeder --- package-lock.json | 18 ++++++++-------- src/common/databases/seeders/seeder.ts | 30 +++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index edf21b7d..edc5e4e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1437,9 +1437,9 @@ } }, "node_modules/@types/node": { - "version": "18.16.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.6.tgz", - "integrity": "sha512-N7KINmeB8IN3vRR8dhgHEp+YpWvGFcpDoh5XZ8jB5a00AdFKCKEyyGTOPTddUf4JqU1ZKTVxkOxakDvchNVI2Q==" + "version": "18.16.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.7.tgz", + "integrity": "sha512-MFg7ua/bRtnA1hYE3pVyWxGd/r7aMqjNOdHvlSsXV3n8iaeGKkOaPzpJh6/ovf4bEXWcojkeMJpTsq3mzXW4IQ==" }, "node_modules/@types/node-schedule": { "version": "2.1.0", @@ -1501,9 +1501,9 @@ "dev": true }, "node_modules/@types/validator": { - "version": "13.7.16", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.16.tgz", - "integrity": "sha512-VyKmLktUHYLbrSbsRi241MSUlGYomQgK/tfCNpej3Gt5qDOM10AZ3nU2aR2s5JritClXuOBu4K7MkywVW/Y6Ow==" + "version": "13.7.17", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.17.tgz", + "integrity": "sha512-aqayTNmeWrZcvnG2MG9eGYI6b7S5fl+yKgPs6bAjOTwPS316R5SxBGKvtSExfyoJU7pIeHJfsHI0Ji41RVMkvQ==" }, "node_modules/@types/yargs": { "version": "17.0.24", @@ -2371,9 +2371,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.387", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.387.tgz", - "integrity": "sha512-tutLf+alr1/0YqJwKPdstVvDLmxmLb5xNyDLNS0RZmenHcEYk9qKfpKDCVZEKJ00JVbnayJm1MZAbYhYDFpcOw==", + "version": "1.4.388", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.388.tgz", + "integrity": "sha512-xZ0y4zjWZgp65okzwwt00f2rYibkFPHUv9qBz+Vzn8cB9UXIo9Zc6Dw81LJYhhNt0G/vR1OJEfStZ49NKl0YxQ==", "dev": true }, "node_modules/emittery": { diff --git a/src/common/databases/seeders/seeder.ts b/src/common/databases/seeders/seeder.ts index 25dba6f3..f37a2dd0 100644 --- a/src/common/databases/seeders/seeder.ts +++ b/src/common/databases/seeders/seeder.ts @@ -42,6 +42,8 @@ import { const uidContact1: string = randomString(); const uidContact2: string = randomString(); + const uidContact3: string = randomString(); + const uidContact4: string = randomString(); const uidAddress1: string = randomString(); const uidAddress2: string = randomString(); @@ -151,6 +153,32 @@ import { updated_at: new Date(), civility: ECivility.FEMALE, }, + { + uid: uidContact3, + address_uid: uidAddress2, + first_name: "Maitre Marcelino", + last_name: "Jack", + email: "Marcelino.Jack@example.com", + phone_number: randomString(), + cell_phone_number: randomString(), + birthdate: null, + created_at: new Date(), + updated_at: new Date(), + civility: ECivility.MALE, + }, + { + uid: uidContact4, + address_uid: uidAddress2, + first_name: "Maitre Massi", + last_name: "Jack", + email: "Massi.Jack@example.com", + phone_number: randomString(), + cell_phone_number: randomString(), + birthdate: null, + created_at: new Date(), + updated_at: new Date(), + civility: ECivility.FEMALE, + }, ]; const offices: Offices[] = [ @@ -509,4 +537,4 @@ import { } console.log(">MOCK DATA - Seeding completed!"); -})(); +})(); \ No newline at end of file