diff --git a/package.json b/package.json index 0f87ee00..01c62d97 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "express": "^4.18.2", "fp-ts": "^2.16.1", "jsonwebtoken": "^9.0.0", - "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.84", + "le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.86", "module-alias": "^2.2.2", "monocle-ts": "^2.3.13", "multer": "^1.4.5-lts.1", diff --git a/src/app/api/admin/CustomersController.ts b/src/app/api/admin/CustomersController.ts index 90e429cf..0094a678 100644 --- a/src/app/api/admin/CustomersController.ts +++ b/src/app/api/admin/CustomersController.ts @@ -8,6 +8,7 @@ import { validateOrReject } from "class-validator"; import authHandler from "@App/middlewares/AuthHandler"; import ruleHandler from "@App/middlewares/RulesHandler"; import roleHandler from "@App/middlewares/RolesHandler"; +import { Prisma } from "@prisma/client"; @Controller() @Service() @@ -28,6 +29,12 @@ export default class CustomersController extends ApiController { query = JSON.parse(req.query["q"] as string); } + const officeId: string = req.body.user.office_Id; + if(query.where?.office_folders?.some?.office_uid) delete query.where.office_folders.some.office_uid; + if(query.where?.office_folders?.some?.office?.uid) delete query.where?.office_folders?.some?.office?.uid; + const customerWhereInput: Prisma.CustomersWhereInput = { ...query.where, office_folders: { some: { office_uid: officeId } }}; + query.where = customerWhereInput; + //call service to get prisma entity const customersEntities = await this.customersService.get(query); diff --git a/src/app/api/admin/OfficeRolesController.ts b/src/app/api/admin/OfficeRolesController.ts index 5c06e471..bd989779 100644 --- a/src/app/api/admin/OfficeRolesController.ts +++ b/src/app/api/admin/OfficeRolesController.ts @@ -10,11 +10,12 @@ import authHandler from "@App/middlewares/AuthHandler"; import ruleHandler from "@App/middlewares/RulesHandler"; import officeRoleHandler from "@App/middlewares/OfficeMembershipHandlers/OfficeRoleHandler"; import roleHandler from "@App/middlewares/RolesHandler"; +import RulesService from "@Services/admin/RulesService/RulesService"; @Controller() @Service() export default class OfficeRolesController extends ApiController { - constructor(private officeRolesService: OfficeRolesService) { + constructor(private officeRolesService: OfficeRolesService, private rulesService: RulesService) { super(); } @@ -30,7 +31,7 @@ export default class OfficeRolesController extends ApiController { query = JSON.parse(req.query["q"] as string); } - if(req.query["search"] && typeof req.query["search"] === "string") { + if (req.query["search"] && typeof req.query["search"] === "string") { const filter = req.query["search"]; query = { where: { @@ -39,7 +40,7 @@ export default class OfficeRolesController extends ApiController { mode: "insensitive", }, }, - } + }; } const officeId: string = req.body.user.office_Id; @@ -108,11 +109,23 @@ export default class OfficeRolesController extends ApiController { return; } + if (req.body.rules) { + const allRules = await this.rulesService.get({ + where: { + namespace: "notary", + }, + }); + + req.body.rules = req.body.rules.filter((rule: any) => { + const ruleFound = allRules.find((r) => r.uid === rule.uid && r.namespace === "notary"); + return ruleFound; + }); + } //init IOfficeRole resource with request body values const officeRoleEntity = OfficeRole.hydrate(req.body); //validate officeRole - await validateOrReject(officeRoleEntity, { groups: ["updateOfficeRole"] }); + await validateOrReject(officeRoleEntity, { groups: ["updateOfficeRole"] }); //call service to get prisma entity const officeRoleEntityUpdated = await this.officeRolesService.update(officeRoleEntity); diff --git a/src/app/api/customer/CustomersController.ts b/src/app/api/customer/CustomersController.ts deleted file mode 100644 index f958ab7e..00000000 --- a/src/app/api/customer/CustomersController.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { Response, Request } from "express"; -import { Controller, Get } from "@ControllerPattern/index"; -import ApiController from "@Common/system/controller-pattern/ApiController"; -import CustomersService from "@Services/customer/CustomersService/CustomersService"; -import { Service } from "typedi"; -import Customer from "le-coffre-resources/dist/Customer"; -import authHandler from "@App/middlewares/AuthHandler"; -import ruleHandler from "@App/middlewares/RulesHandler"; - -@Controller() -@Service() -export default class CustomersController extends ApiController { - constructor(private customersService: CustomersService) { - super(); - } - - /** - * @description Get all customers - */ - @Get("/api/v1/customer/customers") - protected async get(req: Request, response: Response) { - try { - //get query - let query; - if (req.query["q"]) { - query = JSON.parse(req.query["q"] as string); - } - - - //call service to get prisma entity - const customersEntities = await this.customersService.get(query); - - //Hydrate ressource with prisma entity - const customers = Customer.hydrateArray(customersEntities, { strategy: "excludeAll" }); - - //success - this.httpSuccess(response, customers); - } catch (error) { - this.httpInternalError(response, error); - return; - } - } - - - /** - * @description Get a specific customer by uid - */ - @Get("/api/v1/customer/customers/:uid", [authHandler, ruleHandler]) - protected async getOneByUid(req: Request, response: Response) { - try { - const uid = req.params["uid"]; - if (!uid) { - this.httpBadRequest(response, "No uid provided"); - return; - } - - let query; - if (req.query["q"]) { - query = JSON.parse(req.query["q"] as string); - } - - const customerEntity = await this.customersService.getByUid(uid, query); - - if (!customerEntity) { - this.httpNotFoundRequest(response, "customer not found"); - return; - } - - //Hydrate ressource with prisma entity - const customer = Customer.hydrate(customerEntity, { strategy: "excludeAll" }); - - //success - this.httpSuccess(response, customer); - } catch (error) { - this.httpInternalError(response, error); - return; - } - } -} diff --git a/src/app/api/customer/OfficeFoldersController.ts b/src/app/api/customer/OfficeFoldersController.ts index e524b445..7e44ef33 100644 --- a/src/app/api/customer/OfficeFoldersController.ts +++ b/src/app/api/customer/OfficeFoldersController.ts @@ -5,6 +5,8 @@ import OfficeFoldersService from "@Services/customer/OfficeFoldersService/Office import { Service } from "typedi"; import { OfficeFolders, Prisma } from "@prisma/client"; import { OfficeFolder } from "le-coffre-resources/dist/Customer"; +import officeFolderHandler from "@App/middlewares/CustomerHandler/FolderHandler"; +import authHandler from "@App/middlewares/AuthHandler"; // import authHandler from "@App/middlewares/AuthHandler"; // import ruleHandler from "@App/middlewares/RulesHandler"; // import folderHandler from "@App/middlewares/OfficeMembershipHandlers/FolderHandler"; @@ -19,7 +21,7 @@ export default class OfficeFoldersController extends ApiController { /** * @description Get all folders */ - @Get("/api/v1/customer/folders") + @Get("/api/v1/customer/folders", [authHandler]) protected async get(req: Request, response: Response) { try { //get query @@ -28,37 +30,14 @@ export default class OfficeFoldersController extends ApiController { query = JSON.parse(req.query["q"] as string); } - - if (req.query["search"] && typeof req.query["search"] === "string") { - const filter = req.query["search"]; - query = { - where: { - OR: [ - { - name: { contains: filter, mode: "insensitive" }, - }, - { - folder_number: { contains: filter, mode: "insensitive" }, - }, - { - customers: { - some: { - contact: { - OR: [ - { first_name: { contains: filter, mode: "insensitive" } }, - { last_name: { contains: filter, mode: "insensitive" } }, - ], - }, - }, - }, - }, - ], - }, - }; + const customerId: string = req.body.user.customerId; + if(!customerId) { + this.httpBadRequest(response, "No customerId provided"); + return; } - const officeWhereInput: Prisma.OfficesWhereInput = {}; - if (!query.where) query.where = { office: officeWhereInput }; - query.where.office = officeWhereInput; + if(query.where?.customers) delete query.where.customers; + const officeFolderWhereInput: Prisma.OfficeFoldersWhereInput = { ...query.where, customers: { some: { uid: customerId } }}; + query.where = officeFolderWhereInput; //call service to get prisma entity const officeFolderEntities: OfficeFolders[] = await this.officeFoldersService.get(query); @@ -79,7 +58,7 @@ export default class OfficeFoldersController extends ApiController { * @description Get a specific folder by uid * @returns IFolder */ - @Get("/api/v1/customer/folders/:uid") + @Get("/api/v1/customer/folders/:uid", [authHandler, officeFolderHandler]) protected async getOneByUid(req: Request, response: Response) { try { const uid = req.params["uid"]; @@ -91,8 +70,10 @@ export default class OfficeFoldersController extends ApiController { let query; if (req.query["q"]) { query = JSON.parse(req.query["q"] as string); + if(query?.customers) { + query.customers = true; + } } - const officeFolderEntity = await this.officeFoldersService.getByUid(uid, query); if (!officeFolderEntity) { diff --git a/src/app/api/notary/CustomersController.ts b/src/app/api/notary/CustomersController.ts index 59d070c7..af756207 100644 --- a/src/app/api/notary/CustomersController.ts +++ b/src/app/api/notary/CustomersController.ts @@ -7,6 +7,7 @@ import { Customer } from "le-coffre-resources/dist/Notary"; import { validateOrReject } from "class-validator"; import authHandler from "@App/middlewares/AuthHandler"; import ruleHandler from "@App/middlewares/RulesHandler"; +import { Prisma } from "@prisma/client"; @Controller() @Service() @@ -22,11 +23,17 @@ export default class CustomersController extends ApiController { protected async get(req: Request, response: Response) { try { //get query - let query; + let query: Prisma.CustomersFindManyArgs = {}; if (req.query["q"]) { query = JSON.parse(req.query["q"] as string); } + const officeId: string = req.body.user.office_Id; + if(query.where?.office_folders?.some?.office_uid) delete query.where.office_folders.some.office_uid; + if(query.where?.office_folders?.some?.office?.uid) delete query.where?.office_folders?.some?.office?.uid; + const customerWhereInput: Prisma.CustomersWhereInput = { ...query.where, office_folders: { some: { office_uid: officeId } }}; + query.where = customerWhereInput; + //call service to get prisma entity const customersEntities = await this.customersService.get(query); diff --git a/src/app/api/notary/OfficeFolderAnchorsController.ts b/src/app/api/notary/OfficeFolderAnchorsController.ts index 29f9e0ea..9c4c7fdf 100644 --- a/src/app/api/notary/OfficeFolderAnchorsController.ts +++ b/src/app/api/notary/OfficeFolderAnchorsController.ts @@ -5,15 +5,35 @@ import { Service } from "typedi"; import { OfficeFolder } from "le-coffre-resources/dist/Notary"; import { getFolderHashes } from "@Common/optics/notary"; import OfficeFoldersService from "@Services/notary/OfficeFoldersService/OfficeFoldersService"; +import OfficeFolderAnchorsRepository from "@Repositories/OfficeFolderAnchorsRepository"; import SecureService from "@Services/common/SecureService/SecureService"; import authHandler from "@App/middlewares/AuthHandler"; import ruleHandler from "@App/middlewares/RulesHandler"; import folderHandler from "@App/middlewares/OfficeMembershipHandlers/FolderHandler"; +import OfficeFolderAnchor from "le-coffre-resources/dist/Notary/OfficeFolderAnchor"; +const hydrateOfficeFolderAnchor = (data: any): OfficeFolderAnchor => + OfficeFolderAnchor.hydrate( + { + hash_sources: data.hash_sources, + root_hash: data.root_hash, + + blockchain: data.transactions[0].blockchain, + status: data.transactions[0].status, + + anchor_nb_try: data.transactions[0].anchor_nb_try, + tx_id: data.transactions[0].tx_id.toString(), + tx_link: data.transactions[0].tx_link, + tx_hash: data.transactions[0].tx_hash, + + anchored_at: data.transactions[0].anchoring_timestamp, + }, + { strategy: "excludeAll" }, + ); @Controller() @Service() export default class OfficeFoldersController extends ApiController { - constructor(private secureService: SecureService, private officeFoldersService: OfficeFoldersService) { + constructor(private secureService: SecureService, private officeFolderAnchorsRepository: OfficeFolderAnchorsRepository, private officeFoldersService: OfficeFoldersService) { super(); } @@ -83,16 +103,30 @@ export default class OfficeFoldersController extends ApiController { files: true, }, }, + folder_anchor: true, }; - const officeFolderFound = await this.officeFoldersService.getByUid(uid, query); + const officeFolderFound: any = await this.officeFoldersService.getByUid(uid, query); if (!officeFolderFound) { this.httpNotFoundRequest(response, "Office folder not found"); return; } + const officeFolderAnchorFound = OfficeFolderAnchor.hydrate(officeFolderFound.folder_anchor, { + strategy: "excludeAll", + }); + + if (officeFolderAnchorFound) { + this.httpBadRequest(response, { + error: "Office folder already anchored", + folder_anchor: officeFolderAnchorFound, + }); + return; + } + const officeFolder = OfficeFolder.hydrate(officeFolderFound, { strategy: "excludeAll" }); + const folderHashes = getFolderHashes(officeFolder); if (folderHashes.length === 0) { @@ -101,9 +135,20 @@ export default class OfficeFoldersController extends ApiController { } const sortedHashes = [...folderHashes].sort(); - const anchor = await this.secureService.anchor(sortedHashes); + const data = await this.secureService.anchor(sortedHashes); - this.httpSuccess(response, anchor); + const officeFolderAnchor = hydrateOfficeFolderAnchor(data); + + const newOfficeFolderAnchor = await this.officeFolderAnchorsRepository.create( + officeFolderAnchor + ); + + await this.officeFoldersService.update( + uid, + OfficeFolder.hydrate({ uid: uid, folder_anchor: newOfficeFolderAnchor }, { strategy: "excludeAll" }), + ); + + this.httpSuccess(response, officeFolderAnchor); } catch (error) { this.httpInternalError(response, error); return; @@ -129,9 +174,10 @@ export default class OfficeFoldersController extends ApiController { files: true, }, }, + folder_anchor: true, }; - const officeFolderFound = await this.officeFoldersService.getByUid(uid, query); + const officeFolderFound: any = await this.officeFoldersService.getByUid(uid, query); if (!officeFolderFound) { this.httpNotFoundRequest(response, "Office folder not found"); @@ -147,9 +193,31 @@ export default class OfficeFoldersController extends ApiController { } const sortedHashes = [...folderHashes].sort(); - const anchor = await this.secureService.verify(sortedHashes); + const officeFolderAnchorFound = OfficeFolderAnchor.hydrate(officeFolderFound.folder_anchor, { + strategy: "excludeAll", + }); - this.httpSuccess(response, anchor); + if (!officeFolderAnchorFound || !officeFolderAnchorFound.uid) { + this.httpNotFoundRequest(response, {error: "Not anchored", hash_sources: sortedHashes}); + return; + } + + const data = await this.secureService.verify(sortedHashes); + + if (data.errors || data.transactions.length === 0) { + this.httpNotFoundRequest(response, {error: "Not anchored", hash_sources: sortedHashes}); + return; + } + + const officeFolderAnchor = hydrateOfficeFolderAnchor(data); + + const updatedOfficeFolderAnchor = await this.officeFolderAnchorsRepository.update( + officeFolderAnchorFound.uid, + officeFolderAnchor + ); + + this.httpSuccess(response, updatedOfficeFolderAnchor); + return; } catch (error) { this.httpInternalError(response, error); return; diff --git a/src/app/api/notary/RulesController.ts b/src/app/api/notary/RulesController.ts index 63d29165..435a27c1 100644 --- a/src/app/api/notary/RulesController.ts +++ b/src/app/api/notary/RulesController.ts @@ -26,6 +26,10 @@ export default class RulesController extends ApiController { query = JSON.parse(req.query["q"] as string); } + query.where = { + ...query.where, + namespace: "notary", + }; //call service to get prisma entity const rulesEntities = await this.rulesService.get(query); @@ -57,6 +61,11 @@ export default class RulesController extends ApiController { query = JSON.parse(req.query["q"] as string); } + query.where = { + ...query.where, + namespace: "notary", + }; + const ruleEntity = await this.rulesService.getByUid(uid, query); if (!ruleEntity) { diff --git a/src/app/api/common/UserNotificationController.ts b/src/app/api/notary/UserNotificationController.ts similarity index 79% rename from src/app/api/common/UserNotificationController.ts rename to src/app/api/notary/UserNotificationController.ts index eec01d06..1654df3b 100644 --- a/src/app/api/common/UserNotificationController.ts +++ b/src/app/api/notary/UserNotificationController.ts @@ -5,6 +5,7 @@ import { Service } from "typedi"; import UserNotification from "le-coffre-resources/dist/Notary/UserNotification"; import UserNotificationService from "@Services/common/UserNotificationService/UserNotificationService"; import authHandler from "@App/middlewares/AuthHandler"; +import { Prisma } from "@prisma/client"; @Controller() @Service() @@ -16,7 +17,7 @@ export default class UserNotificationController extends ApiController { /** * @description Get all customers */ - @Get("/api/v1/notifications", [authHandler]) + @Get("/api/v1/notary/notifications", [authHandler]) protected async get(req: Request, response: Response) { try { //get query @@ -25,11 +26,13 @@ export default class UserNotificationController extends ApiController { query = JSON.parse(req.query["q"] as string); } - if (query.where) { - query.where = { ...query.where, user: { uid: req.body.user.uid } }; - } else { - query.where = { user: { uid: req.body.user.uid } }; - } + + const userId: string = req.body.user.userId; + if(query.where?.user_uid) delete query.where.user_uid; + if(query.where?.user?.uid) delete query.where.user.uid; + const notificationWhereInput: Prisma.UserNotificationsWhereInput = { ...query.where, user_uid: userId }; + query.where = notificationWhereInput; + query.include = { notification: true }; //call service to get prisma entity const userNotificationEntities = await this.userNotificationService.get(query); @@ -48,7 +51,7 @@ export default class UserNotificationController extends ApiController { /** * @description Modify a specific customer by uid */ - @Put("/api/v1/notifications/:uid") + @Put("/api/v1/notary/notifications/:uid", [authHandler]) protected async put(req: Request, response: Response) { try { const uid = req.params["uid"]; @@ -64,6 +67,11 @@ export default class UserNotificationController extends ApiController { return; } + if(userNotificationFound.user_uid !== req.body.user.userId) { + this.httpForbidden(response, "You are not allowed to update this user notification"); + return; + } + //init IUser resource with request body values const userNotificationEntity = UserNotification.hydrate(req.body); @@ -86,7 +94,7 @@ export default class UserNotificationController extends ApiController { /** * @description Get a specific customer by uid */ - @Get("/api/v1/notifications/:uid") + @Get("/api/v1/notary/notifications/:uid", [authHandler]) protected async getOneByUid(req: Request, response: Response) { try { const uid = req.params["uid"]; @@ -108,6 +116,11 @@ export default class UserNotificationController extends ApiController { return; } + if(userNotificationEntity.user_uid !== req.body.userId) { + this.httpForbidden(response, "You are allowed to get this user notification"); + return; + } + //Hydrate ressource with prisma entity const userNotification = UserNotification.hydrate(userNotificationEntity, { strategy: "excludeAll" }); diff --git a/src/app/api/super-admin/CustomersController.ts b/src/app/api/super-admin/CustomersController.ts index d1a15fd2..62a82548 100644 --- a/src/app/api/super-admin/CustomersController.ts +++ b/src/app/api/super-admin/CustomersController.ts @@ -8,6 +8,7 @@ import { validateOrReject } from "class-validator"; import authHandler from "@App/middlewares/AuthHandler"; import ruleHandler from "@App/middlewares/RulesHandler"; import roleHandler from "@App/middlewares/RolesHandler"; +import { Prisma } from "@prisma/client"; @Controller() @Service() @@ -28,6 +29,12 @@ export default class CustomersController extends ApiController { query = JSON.parse(req.query["q"] as string); } + const officeId: string = req.body.user.office_Id; + if(query.where?.office_folders?.some?.office_uid) delete query.where.office_folders.some.office_uid; + if(query.where?.office_folders?.some?.office?.uid) delete query.where?.office_folders?.some?.office?.uid; + const customerWhereInput: Prisma.CustomersWhereInput = { ...query.where, office_folders: { some: { office_uid: officeId } }}; + query.where = customerWhereInput; + //call service to get prisma entity const customersEntities = await this.customersService.get(query); diff --git a/src/app/index.ts b/src/app/index.ts index fd205cf5..092417a4 100644 --- a/src/app/index.ts +++ b/src/app/index.ts @@ -42,14 +42,12 @@ import FilesControllerCustomer from "./api/customer/FilesController"; import DocumentsControllerCustomer from "./api/customer/DocumentsController"; import OfficeFoldersController from "./api/customer/OfficeFoldersController"; import OfficeFolderAnchorsController from "./api/notary/OfficeFolderAnchorsController"; -import CustomersController from "./api/customer/CustomersController"; import AppointmentsController from "./api/super-admin/AppointmentsController"; import VotesController from "./api/super-admin/VotesController"; import LiveVoteController from "./api/super-admin/LiveVoteController"; import DocumentControllerId360 from "./api/id360/DocumentController"; import CustomerControllerId360 from "./api/id360/CustomerController"; - -import UserNotificationController from "./api/common/UserNotificationController"; +import UserNotificationController from "./api/notary/UserNotificationController"; /** @@ -105,7 +103,6 @@ export default { Container.get(DocumentsControllerCustomer); Container.get(OfficeFoldersController); Container.get(OfficeFolderAnchorsController); - Container.get(CustomersController); Container.get(UserNotificationController); Container.get(DocumentControllerId360); Container.get(CustomerControllerId360); diff --git a/src/app/middlewares/CustomerHandler/FolderHandler.ts b/src/app/middlewares/CustomerHandler/FolderHandler.ts new file mode 100644 index 00000000..aaf6ad54 --- /dev/null +++ b/src/app/middlewares/CustomerHandler/FolderHandler.ts @@ -0,0 +1,24 @@ +import HttpCodes from "@Common/system/controller-pattern/HttpCodes"; +import OfficeFoldersService from "@Services/customer/OfficeFoldersService/OfficeFoldersService"; +import { NextFunction, Request, Response } from "express"; +import Container from "typedi"; + +export default async function officeFolderHandler(req: Request, response: Response, next: NextFunction) { + const customerId = req.body.user.customerId; + const uid = req.path && req.path.split("/")[5]; + + if (uid) { + const officeFolderService = Container.get(OfficeFoldersService); + const officeFolder = await officeFolderService.getByUidWithCustomers(uid); + if (!officeFolder) { + response.status(HttpCodes.NOT_FOUND).send("Folder not found"); + return; + } + if (!officeFolder.customers.find((customer) => customer.uid == customerId)) { + response.status(HttpCodes.UNAUTHORIZED).send("Not authorized with this depositor"); + return; + } + } + + next(); +} diff --git a/src/common/databases/migrations/20230929113629_rule_namespace/migration.sql b/src/common/databases/migrations/20230929113629_rule_namespace/migration.sql new file mode 100644 index 00000000..0eeea98b --- /dev/null +++ b/src/common/databases/migrations/20230929113629_rule_namespace/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "rules" ADD COLUMN "namespace" VARCHAR(255) NOT NULL DEFAULT 'notary'; diff --git a/src/common/databases/schema.prisma b/src/common/databases/schema.prisma index a2120220..113f741d 100644 --- a/src/common/databases/schema.prisma +++ b/src/common/databases/schema.prisma @@ -299,7 +299,7 @@ model Rules { updated_at DateTime? @updatedAt role Roles[] @relation("RolesHasRules") office_roles OfficeRoles[] @relation("OfficeRolesHasRules") - + namespace String @db.VarChar(255) @default("notary") @@map("rules") } diff --git a/src/common/databases/seeders/seeder.ts b/src/common/databases/seeders/seeder.ts index 54db8b54..744af3dc 100644 --- a/src/common/databases/seeders/seeder.ts +++ b/src/common/databases/seeders/seeder.ts @@ -19,7 +19,6 @@ export default async function main() { try { const prisma = new PrismaClient(); - const idNot2 = "jelkvelknvlkn"; const idNot3 = "rleenrenlnr"; const idNot4 = "ljfeflecnmd"; const idNot5 = "rflrefrjf"; @@ -231,9 +230,9 @@ export default async function main() { }, { address: addresses[5], - first_name: "Thibault", - last_name: "Dubois", - email: "thibault.dubois@outlook.com", + first_name: "Paul", + last_name: "Dupont", + email: "paul.dupont@outlook.com", phone_number: "06 67 89 01 23", cell_phone_number: "06 67 89 01 23", birthdate: null, @@ -243,9 +242,9 @@ export default async function main() { }, { address: addresses[6], - first_name: "Léa", - last_name: "Fontaine", - email: "lea.fontaine@gmail.com", + first_name: "Jean", + last_name: "Dubigot", + email: "jean.dubigot@gmail.com", phone_number: "06 78 90 12 34", cell_phone_number: "06 78 90 12 34", birthdate: null, @@ -255,9 +254,9 @@ export default async function main() { }, { address: addresses[7], - first_name: "Guillaume", - last_name: "Renaud", - email: "guillaume.renaud@gmail.com", + first_name: "Vincent", + last_name: "Martin", + email: "vincent.martin@gmail.com", phone_number: "06 89 01 23 45", cell_phone_number: "06 89 01 23 45", birthdate: null, @@ -424,6 +423,12 @@ export default async function main() { ]; const customers: Customer[] = [ + { + contact: contacts[0], + created_at: new Date(), + updated_at: new Date(), + status: ECustomerStatus.PENDING, + }, { contact: contacts[5], created_at: new Date(), @@ -522,259 +527,302 @@ export default async function main() { label: "Lecture des utilisateurs", created_at: new Date(), updated_at: new Date(), + namespace: "collaborator", }, { name: "GET offices", - label: "Lecture des offices", + label: "Afficher des offices", created_at: new Date(), updated_at: new Date(), + namespace: "collaborator", }, { name: "GET customers", - label: "Lecture des clients", + label: "Afficher des clients", created_at: new Date(), updated_at: new Date(), + namespace: "collaborator", }, { name: "GET deeds", - label: "Lecture des actes", + label: "Voir des types d'acte", created_at: new Date(), updated_at: new Date(), + namespace: "collaborator", }, { name: "GET deed-types", label: "Lecture des types d'actes", created_at: new Date(), updated_at: new Date(), + namespace: "collaborator", }, { name: "GET documents", - label: "Lecture des documents", + label: "Afficher des documents", created_at: new Date(), updated_at: new Date(), + namespace: "collaborator", }, { name: "GET document-types", label: "Lecture des types de documents", created_at: new Date(), updated_at: new Date(), + namespace: "collaborator", }, { name: "GET files", label: "Lecture des fichiers", created_at: new Date(), updated_at: new Date(), + namespace: "collaborator", }, { name: "GET folders", - label: "Lecture des dossiers", + label: "Afficher les dossiers", created_at: new Date(), updated_at: new Date(), + namespace: "collaborator", }, { name: "GET roles", - label: "Lecture utilisateurs", + label: "Afficher les rôles", created_at: new Date(), updated_at: new Date(), + namespace: "collaborator", }, { name: "GET rules", - label: "Lecture des droits", + label: "Afficher les droits", created_at: new Date(), updated_at: new Date(), + namespace: "collaborator", }, { name: "GET office-roles", label: "Lecture des rôles d'office", created_at: new Date(), updated_at: new Date(), + namespace: "collaborator", }, { name: "POST deeds", - label: "Création des actes", + label: "Créer un template de type d'acte", created_at: new Date(), updated_at: new Date(), + namespace: "notary", }, { name: "PUT deeds", - label: "Modification des actes", + label: "Modifier un type d'acte", created_at: new Date(), updated_at: new Date(), + namespace: "notary", }, { name: "DELETE deeds", - label: "Suppression des actes", + label: "Supprimer des types d'actes", created_at: new Date(), updated_at: new Date(), + namespace: "notary", }, { name: "POST folders", - label: "Création des dossiers", + label: "Créer un dossier", created_at: new Date(), updated_at: new Date(), + namespace: "notary", }, { name: "PUT folders", - label: "Modification des dossiers", + label: "Modifier des dossiers", created_at: new Date(), updated_at: new Date(), + namespace: "notary", }, { name: "DELETE folders", - label: "Suppression des dossiers", + label: "Supprimer un dossier vide", created_at: new Date(), updated_at: new Date(), + namespace: "notary", }, { name: "POST documents", - label: "Création des documents", + label: "Demander des documents à un client", created_at: new Date(), updated_at: new Date(), + namespace: "notary", }, { name: "PUT documents", - label: "Modification des documents", + label: "Valider des documents", created_at: new Date(), updated_at: new Date(), + namespace: "notary", }, { name: "DELETE documents", - label: "Suppression des documents", + label: "Supprimer un document", created_at: new Date(), updated_at: new Date(), + namespace: "super-admin", }, { name: "POST customers", - label: "Création des clients", + label: "Créer des clients", created_at: new Date(), updated_at: new Date(), + namespace: "notary", }, { name: "PUT customers", - label: "Modification des clients", + label: "Modifier des clients", created_at: new Date(), updated_at: new Date(), + namespace: "notary", }, { name: "DELETE customers", - label: "Suppression des clients", + label: "Supprimer un client", created_at: new Date(), updated_at: new Date(), + namespace: "super-admin", }, { name: "POST anchors", label: "Ancrer un dossier", created_at: new Date(), updated_at: new Date(), + namespace: "notary", }, { name: "GET anchors", label: "Vérifier l'ancrage un dossier", created_at: new Date(), updated_at: new Date(), + namespace: "notary", }, { name: "POST deed-types", label: "Création des types d'actes", created_at: new Date(), updated_at: new Date(), + namespace: "admin", }, { name: "PUT deed-types", label: "Modification des types d'actes", created_at: new Date(), updated_at: new Date(), + namespace: "admin", }, { name: "DELETE deed-types", label: "Suppression des types d'actes", created_at: new Date(), updated_at: new Date(), + namespace: "admin", }, { name: "POST document-types", label: "Création des types de documents", created_at: new Date(), updated_at: new Date(), + namespace: "admin", }, { name: "PUT document-types", label: "Modification des types de documents", created_at: new Date(), updated_at: new Date(), + namespace: "admin", }, { name: "DELETE document-types", label: "Suppression des types de documents", created_at: new Date(), updated_at: new Date(), + namespace: "admin", }, { name: "POST office-roles", label: "Création des rôles d'office", created_at: new Date(), updated_at: new Date(), + namespace: "admin", }, { name: "PUT office-roles", label: "Modification des rôles d'office", created_at: new Date(), updated_at: new Date(), + namespace: "admin", }, { name: "DELETE office-roles", label: "Suppression des rôles d'office", created_at: new Date(), updated_at: new Date(), - }, - { - name: "DELETE office-roles", - label: "Suppression des rôles d'office", - created_at: new Date(), - updated_at: new Date(), + namespace: "admin", }, { name: "PUT users", label: "Modification des utilisateurs", created_at: new Date(), updated_at: new Date(), + namespace: "admin", + }, + { + name: "DELETE office-roles", + label: "Suppression des rôles d'office", + created_at: new Date(), + updated_at: new Date(), + namespace: "super-admin", }, { name: "PUT offices", label: "Modification des offices", created_at: new Date(), updated_at: new Date(), + namespace: "super-admin", }, ]; + const collaboratorRules = rules.filter((rule) => rule.namespace === "collaborator"); + const notaryRules = [...collaboratorRules, ...rules.filter((rule) => rule.namespace === "notary")]; + const adminRules = [...notaryRules, ...rules.filter((rule) => rule.namespace === "admin")]; + const superAdminRules = [...adminRules, ...rules.filter((rule) => rule.namespace === "super-admin")]; + const roles: Role[] = [ { name: "super-admin", label: "Super administrateur", created_at: new Date(), updated_at: new Date(), - rules: rules, + rules: superAdminRules, }, { name: "admin", label: "Administrateur", created_at: new Date(), updated_at: new Date(), - rules: rules.slice(0, 35), + rules: adminRules, }, { name: "notary", label: "Notaire", created_at: new Date(), updated_at: new Date(), - rules: rules.slice(0, 25), + rules: [], }, { name: "default", label: "Utilisateur", created_at: new Date(), updated_at: new Date(), - rules: rules.slice(0,1), + rules: [], }, ]; @@ -784,26 +832,18 @@ export default async function main() { created_at: new Date(), updated_at: new Date(), office: offices[0]!, - rules: rules.slice(0, 35), + rules: notaryRules, }, { name: "Collaborateur", created_at: new Date(), updated_at: new Date(), office: offices[0]!, - rules: rules.slice(0, 22), + rules: notaryRules, }, ]; const users: User[] = [ - { - created_at: new Date(), - updated_at: new Date(), - idNot: idNot2, - contact: contacts[0], - office_membership: offices[0], - role: roles[0], - }, { created_at: new Date(), updated_at: new Date(), @@ -1320,6 +1360,7 @@ export default async function main() { data: { name: rule.name, label: rule.label, + namespace: rule.namespace, }, }); rule.uid = ruleCreated.uid; diff --git a/src/common/notifications/NotificationBuilder.ts b/src/common/notifications/NotificationBuilder.ts index 732d1c73..0e8b9eb3 100644 --- a/src/common/notifications/NotificationBuilder.ts +++ b/src/common/notifications/NotificationBuilder.ts @@ -5,6 +5,7 @@ import { Service } from "typedi"; import NotificationsService from "@Services/common/NotificationsService/NotificationsService"; import UsersService from "@Services/super-admin/UsersService/UsersService"; import OfficeFoldersService from "@Services/super-admin/OfficeFoldersService/OfficeFoldersService"; +import { BackendVariables } from "@Common/config/variables/Variables"; @Service() export default class NotificationBuilder { @@ -13,6 +14,7 @@ export default class NotificationBuilder { private documentsService: DocumentsService, private usersService: UsersService, private foldersService: OfficeFoldersService, + private backendVariables: BackendVariables ) {} public async sendDocumentDepositedNotification(documentEntity: Documents) { @@ -32,7 +34,7 @@ export default class NotificationBuilder { " " + document.depositor?.contact?.last_name + " vous a envoyé un document à valider", - redirection_url: "", + redirection_url: `${this.backendVariables.APP_HOST}/folders/${document.folder?.uid}/documents/${document.uid}`, created_at: new Date(), updated_at: new Date(), user: document.folder!.stakeholders || [], @@ -55,7 +57,7 @@ export default class NotificationBuilder { " - " + document.folder?.name + " a été certifié. Vous pouvez désormais télécharger le certificat de dépôt pour le mettre dans la GED de votre logiciel de rédaction d'acte.", - redirection_url: "", + redirection_url: `${this.backendVariables.APP_HOST}/folders/${document.folder?.uid}/documents/${document.uid}`, created_at: new Date(), updated_at: new Date(), user: document.folder!.stakeholders || [], @@ -80,7 +82,7 @@ export default class NotificationBuilder { this.notificationsService.create({ message: message, - redirection_url: "", + redirection_url: `${this.backendVariables.APP_HOST}/users/${vote.appointment.targeted_user.uid}`, created_at: new Date(), updated_at: new Date(), user: superAdminList || [], @@ -131,7 +133,7 @@ export default class NotificationBuilder { " avant le " + formattedDate + " (date limite d'expiration des documents).", - redirection_url: "", + redirection_url: `${this.backendVariables.APP_HOST}/folders/${folder.uid}`, created_at: new Date(), updated_at: new Date(), user: folder.stakeholders || [], diff --git a/src/common/repositories/OfficeFolderAnchorsRepository.ts b/src/common/repositories/OfficeFolderAnchorsRepository.ts new file mode 100644 index 00000000..c0687017 --- /dev/null +++ b/src/common/repositories/OfficeFolderAnchorsRepository.ts @@ -0,0 +1,68 @@ +import Database from "@Common/databases/database"; +import BaseRepository from "@Repositories/BaseRepository"; +import { Service } from "typedi"; +import { OfficeFolderAnchors, Prisma } from "@prisma/client"; +import { OfficeFolderAnchor } from "le-coffre-resources/dist/SuperAdmin"; + +@Service() +export default class OfficeFolderAnchorsRepository extends BaseRepository { + constructor(private database: Database) { + super(); + } + protected get model() { + return this.database.getClient().officeFolderAnchors; + } + protected get instanceDb() { + return this.database.getClient(); + } + + /** + * @description : Create new office folder anchor + */ + public async create(officeFolderAnchor: OfficeFolderAnchor): Promise { + const createArgs: Prisma.OfficeFolderAnchorsCreateArgs = { + data: { + hash_sources: officeFolderAnchor.hash_sources, + root_hash: officeFolderAnchor.root_hash!, + + blockchain: officeFolderAnchor.blockchain as OfficeFolderAnchors["blockchain"], + status: officeFolderAnchor.status as OfficeFolderAnchors["status"], + + anchor_nb_try: officeFolderAnchor.anchor_nb_try, + tx_id: officeFolderAnchor.tx_id, + tx_link: officeFolderAnchor.tx_link, + tx_hash: officeFolderAnchor.tx_hash, + + anchored_at: officeFolderAnchor.anchored_at, + }, + }; + + return this.model.create({ ...createArgs }); + } + + /** + * @description : Update data of an office folder anchor + */ + public async update(officeFolderAnchorUid: string, officeFolderAnchor: OfficeFolderAnchor): Promise { + const updateArgs: Prisma.OfficeFolderAnchorsUpdateArgs = { + where: { + uid: officeFolderAnchorUid, + }, + data: { + blockchain: officeFolderAnchor.blockchain as OfficeFolderAnchors["blockchain"], + status: officeFolderAnchor.status as OfficeFolderAnchors["status"], + + anchor_nb_try: officeFolderAnchor.anchor_nb_try, + tx_id: officeFolderAnchor.tx_id, + tx_link: officeFolderAnchor.tx_link, + tx_hash: officeFolderAnchor.tx_hash, + + anchored_at: officeFolderAnchor.anchored_at, + }, + }; + + return this.model.update({ + ...updateArgs, + }); + } +} diff --git a/src/common/repositories/OfficeFoldersRepository.ts b/src/common/repositories/OfficeFoldersRepository.ts index d391ef92..5d591906 100644 --- a/src/common/repositories/OfficeFoldersRepository.ts +++ b/src/common/repositories/OfficeFoldersRepository.ts @@ -84,6 +84,7 @@ export default class OfficeFoldersRepository extends BaseRepository { uid: document.uid!, })), }, + folder_anchor_uid: officeFolder.folder_anchor?.uid, }, }; @@ -93,6 +94,7 @@ export default class OfficeFoldersRepository extends BaseRepository { stakeholders: true, customers: true, documents: true, + folder_anchor: true, }, }); } @@ -109,6 +111,20 @@ export default class OfficeFoldersRepository extends BaseRepository { }); } + /** + * @description : Find one office folder + */ + public async findOneByUidWithCustomers(uid: string) { + return this.model.findUnique({ + where: { + uid: uid, + }, + include: { + customers: true, + } + }); + } + /** * @description : Find one office folder */ diff --git a/src/common/repositories/RulesRepository.ts b/src/common/repositories/RulesRepository.ts index 281e505b..aa44b819 100644 --- a/src/common/repositories/RulesRepository.ts +++ b/src/common/repositories/RulesRepository.ts @@ -31,7 +31,8 @@ export default class RulesRepository extends BaseRepository { const createArgs: Prisma.RulesCreateArgs = { data: { name: rule.name, - label: rule.label + label: rule.label, + namespace: rule.namespace, }, }; @@ -48,7 +49,7 @@ export default class RulesRepository extends BaseRepository { }, data: { name: rule.name, - label: rule.label + label: rule.label, }, }; diff --git a/src/common/system/controller-pattern/BaseController.ts b/src/common/system/controller-pattern/BaseController.ts index 39bd4be7..7baa9c02 100644 --- a/src/common/system/controller-pattern/BaseController.ts +++ b/src/common/system/controller-pattern/BaseController.ts @@ -40,6 +40,10 @@ export default abstract class BaseController { return this.httpResponse(response, HttpCodes.NOT_IMPLEMENTED, responseData); } + protected httpForbidden(response: Response, responseData: IResponseData = "Forbidden") { + return this.httpResponse(response, HttpCodes.FORBIDDEN, responseData); + } + protected httpResponse(response: Response, httpCode: HttpCodes, responseData: IResponseData = {}) { if (responseData instanceof Error) { throw responseData; diff --git a/src/common/system/controller-pattern/HttpCodes.ts b/src/common/system/controller-pattern/HttpCodes.ts index 648c30b1..95c4a67d 100644 --- a/src/common/system/controller-pattern/HttpCodes.ts +++ b/src/common/system/controller-pattern/HttpCodes.ts @@ -8,5 +8,6 @@ enum HttpCodes { NOT_IMPLEMENTED = 501, NOT_FOUND = 404, UNAUTHORIZED = 401, + FORBIDDEN = 403, } export default HttpCodes; diff --git a/src/services/common/AnchoringProofService/AnchoringProofService.ts b/src/services/common/AnchoringProofService/AnchoringProofService.ts index 82c13dcd..b1af4a2d 100644 --- a/src/services/common/AnchoringProofService/AnchoringProofService.ts +++ b/src/services/common/AnchoringProofService/AnchoringProofService.ts @@ -53,7 +53,7 @@ export default class AnchoringProofService extends BaseService { [[ANCHORING_TIME]] Certificat de dépôt international Hash : - b834ce9229ee2c2283c837685772a473 + [[ROOT_HASH]] Déposant(s) Auteur : @@ -62,7 +62,7 @@ export default class AnchoringProofService extends BaseService { Smart-Chain Explorateur blockchain - https://tzstats.com/opYWLHH96gbV8HPajRqmoRx3UBVbr6iXz43kuHqm8ey4LLPWqeC + [[TX_LINK]] `; @@ -85,7 +85,10 @@ export default class AnchoringProofService extends BaseService { `; - htmlContent = htmlContent.replace("[[ANCHORING_TIME]]", data.anchoringTime); + htmlContent = htmlContent + .replace("[[ROOT_HASH]]", data.rootHash) + .replace("[[ANCHORING_TIME]]", data.anchoringTime) + .replace(/\[\[TX_LINK\]\]/g, data.txLink); await page.setContent(htmlContent); await page.addStyleTag({ diff --git a/src/services/common/ContactService.ts b/src/services/common/ContactService.ts deleted file mode 100644 index e1cc6c19..00000000 --- a/src/services/common/ContactService.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Contacts, Customers } from "@prisma/client"; -import BaseService from "@Services/BaseService"; -import { Service } from "typedi"; -import ContactRepository from "@Repositories/ContactRepository"; - -@Service() -export default class DocumentsService extends BaseService { - constructor(private contactRepository: ContactRepository) { - super(); - } - - /** - * @description : Get a contact by email - * @throws {Error} If contact cannot be get by email - */ - public async getByEmail(email: string): Promise<(Contacts & {customers: Customers | null}) | null> { - return this.contactRepository.findOneByEmail(email); - } -} diff --git a/src/services/customer/CustomersService/CustomersService.ts b/src/services/customer/CustomersService/CustomersService.ts index 68c9888e..8ec5f672 100644 --- a/src/services/customer/CustomersService/CustomersService.ts +++ b/src/services/customer/CustomersService/CustomersService.ts @@ -1,12 +1,11 @@ import { Customers, Prisma } from "@prisma/client"; import CustomersRepository from "@Repositories/CustomersRepository"; -import ContactRepository from "@Repositories/ContactRepository"; import BaseService from "@Services/BaseService"; import { Service } from "typedi"; @Service() export default class CustomersService extends BaseService { - constructor(private customerRepository: CustomersRepository, private contactRepository: ContactRepository) { + constructor(private customerRepository: CustomersRepository) { super(); } @@ -17,30 +16,4 @@ export default class CustomersService extends BaseService { public async get(query: Prisma.CustomersFindManyArgs): Promise { return this.customerRepository.findMany(query); } - - /** - * @description : Get a customer by uid - * @throws {Error} If customer cannot be get by uid - */ - public async getByUid(uid: string, query?: Prisma.CustomersInclude): Promise { - return this.customerRepository.findOneByUid(uid, query); - } - - /** - * @description : Get a customer by contact uid - * @throws {Error} If customer cannot be get by contact uid - */ - public async getByContact(contactUid: string): Promise { - return this.customerRepository.findOneByContact(contactUid); - } - - /** - * @description : Get a customer by contact uid - * @throws {Error} If customer cannot be get by contact uid - */ - public async getByEmail(contactUid: string) { - return this.contactRepository.findOneByEmail(contactUid); - } - - -} +} \ No newline at end of file diff --git a/src/services/customer/OfficeFoldersService/OfficeFoldersService.ts b/src/services/customer/OfficeFoldersService/OfficeFoldersService.ts index 6afdede7..5095585a 100644 --- a/src/services/customer/OfficeFoldersService/OfficeFoldersService.ts +++ b/src/services/customer/OfficeFoldersService/OfficeFoldersService.ts @@ -26,4 +26,14 @@ export default class OfficeFoldersService extends BaseService { public async getByUid(uid: string, query?: Prisma.OfficeFoldersInclude) { return this.officeFoldersRepository.findOneByUid(uid, query); } + + /** + * @description : Get a folder by uid + * @throws {Error} If folder cannot be get by uid + */ + public async getByUidWithCustomers(uid: string) { + return this.officeFoldersRepository.findOneByUidWithCustomers(uid); + } + + } diff --git a/src/services/notary/CustomersService/CustomersService.ts b/src/services/notary/CustomersService/CustomersService.ts index 72e6923d..3202d143 100644 --- a/src/services/notary/CustomersService/CustomersService.ts +++ b/src/services/notary/CustomersService/CustomersService.ts @@ -23,6 +23,14 @@ export default class CustomersService extends BaseService { * @throws {Error} If customer cannot be created */ public async create(customerEntity: Customer): Promise { + const customers = await this.get({ + where: { + contact: { + OR: [{ email: customerEntity.contact?.email }, { cell_phone_number: customerEntity.contact?.cell_phone_number }], + }, + }, + }); + if(customers[0]) return customers[0]; return this.customerRepository.create(customerEntity); }