From 5be70cff2eb385ad78185a49ec89a59326686a57 Mon Sep 17 00:00:00 2001 From: Vins Date: Fri, 16 Feb 2024 15:00:07 +0100 Subject: [PATCH 1/7] Ondoing --- package.json | 3 +- src/app/api/notary/BucketController.ts | 126 ++++++++++++++++++ src/app/api/notary/OfficesController.ts | 3 + src/app/index.ts | 2 + .../20240215123932_rib/migration.sql | 2 + .../20240215142816_rib_optional/migration.sql | 2 + .../20240216104224_rib_name/migration.sql | 2 + src/common/databases/schema.prisma | 2 + src/common/databases/seeders/prod-seeder.ts | 2 +- src/common/databases/seeders/seeder.ts | 2 +- .../repositories/DocumentTypesRepository.ts | 4 +- src/common/repositories/OfficesRepository.ts | 2 + .../common/BucketService/BucketService.ts | 77 +++++++++++ .../notary/OfficesService/OfficesService.ts | 12 +- src/test/config/Init.ts | 2 +- 15 files changed, 236 insertions(+), 7 deletions(-) create mode 100644 src/app/api/notary/BucketController.ts create mode 100644 src/common/databases/migrations/20240215123932_rib/migration.sql create mode 100644 src/common/databases/migrations/20240215142816_rib_optional/migration.sql create mode 100644 src/common/databases/migrations/20240216104224_rib_name/migration.sql create mode 100644 src/services/common/BucketService/BucketService.ts diff --git a/package.json b/package.json index 1cb06594..b9393cad 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "@prisma/client": "^4.11.0", "@sentry/node": "^7.91.0", "adm-zip": "^0.5.10", + "aws-sdk": "^2.1556.0", "axios": "^1.6.2", "bcrypt": "^5.1.1", "class-transformer": "^0.5.1", @@ -58,7 +59,7 @@ "file-type-checker": "^1.0.8", "fp-ts": "^2.16.1", "jsonwebtoken": "^9.0.0", - "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.108", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.115", "module-alias": "^2.2.2", "monocle-ts": "^2.3.13", "multer": "^1.4.5-lts.1", diff --git a/src/app/api/notary/BucketController.ts b/src/app/api/notary/BucketController.ts new file mode 100644 index 00000000..24c4f2ac --- /dev/null +++ b/src/app/api/notary/BucketController.ts @@ -0,0 +1,126 @@ +import { Response, Request } from "express"; +import { Controller, Delete, Get, Post } from "@ControllerPattern/index"; +import ApiController from "@Common/system/controller-pattern/ApiController"; + +import { Service } from "typedi"; +import BucketService from "@Services/common/BucketService/BucketService"; +import authHandler from "@App/middlewares/AuthHandler"; +import OfficesService from "@Services/notary/OfficesService/OfficesService"; +import { Office as OfficeResource } from "le-coffre-resources/dist/Notary"; + +@Controller() +@Service() +export default class BucketController extends ApiController { + constructor(private bucketService: BucketService, private officesService: OfficesService) { + super(); + } + + @Get("/api/v1/notary/bucket/:uid", [authHandler]) + protected async getRibStream(req: Request, response: Response) { + const uid = req.params["uid"]; + if (!uid) { + this.httpBadRequest(response, "No uid provided"); + return; + } + + const office = await this.officesService.getByUid(uid, {address: true}); + + if(!office) { + this.httpNotFoundRequest(response, "Office not found"); + return; + } + + const fileName = office.rib_name; + if(!fileName) { + return; + } + + try { + const file = await this.bucketService.getByUid(uid, fileName); + response.attachment(fileName); + response.send(file.Body); + } catch (error) { + this.httpInternalError(response, error); + } + } + + @Post("/api/v1/notary/bucket", [authHandler]) + protected async post(req: Request, response: Response) { + try { + const officeId: string = req.body.user.office_Id + if (!req.file) throw new Error("No file provided"); + if (!officeId) throw new Error("No officeId provided"); + + const office = await this.officesService.getByUid(officeId, {address: true}); + + if (!office) { + this.httpNotFoundRequest(response, "office not found"); + return; + } + const fileUrl = await this.bucketService.createOrUpdate(officeId, req.file); + + if(!fileUrl || !req.file.originalname ) throw new Error("Error while uploading file"); + + office.rib_url = fileUrl; + office.rib_name = req.file.originalname; + + const officeEntity = OfficeResource.hydrate(office, { + strategy: "excludeAll", + }); + + //call service to get prisma entity + const officeEntityUpdated = await this.officesService.update(officeId, officeEntity); + //Hydrate ressource with prisma entity + const officeUpdated = OfficeResource.hydrate(officeEntityUpdated, { + strategy: "excludeAll", + }); + + //success + this.httpCreated(response, officeUpdated); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } + + @Delete("/api/v1/notary/bucket/:uid", [authHandler]) + protected async delete(req: Request, response: Response) { + try { + const officeId: string = req.body.user.office_Id + if (!officeId) throw new Error("No officeId provided"); + + const office = await this.officesService.getByUid(officeId, {address: true}); + console.log(office); + + + if (!office) { + this.httpNotFoundRequest(response, "office not found"); + return; + } + + const fileName = office.rib_name; + + await this.bucketService.delete(officeId, fileName!); + + office.rib_url = null; + office.rib_name = null; + + const officeEntity = OfficeResource.hydrate(office, { + strategy: "excludeAll", + }); + + //call service to get prisma entity + const officeEntityUpdated = await this.officesService.update(officeId, officeEntity); + //Hydrate ressource with prisma entity + const officeUpdated = OfficeResource.hydrate(officeEntityUpdated, { + strategy: "excludeAll", + }); + + //success + this.httpCreated(response, officeUpdated); + } catch (error) { + this.httpInternalError(response, error); + return; + } + } +} diff --git a/src/app/api/notary/OfficesController.ts b/src/app/api/notary/OfficesController.ts index ec418381..d738ca99 100644 --- a/src/app/api/notary/OfficesController.ts +++ b/src/app/api/notary/OfficesController.ts @@ -69,6 +69,7 @@ export default class OfficesController extends ApiController { } const officeEntity = await this.officesService.getByUid(uid, query); + if (!officeEntity) { this.httpNotFoundRequest(response, "office not found"); @@ -77,6 +78,8 @@ export default class OfficesController extends ApiController { //Hydrate ressource with prisma entity const office = OfficeResource.hydrate(officeEntity, { strategy: "excludeAll" }); + + //success this.httpSuccess(response, office); } catch (error) { diff --git a/src/app/index.ts b/src/app/index.ts index 2de03a00..3646fda0 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -46,6 +46,7 @@ import DocumentControllerId360 from "./api/id360/DocumentController"; import CustomerControllerId360 from "./api/id360/CustomerController"; import UserNotificationController from "./api/notary/UserNotificationController"; import AuthController from "./api/customer/AuthController"; +import BucketController from "./api/notary/BucketController"; /** * @description This allow to declare all controllers used in the application @@ -100,5 +101,6 @@ export default { Container.get(DocumentControllerId360); Container.get(CustomerControllerId360); Container.get(AuthController); + Container.get(BucketController); }, }; diff --git a/src/common/databases/migrations/20240215123932_rib/migration.sql b/src/common/databases/migrations/20240215123932_rib/migration.sql new file mode 100644 index 00000000..119d529e --- /dev/null +++ b/src/common/databases/migrations/20240215123932_rib/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "offices" ADD COLUMN "rib_url" VARCHAR(255) DEFAULT ''; diff --git a/src/common/databases/migrations/20240215142816_rib_optional/migration.sql b/src/common/databases/migrations/20240215142816_rib_optional/migration.sql new file mode 100644 index 00000000..20107944 --- /dev/null +++ b/src/common/databases/migrations/20240215142816_rib_optional/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "offices" ALTER COLUMN "rib_url" DROP DEFAULT; diff --git a/src/common/databases/migrations/20240216104224_rib_name/migration.sql b/src/common/databases/migrations/20240216104224_rib_name/migration.sql new file mode 100644 index 00000000..f733246c --- /dev/null +++ b/src/common/databases/migrations/20240216104224_rib_name/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "offices" ADD COLUMN "rib_name" VARCHAR(255); diff --git a/src/common/databases/schema.prisma b/src/common/databases/schema.prisma index de4afe97..06b7518b 100644 --- a/src/common/databases/schema.prisma +++ b/src/common/databases/schema.prisma @@ -92,6 +92,8 @@ model Offices { created_at DateTime? @default(now()) updated_at DateTime? @updatedAt checked_at DateTime? + rib_url String? @db.VarChar(255) + rib_name String? @db.VarChar(255) deed_types DeedTypes[] users Users[] office_folders OfficeFolders[] diff --git a/src/common/databases/seeders/prod-seeder.ts b/src/common/databases/seeders/prod-seeder.ts index b2e816bf..b6151603 100644 --- a/src/common/databases/seeders/prod-seeder.ts +++ b/src/common/databases/seeders/prod-seeder.ts @@ -1264,7 +1264,7 @@ export default async function main() { const documentTypeCreated = await prisma.documentTypes.create({ data: { name: documentType.name, - public_description: documentType.public_description, + public_description: documentType.public_description || "", private_description: documentType.private_description, office: { connect: { diff --git a/src/common/databases/seeders/seeder.ts b/src/common/databases/seeders/seeder.ts index 2e7b3f30..ce7e4cdf 100644 --- a/src/common/databases/seeders/seeder.ts +++ b/src/common/databases/seeders/seeder.ts @@ -1975,7 +1975,7 @@ export default async function main() { const documentTypeCreated = await prisma.documentTypes.create({ data: { name: documentType.name, - public_description: documentType.public_description, + public_description: documentType.public_description || "", private_description: documentType.private_description, office: { connect: { diff --git a/src/common/repositories/DocumentTypesRepository.ts b/src/common/repositories/DocumentTypesRepository.ts index 221e541f..790412b9 100644 --- a/src/common/repositories/DocumentTypesRepository.ts +++ b/src/common/repositories/DocumentTypesRepository.ts @@ -30,7 +30,7 @@ export default class DocumentTypesRepository extends BaseRepository { const createArgs: Prisma.DocumentTypesCreateArgs = { data: { name: documentType.name, - public_description: documentType.public_description, + public_description: documentType.public_description || "", private_description: documentType.private_description, office: { connect: { @@ -52,7 +52,7 @@ export default class DocumentTypesRepository extends BaseRepository { }, data: { name: documentType.name, - public_description: documentType.public_description, + public_description: documentType.public_description || "", private_description: documentType.private_description, archived_at: documentType.archived_at, }, diff --git a/src/common/repositories/OfficesRepository.ts b/src/common/repositories/OfficesRepository.ts index fb763880..a937bac0 100644 --- a/src/common/repositories/OfficesRepository.ts +++ b/src/common/repositories/OfficesRepository.ts @@ -67,6 +67,8 @@ export default class OfficesRepository extends BaseRepository { }, }, office_status: EOfficeStatus[office.office_status as keyof typeof EOfficeStatus], + rib_url: office.rib_url, + rib_name: office.rib_name, }, }; return this.model.update(updateArgs); diff --git a/src/services/common/BucketService/BucketService.ts b/src/services/common/BucketService/BucketService.ts new file mode 100644 index 00000000..ff29737a --- /dev/null +++ b/src/services/common/BucketService/BucketService.ts @@ -0,0 +1,77 @@ +import BaseService from "@Services/BaseService"; +import { Service } from "typedi"; +import * as AWS from "aws-sdk"; +import { BackendVariables } from "@Common/config/variables/Variables"; +import path from "path"; + +// Configure the AWS SDK for Scaleway +const s3 = new AWS.S3({ + accessKeyId: "SCWZ39ZVQWXFC7HA0647", + secretAccessKey: "59bcf27d-bee3-4d14-8b4d-03fd6a8be6cd", + endpoint: "https://lecoffre-bucket.s3.fr-par.scw.cloud", // Use the appropriate Scaleway endpoint + s3ForcePathStyle: true, // Needed for Scaleway's S3-compatible API + signatureVersion: "v4", +}); + +@Service() +export default class BucketService extends BaseService { + constructor(private variables: BackendVariables) { + super(); + } + + public async download() { + + } + + public async getByUid(uid: string, fileName: string) { + const key = path.join(this.variables.ENV, uid, fileName); + + return new Promise(async (resolve, reject) => { + s3.getObject( + { + Bucket: "lecoffre-bucket", + Key: key, + }, + function (err, data) { + if (err) return reject(err); + resolve(data); + }, + ); + }); + } + + public async createOrUpdate(officeId: string, file: Express.Multer.File) { + const key = path.join(this.variables.ENV, officeId, file.originalname); + + const uploadParams = { + Bucket: "lecoffre-bucket", + Key: key, // Example: 'example.txt' + Body: file.buffer, // Example: fs.createReadStream('/path/to/file') + ACL: "public-read", // Optional: Set the ACL if needed + }; + + return new Promise((resolve, reject) => { + s3.putObject(uploadParams, function (err, data) { + if (err) return reject(err); + resolve(`https://lecoffre-bucket.s3.fr-par.scw.cloud/lecoffre-bucket/${key}`); + }); + }); + } + + public async delete(officeId: string, fileName: string) { + const key = path.join(this.variables.ENV, officeId, fileName); + + return new Promise(async (resolve, reject) => { + s3.getObject( + { + Bucket: "lecoffre-bucket", + Key: key, + }, + function (err, data) { + if (err) return reject(err); + resolve(data); + }, + ); + }); + } +} diff --git a/src/services/notary/OfficesService/OfficesService.ts b/src/services/notary/OfficesService/OfficesService.ts index af25083b..af0bb8dd 100644 --- a/src/services/notary/OfficesService/OfficesService.ts +++ b/src/services/notary/OfficesService/OfficesService.ts @@ -1,7 +1,9 @@ -import { Prisma } from "@prisma/client"; +import { Offices, Prisma } from "@prisma/client"; import OfficesRepository from "@Repositories/OfficesRepository"; import BaseService from "@Services/BaseService"; import { Service } from "typedi"; +import { Office as OfficeRessource } from "le-coffre-resources/dist/Notary"; + @Service() export default class OfficesService extends BaseService { @@ -24,4 +26,12 @@ export default class OfficesService extends BaseService { public async getByUid(uid: string, query?: Prisma.OfficesInclude) { return this.officeRepository.findOneByUid(uid, query); } + + /** + * @description : Modify an office + * @throws {Error} If office cannot be modified + */ + public async update(uid: string, officeEntity: OfficeRessource): Promise { + return this.officeRepository.update(uid, officeEntity); + } } diff --git a/src/test/config/Init.ts b/src/test/config/Init.ts index 4541cb77..919f934d 100644 --- a/src/test/config/Init.ts +++ b/src/test/config/Init.ts @@ -36,7 +36,7 @@ export const initDocumentType = (documentType: DocumentType, office: Office): Pr return prisma.documentTypes.create({ data: { name: documentType.name, - public_description: documentType.public_description, + public_description: documentType.public_description || "" , private_description: documentType.private_description, archived_at: null, office_uid: office.uid!, From 42c72713fbe4ab4aaa94c1d00b7a889003c25fc3 Mon Sep 17 00:00:00 2001 From: Vins Date: Fri, 16 Feb 2024 16:00:07 +0100 Subject: [PATCH 2/7] Fixed no file error --- src/app/api/notary/BucketController.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/app/api/notary/BucketController.ts b/src/app/api/notary/BucketController.ts index 24c4f2ac..555e6d5d 100644 --- a/src/app/api/notary/BucketController.ts +++ b/src/app/api/notary/BucketController.ts @@ -32,12 +32,13 @@ export default class BucketController extends ApiController { const fileName = office.rib_name; if(!fileName) { + this.httpNotFoundRequest(response, "No file found"); return; } try { - const file = await this.bucketService.getByUid(uid, fileName); - response.attachment(fileName); + const file = await this.bucketService.getByUid(uid, fileName!); + response.attachment(fileName!); response.send(file.Body); } catch (error) { this.httpInternalError(response, error); From ecc0342155ada88e23830d823a149d9105eb7273 Mon Sep 17 00:00:00 2001 From: Vins Date: Fri, 16 Feb 2024 17:09:21 +0100 Subject: [PATCH 3/7] Fixed --- src/app/api/customer/OfficeRibController.ts | 43 ++++++++++++++++++ ...etController.ts => OfficeRibController.ts} | 45 +++++++++---------- src/app/api/notary/OfficesController.ts | 4 +- src/app/index.ts | 6 ++- src/app/uuidMatcher.ts | 2 + .../OfficeRibService.ts} | 7 +-- .../customer/OfficesService/OfficesService.ts | 28 ++++++++++++ 7 files changed, 102 insertions(+), 33 deletions(-) create mode 100644 src/app/api/customer/OfficeRibController.ts rename src/app/api/notary/{BucketController.ts => OfficeRibController.ts} (72%) create mode 100644 src/app/uuidMatcher.ts rename src/services/common/{BucketService/BucketService.ts => OfficeRibService/OfficeRibService.ts} (95%) create mode 100644 src/services/customer/OfficesService/OfficesService.ts diff --git a/src/app/api/customer/OfficeRibController.ts b/src/app/api/customer/OfficeRibController.ts new file mode 100644 index 00000000..f11538c8 --- /dev/null +++ b/src/app/api/customer/OfficeRibController.ts @@ -0,0 +1,43 @@ +import { Response, Request } from "express"; +import { Controller, Get } from "@ControllerPattern/index"; +import ApiController from "@Common/system/controller-pattern/ApiController"; + +import { Service } from "typedi"; +import OfficerRibService from "@Services/common/OfficeRibService/OfficeRibService"; +import authHandler from "@App/middlewares/AuthHandler"; +import OfficesService from "@Services/customer/OfficesService/OfficesService"; + +@Controller() +@Service() +export default class OfficeRibController extends ApiController { + constructor(private officeRibService: OfficerRibService, private officesService: OfficesService) { + super(); + } + + @Get("/api/v1/customer/office/:uid/rib", [authHandler]) + protected async getRibStream(req: Request, response: Response) { + const officeId = req.params["uid"]; + if (!officeId) throw new Error("No officeId provided"); + + const office = await this.officesService.getByUid(officeId, { address: true }); + + if (!office) { + this.httpNotFoundRequest(response, "Office not found"); + return; + } + + const fileName = office.rib_name; + if (!fileName) { + this.httpNotFoundRequest(response, "No file found"); + return; + } + + try { + const file = await this.officeRibService.getByUid(officeId, fileName!); + response.attachment(fileName!); + response.send(file.Body); + } catch (error) { + this.httpInternalError(response, error); + } + } +} diff --git a/src/app/api/notary/BucketController.ts b/src/app/api/notary/OfficeRibController.ts similarity index 72% rename from src/app/api/notary/BucketController.ts rename to src/app/api/notary/OfficeRibController.ts index 555e6d5d..d7330ad3 100644 --- a/src/app/api/notary/BucketController.ts +++ b/src/app/api/notary/OfficeRibController.ts @@ -3,41 +3,38 @@ import { Controller, Delete, Get, Post } from "@ControllerPattern/index"; import ApiController from "@Common/system/controller-pattern/ApiController"; import { Service } from "typedi"; -import BucketService from "@Services/common/BucketService/BucketService"; +import OfficerRibService from "@Services/common/OfficeRibService/OfficeRibService"; import authHandler from "@App/middlewares/AuthHandler"; import OfficesService from "@Services/notary/OfficesService/OfficesService"; import { Office as OfficeResource } from "le-coffre-resources/dist/Notary"; @Controller() @Service() -export default class BucketController extends ApiController { - constructor(private bucketService: BucketService, private officesService: OfficesService) { +export default class OfficeRibController extends ApiController { + constructor(private officeRibService: OfficerRibService, private officesService: OfficesService) { super(); } - @Get("/api/v1/notary/bucket/:uid", [authHandler]) + @Get("/api/v1/notary/office/rib", [authHandler]) protected async getRibStream(req: Request, response: Response) { - const uid = req.params["uid"]; - if (!uid) { - this.httpBadRequest(response, "No uid provided"); - return; - } + const officeId: string = req.body.user.office_Id; + if (!officeId) throw new Error("No officeId provided"); - const office = await this.officesService.getByUid(uid, {address: true}); - - if(!office) { + const office = await this.officesService.getByUid(officeId, { address: true }); + + if (!office) { this.httpNotFoundRequest(response, "Office not found"); return; } const fileName = office.rib_name; - if(!fileName) { + if (!fileName) { this.httpNotFoundRequest(response, "No file found"); return; } try { - const file = await this.bucketService.getByUid(uid, fileName!); + const file = await this.officeRibService.getByUid(officeId, fileName!); response.attachment(fileName!); response.send(file.Body); } catch (error) { @@ -45,22 +42,22 @@ export default class BucketController extends ApiController { } } - @Post("/api/v1/notary/bucket", [authHandler]) + @Post("/api/v1/notary/office/rib", [authHandler]) protected async post(req: Request, response: Response) { try { - const officeId: string = req.body.user.office_Id + const officeId: string = req.body.user.office_Id; if (!req.file) throw new Error("No file provided"); if (!officeId) throw new Error("No officeId provided"); - const office = await this.officesService.getByUid(officeId, {address: true}); + const office = await this.officesService.getByUid(officeId, { address: true }); if (!office) { this.httpNotFoundRequest(response, "office not found"); return; } - const fileUrl = await this.bucketService.createOrUpdate(officeId, req.file); + const fileUrl = await this.officeRibService.createOrUpdate(officeId, req.file); - if(!fileUrl || !req.file.originalname ) throw new Error("Error while uploading file"); + if (!fileUrl || !req.file.originalname) throw new Error("Error while uploading file"); office.rib_url = fileUrl; office.rib_name = req.file.originalname; @@ -84,15 +81,13 @@ export default class BucketController extends ApiController { } } - @Delete("/api/v1/notary/bucket/:uid", [authHandler]) + @Delete("/api/v1/notary/office/rib", [authHandler]) protected async delete(req: Request, response: Response) { try { - const officeId: string = req.body.user.office_Id + const officeId: string = req.body.user.office_Id; if (!officeId) throw new Error("No officeId provided"); - const office = await this.officesService.getByUid(officeId, {address: true}); - console.log(office); - + const office = await this.officesService.getByUid(officeId, { address: true }); if (!office) { this.httpNotFoundRequest(response, "office not found"); @@ -101,7 +96,7 @@ export default class BucketController extends ApiController { const fileName = office.rib_name; - await this.bucketService.delete(officeId, fileName!); + await this.officeRibService.delete(officeId, fileName!); office.rib_url = null; office.rib_name = null; diff --git a/src/app/api/notary/OfficesController.ts b/src/app/api/notary/OfficesController.ts index d738ca99..d7dac131 100644 --- a/src/app/api/notary/OfficesController.ts +++ b/src/app/api/notary/OfficesController.ts @@ -7,6 +7,7 @@ import { Offices, Prisma } from "@prisma/client"; import { Office as OfficeResource } from "le-coffre-resources/dist/Notary"; import ruleHandler from "@App/middlewares/RulesHandler"; import authHandler from "@App/middlewares/AuthHandler"; +import uuidMatcher from "@App/uuidMatcher"; @Controller() @Service() @@ -49,8 +50,9 @@ export default class OfficesController extends ApiController { /** * @description Get a specific office by uid + * `/:id(${uuidMatcher})` */ - @Get("/api/v1/notary/offices/:uid", [authHandler, ruleHandler]) + @Get(`/api/v1/notary/offices/:uid(${uuidMatcher})`, [authHandler, ruleHandler]) protected async getOneByUid(req: Request, response: Response) { try { const uid = req.params["uid"]; diff --git a/src/app/index.ts b/src/app/index.ts index 3646fda0..510d903d 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -46,7 +46,8 @@ import DocumentControllerId360 from "./api/id360/DocumentController"; import CustomerControllerId360 from "./api/id360/CustomerController"; import UserNotificationController from "./api/notary/UserNotificationController"; import AuthController from "./api/customer/AuthController"; -import BucketController from "./api/notary/BucketController"; +import NotaryOfficeRibController from "./api/notary/OfficeRibController"; +import CustomerOfficeRibController from "./api/customer/OfficeRibController"; /** * @description This allow to declare all controllers used in the application @@ -101,6 +102,7 @@ export default { Container.get(DocumentControllerId360); Container.get(CustomerControllerId360); Container.get(AuthController); - Container.get(BucketController); + Container.get(NotaryOfficeRibController); + Container.get(CustomerOfficeRibController); }, }; diff --git a/src/app/uuidMatcher.ts b/src/app/uuidMatcher.ts new file mode 100644 index 00000000..558fc1f3 --- /dev/null +++ b/src/app/uuidMatcher.ts @@ -0,0 +1,2 @@ +const uuidMatcher = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}"; +export default uuidMatcher; \ No newline at end of file diff --git a/src/services/common/BucketService/BucketService.ts b/src/services/common/OfficeRibService/OfficeRibService.ts similarity index 95% rename from src/services/common/BucketService/BucketService.ts rename to src/services/common/OfficeRibService/OfficeRibService.ts index ff29737a..535125bc 100644 --- a/src/services/common/BucketService/BucketService.ts +++ b/src/services/common/OfficeRibService/OfficeRibService.ts @@ -14,18 +14,15 @@ const s3 = new AWS.S3({ }); @Service() -export default class BucketService extends BaseService { +export default class OfficerRibService extends BaseService { constructor(private variables: BackendVariables) { super(); } - public async download() { - - } - public async getByUid(uid: string, fileName: string) { const key = path.join(this.variables.ENV, uid, fileName); + return new Promise(async (resolve, reject) => { s3.getObject( { diff --git a/src/services/customer/OfficesService/OfficesService.ts b/src/services/customer/OfficesService/OfficesService.ts new file mode 100644 index 00000000..8c7091d2 --- /dev/null +++ b/src/services/customer/OfficesService/OfficesService.ts @@ -0,0 +1,28 @@ +import {Prisma } from "@prisma/client"; +import OfficesRepository from "@Repositories/OfficesRepository"; +import BaseService from "@Services/BaseService"; +import { Service } from "typedi"; + + +@Service() +export default class OfficesService extends BaseService { + constructor(private officeRepository: OfficesRepository) { + super(); + } + + /** + * @description : Get all offices + * @throws {Error} If offices cannot be get + */ + public async get(query: Prisma.OfficesFindManyArgs) { + return this.officeRepository.findMany(query); + } + + /** + * @description : Get a office by uid + * @throws {Error} If office cannot be get + */ + public async getByUid(uid: string, query?: Prisma.OfficesInclude) { + return this.officeRepository.findOneByUid(uid, query); + } +} From dffe813968eb2998142708d5811825a51fee4b9c Mon Sep 17 00:00:00 2001 From: Vins Date: Mon, 19 Feb 2024 11:14:33 +0100 Subject: [PATCH 4/7] Finished --- src/common/config/variables/Variables.ts | 16 ++++++++ .../common/IdNotService/IdNotService.ts | 1 - .../OfficeRibService/OfficeRibService.ts | 38 +++++++++---------- 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/common/config/variables/Variables.ts b/src/common/config/variables/Variables.ts index 49ad7c2f..10df52e7 100644 --- a/src/common/config/variables/Variables.ts +++ b/src/common/config/variables/Variables.ts @@ -130,6 +130,18 @@ export class BackendVariables { @IsNotEmpty() public readonly SMS_FACTOR_TOKEN!: string; + @IsNotEmpty() + public readonly SCW_ACCESS_KEY_ID!: string; + + @IsNotEmpty() + public readonly SCW_ACCESS_KEY_SECRET!: string; + + @IsNotEmpty() + public readonly SCW_BUCKET_ENDPOINT!: string; + + @IsNotEmpty() + public readonly SCW_BUCKET_NAME!: string; + public constructor() { dotenv.config(); this.DATABASE_PORT = process.env["DATABASE_PORT"]!; @@ -174,6 +186,10 @@ export class BackendVariables { this.OVH_CONSUMER_KEY = process.env["OVH_CONSUMER_KEY"]!; this.OVH_SMS_SERVICE_NAME = process.env["OVH_SMS_SERVICE_NAME"]!; this.SMS_FACTOR_TOKEN = process.env["SMS_FACTOR_TOKEN"]!; + this.SCW_ACCESS_KEY_ID = process.env["SCW_ACCESS_KEY_ID"]!; + this.SCW_ACCESS_KEY_SECRET = process.env["SCW_ACCESS_KEY_SECRET"]!; + this.SCW_BUCKET_ENDPOINT = process.env["SCW_BUCKET_ENDPOINT"]!; + this.SCW_BUCKET_NAME = process.env["SCW_BUCKET_NAME"]!; } public async validate(groups?: string[]) { const validationOptions = groups ? { groups } : undefined; diff --git a/src/services/common/IdNotService/IdNotService.ts b/src/services/common/IdNotService/IdNotService.ts index b9157b6d..047868af 100644 --- a/src/services/common/IdNotService/IdNotService.ts +++ b/src/services/common/IdNotService/IdNotService.ts @@ -121,7 +121,6 @@ export default class IdNotService extends BaseService { code: code, grant_type: "authorization_code", }); - console.log(this.variables.IDNOT_BASE_URL + this.variables.IDNOT_CONNEXION_URL + "?" + query.toString()); const token = await fetch(this.variables.IDNOT_BASE_URL + this.variables.IDNOT_CONNEXION_URL + "?" + query, { method: "POST" }); if(token.status !== 200) console.error(await token.text()); diff --git a/src/services/common/OfficeRibService/OfficeRibService.ts b/src/services/common/OfficeRibService/OfficeRibService.ts index 535125bc..5ada46a8 100644 --- a/src/services/common/OfficeRibService/OfficeRibService.ts +++ b/src/services/common/OfficeRibService/OfficeRibService.ts @@ -4,29 +4,29 @@ import * as AWS from "aws-sdk"; import { BackendVariables } from "@Common/config/variables/Variables"; import path from "path"; -// Configure the AWS SDK for Scaleway -const s3 = new AWS.S3({ - accessKeyId: "SCWZ39ZVQWXFC7HA0647", - secretAccessKey: "59bcf27d-bee3-4d14-8b4d-03fd6a8be6cd", - endpoint: "https://lecoffre-bucket.s3.fr-par.scw.cloud", // Use the appropriate Scaleway endpoint - s3ForcePathStyle: true, // Needed for Scaleway's S3-compatible API - signatureVersion: "v4", -}); - @Service() export default class OfficerRibService extends BaseService { + private readonly s3: AWS.S3; constructor(private variables: BackendVariables) { super(); + + // Configure the AWS SDK for Scaleway + this.s3 = new AWS.S3({ + accessKeyId: this.variables.SCW_ACCESS_KEY_ID, + secretAccessKey: this.variables.SCW_ACCESS_KEY_SECRET, + endpoint: this.variables.SCW_BUCKET_ENDPOINT, // Use the appropriate Scaleway endpoint + s3ForcePathStyle: true, // Needed for Scaleway's S3-compatible API + signatureVersion: "v4", + }); } public async getByUid(uid: string, fileName: string) { const key = path.join(this.variables.ENV, uid, fileName); - - + return new Promise(async (resolve, reject) => { - s3.getObject( + this.s3.getObject( { - Bucket: "lecoffre-bucket", + Bucket: this.variables.SCW_BUCKET_NAME, Key: key, }, function (err, data) { @@ -34,21 +34,21 @@ export default class OfficerRibService extends BaseService { resolve(data); }, ); - }); + }); } public async createOrUpdate(officeId: string, file: Express.Multer.File) { const key = path.join(this.variables.ENV, officeId, file.originalname); const uploadParams = { - Bucket: "lecoffre-bucket", + Bucket: this.variables.SCW_BUCKET_NAME, Key: key, // Example: 'example.txt' Body: file.buffer, // Example: fs.createReadStream('/path/to/file') ACL: "public-read", // Optional: Set the ACL if needed }; return new Promise((resolve, reject) => { - s3.putObject(uploadParams, function (err, data) { + this.s3.putObject(uploadParams, function (err, data) { if (err) return reject(err); resolve(`https://lecoffre-bucket.s3.fr-par.scw.cloud/lecoffre-bucket/${key}`); }); @@ -59,9 +59,9 @@ export default class OfficerRibService extends BaseService { const key = path.join(this.variables.ENV, officeId, fileName); return new Promise(async (resolve, reject) => { - s3.getObject( + this.s3.getObject( { - Bucket: "lecoffre-bucket", + Bucket: this.variables.SCW_BUCKET_NAME, Key: key, }, function (err, data) { @@ -69,6 +69,6 @@ export default class OfficerRibService extends BaseService { resolve(data); }, ); - }); + }); } } From af9d593f7dfcd722009be207c388f483e0ae7126 Mon Sep 17 00:00:00 2001 From: Vins Date: Mon, 19 Feb 2024 15:05:07 +0100 Subject: [PATCH 5/7] Bug et retours --- package.json | 2 +- src/common/databases/seeders/prod-seeder.ts | 2 +- src/common/databases/seeders/seeder.ts | 2 +- src/common/repositories/DeedTypesRepository.ts | 4 ++-- src/test/config/Init.ts | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index b9393cad..a6f832c1 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "file-type-checker": "^1.0.8", "fp-ts": "^2.16.1", "jsonwebtoken": "^9.0.0", - "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.115", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.119", "module-alias": "^2.2.2", "monocle-ts": "^2.3.13", "multer": "^1.4.5-lts.1", diff --git a/src/common/databases/seeders/prod-seeder.ts b/src/common/databases/seeders/prod-seeder.ts index b6151603..30cfe1af 100644 --- a/src/common/databases/seeders/prod-seeder.ts +++ b/src/common/databases/seeders/prod-seeder.ts @@ -1280,7 +1280,7 @@ export default async function main() { const createArgs: Prisma.DeedTypesCreateArgs = { data: { name: deedType.name, - description: deedType.description, + description: deedType.description || "" , office: { connect: { uid: deedType.office!.uid, diff --git a/src/common/databases/seeders/seeder.ts b/src/common/databases/seeders/seeder.ts index ce7e4cdf..c1feda5e 100644 --- a/src/common/databases/seeders/seeder.ts +++ b/src/common/databases/seeders/seeder.ts @@ -1991,7 +1991,7 @@ export default async function main() { const createArgs: Prisma.DeedTypesCreateArgs = { data: { name: deedType.name, - description: deedType.description, + description: deedType.description || "", office: { connect: { uid: deedType.office!.uid, diff --git a/src/common/repositories/DeedTypesRepository.ts b/src/common/repositories/DeedTypesRepository.ts index 43495cbe..b89ab362 100644 --- a/src/common/repositories/DeedTypesRepository.ts +++ b/src/common/repositories/DeedTypesRepository.ts @@ -31,7 +31,7 @@ export default class DeedTypesRepository extends BaseRepository { const createArgs: Prisma.DeedTypesCreateArgs = { data: { name: deedType.name, - description: deedType.description, + description: deedType.description || "", office: { connect: { uid: deedType.office!.uid, @@ -59,7 +59,7 @@ export default class DeedTypesRepository extends BaseRepository { }, data: { name: deedType.name, - description: deedType.description, + description: deedType.description || "", archived_at: deedType.archived_at, document_types: { set: deedType.document_types?.map((documentType) => ({ diff --git a/src/test/config/Init.ts b/src/test/config/Init.ts index 919f934d..27be6340 100644 --- a/src/test/config/Init.ts +++ b/src/test/config/Init.ts @@ -48,7 +48,7 @@ export const initDeedType = (deedType: DeedType): Promise => { const createArgs: Prisma.DeedTypesCreateArgs = { data: { name: deedType.name, - description: deedType.description, + description: deedType.description || "", office: { connect: { uid: deedType.office!.uid, From fe657129a7226c26c17d189c60ececda7410c205 Mon Sep 17 00:00:00 2001 From: Vins Date: Tue, 20 Feb 2024 09:30:31 +0100 Subject: [PATCH 6/7] Fix Rules + rules PUT rib --- src/app/api/admin/OfficeRolesController.ts | 11 +++++++++-- src/common/databases/seeders/seeder.ts | 7 +++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/app/api/admin/OfficeRolesController.ts b/src/app/api/admin/OfficeRolesController.ts index 5226a859..cb3162bd 100644 --- a/src/app/api/admin/OfficeRolesController.ts +++ b/src/app/api/admin/OfficeRolesController.ts @@ -116,12 +116,19 @@ export default class OfficeRolesController extends ApiController { if (req.body.rules) { const allRules = await this.rulesService.get({ where: { - namespace: "notary", + OR: [ + { + namespace: "notary", + }, + { + namespace: "collaborator", + }, + ], }, }); req.body.rules = req.body.rules.filter((rule: any) => { - const ruleFound = allRules.find((r) => r.uid === rule.uid && r.namespace === "notary"); + const ruleFound = allRules.find((r) => r.uid === rule.uid && (r.namespace === "notary" || r.namespace === "collaborator")); return ruleFound; }); } diff --git a/src/common/databases/seeders/seeder.ts b/src/common/databases/seeders/seeder.ts index c1feda5e..d8f1339c 100644 --- a/src/common/databases/seeders/seeder.ts +++ b/src/common/databases/seeders/seeder.ts @@ -788,6 +788,13 @@ export default async function main() { updated_at: new Date(), namespace: "super-admin", }, + { + name: "PUT rib", + label: "Editer le RIB de l'office", + created_at: new Date(), + updated_at: new Date(), + namespace: "notary", + }, ]; const collaboratorRules = rules.filter((rule) => rule.namespace === "collaborator"); From ba667f73f5936ea08aaceb8b4bf2688b9ae5d96c Mon Sep 17 00:00:00 2001 From: Vins Date: Tue, 20 Feb 2024 14:16:17 +0100 Subject: [PATCH 7/7] Finished --- src/app/api/notary/OfficeRibController.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/api/notary/OfficeRibController.ts b/src/app/api/notary/OfficeRibController.ts index d7330ad3..3a0fb9ae 100644 --- a/src/app/api/notary/OfficeRibController.ts +++ b/src/app/api/notary/OfficeRibController.ts @@ -35,7 +35,8 @@ export default class OfficeRibController extends ApiController { try { const file = await this.officeRibService.getByUid(officeId, fileName!); - response.attachment(fileName!); + response.attachment(fileName!); + response.setHeader("Access-Control-Expose-Headers", "Content-Disposition"); response.send(file.Body); } catch (error) { this.httpInternalError(response, error);