Merge Staging in Preprod

This commit is contained in:
Arnaud D. Natali 2023-10-02 00:29:02 +02:00 committed by GitHub
commit 8716964d2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1142 additions and 505 deletions

View File

@ -7,6 +7,8 @@ RUN npm install -D prisma@4.11.0
COPY package.json ./
RUN apk update && apk add openssh-client git
# ENV PUPPETEER_CACHE_DIR=~/leCoffre/.cache/puppeteer
# ENV PUPPETEER_SKIP_DOWNLOAD=false
COPY id_rsa /root/.ssh/id_rsa
RUN chmod 600 ~/.ssh/id_rsa
@ -22,6 +24,8 @@ WORKDIR leCoffre
COPY --from=deps leCoffre/node_modules ./node_modules
COPY --from=deps leCoffre/package.json package.json
#COPY --from=deps leCoffre/.cache ./.cache
COPY tsconfig.json tsconfig.json
COPY src src
@ -39,8 +43,9 @@ COPY --from=builder --chown=lecoffreuser leCoffre/node_modules ./node_modules
COPY --from=builder --chown=lecoffreuser leCoffre/dist dist
COPY --from=builder --chown=lecoffreuser leCoffre/package.json ./package.json
COPY --from=builder --chown=lecoffreuser leCoffre/src/common/databases ./src/common/databases
#COPY --from=builder --chown=lecoffreuser leCoffre/.cache ./.cache
USER lecoffreuser
CMD ["npm", "run", "api:start"]
CMD ["npm", "run", "start"]
EXPOSE 3001

View File

@ -5,7 +5,7 @@ scwSecretKey: AgChoEnPitXp4Ny/rVMEcevaWKNVpyj2cJYAcq+yFqKwVwnLB+ffDvwqz9XBHu+6d4
lecoffreBack:
serviceAccountName: lecoffre-back-sa
envSecrets: stg-env
command: "'sh', '-c', 'export $(xargs </etc/env/.env) && npm run api:start'"
command: "'sh', '-c', 'export $(xargs </etc/env/.env) && npm run start'"
imagePullSecrets:
- name: docker-pull-secret
image:

View File

@ -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",

View File

@ -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);

View File

@ -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();
}
@ -39,7 +40,7 @@ export default class OfficeRolesController extends ApiController {
mode: "insensitive",
},
},
}
};
}
const officeId: string = req.body.user.office_Id;
@ -108,6 +109,18 @@ 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<OfficeRole>(req.body);

View File

@ -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<Customer>(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<Customer>(customerEntity, { strategy: "excludeAll" });
//success
this.httpSuccess(response, customer);
} catch (error) {
this.httpInternalError(response, error);
return;
}
}
}

View File

@ -24,13 +24,14 @@ export default class DocumentsController extends ApiController {
protected async get(req: Request, response: Response) {
try {
//get query
let query: Prisma.CustomersFindManyArgs = {};
let query: Prisma.DocumentsFindManyArgs = {};
if (req.query["q"]) {
query = JSON.parse(req.query["q"] as string);
}
const customerId: string = req.body.user.customerId;
const customerWhereInput: Prisma.DocumentsWhereInput ={ depositor: { uid: customerId } };
if(query.where?.depositor) delete query.where.depositor;
if(query.where?.depositor_uid) delete query.where.depositor_uid;
const customerWhereInput: Prisma.DocumentsWhereInput = { ...query.where, depositor: { uid: customerId } };
query.where = customerWhereInput;

View File

@ -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) {

View File

@ -35,13 +35,11 @@ export default class CustomerController extends ApiController {
}
try {
const enrollment = await this.id360Service.getEnrollment(callbackToken);
console.log("enrollment", enrollment);
if (enrollment.status !== "OK") {
this.httpUnauthorized(response, "Enrollment status is not OK");
return;
}
const customerData = await this.id360Service.getReport(enrollment.id);
console.log(customerData.external_methods.france_connect.results);
const customer = await this.customerService.get({
where: {
contact: {
@ -55,7 +53,6 @@ export default class CustomerController extends ApiController {
contact: true,
}
});
console.log(customer);
// const contact = await this.customerService.getByEmail(
// customerData.external_methods.france_connect.results.france_connect_out_userinfo[0].email,
// );

View File

@ -50,10 +50,7 @@ export default class DocumentController extends ApiController {
this.httpBadRequest(response, "Missing document id");
return;
}
const enrl = await this.id360Service.createEnrollment(documentId!);
console.log(enrl)
await this.id360Service.createEnrollment(documentId!);
//success
this.httpSuccess(response);
} catch (error) {

View File

@ -46,29 +46,6 @@ export default class UserController extends ApiController {
}
}
@Post("/api/v1/idnot/user/login/:idnot")
protected async login(req: Request, response: Response) {
try {
const id = req.params["idnot"];
if (!id) throw new Error("idnot is required");
const payload = await this.authService.getUserJwtPayload(id);
if (!payload) {
this.httpNotFoundRequest(response);
return;
}
const accessToken = this.authService.generateAccessToken(payload);
const refreshToken = this.authService.generateRefreshToken(payload);
//success
this.httpSuccess(response, { accessToken, refreshToken });
} catch (error) {
console.log(error);
this.httpInternalError(response);
return;
}
}
@Post("/api/v1/idnot/user/auth/refresh-token")
protected async refreshToken(req: Request, response: Response) {
try {

View File

@ -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);

View File

@ -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<OfficeFolderAnchor>(
{
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() ?? undefined,
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<OfficeFolderAnchor>(officeFolderFound.folder_anchor, {
strategy: "excludeAll",
});
if (officeFolderAnchorFound) {
this.httpBadRequest(response, {
error: "Office folder already anchored",
folder_anchor: officeFolderAnchorFound,
});
return;
}
const officeFolder = OfficeFolder.hydrate<OfficeFolder>(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<OfficeFolder>({ 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<OfficeFolderAnchor>(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;

View File

@ -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) {

View File

@ -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<UserNotification>(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<UserNotification>(userNotificationEntity, { strategy: "excludeAll" });

View File

@ -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);

View File

@ -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);

View File

@ -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();
}

View File

@ -45,12 +45,12 @@ export default async function documentHandler(req: Request, response: Response,
const document = await documentService.getByUidWithOffice(uid!);
if (!document) {
response.sendStatus(HttpCodes.NOT_FOUND);
response.sendStatus(HttpCodes.NOT_FOUND).send("Document not found");
return;
}
if (document.document_type.office.uid != officeId) {
response.sendStatus(HttpCodes.UNAUTHORIZED);
if (document.folder.office.uid != officeId) {
response.sendStatus(HttpCodes.UNAUTHORIZED).send("Unauthorized with this office");
return;
}
}

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "rules" ADD COLUMN "namespace" VARCHAR(255) NOT NULL DEFAULT 'notary';

View File

@ -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")
}

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,11 @@
import DocumentsService from "@Services/super-admin/DocumentsService/DocumentsService";
import { Documents } from "@prisma/client";
import { Documents, OfficeFolders } from "@prisma/client";
import User, { Document, OfficeFolder, Vote } from "le-coffre-resources/dist/SuperAdmin";
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,33 +34,33 @@ 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 || [],
});
}
public async sendDocumentAnchoredNotification(documentEntity: Documents) {
const documentPrisma = await this.documentsService.getByUid(documentEntity.uid, {
depositor: { include: { contact: true } },
folder: { include: { folder_anchor: true, office: true, stakeholders: true } },
});
if (!documentPrisma) throw new Error("Document not found");
const document = Document.hydrate<Document>(documentPrisma);
if (document.folder?.folder_anchor?.status !== "VERIFIED_ON_CHAIN") return;
public async sendFolderAnchoredNotification(folderEntity: OfficeFolders) {
if(!folderEntity.uid) return;
const officeFolderPrisma = await this.foldersService.getByUid(folderEntity.uid,
{ folder_anchor: true, office: true, stakeholders: true }
);
if (!officeFolderPrisma) throw new Error("Folder not found");
const folder = OfficeFolder.hydrate<OfficeFolder>(officeFolderPrisma);
if (folder.folder_anchor?.status !== "VERIFIED_ON_CHAIN") return;
this.notificationsService.create({
message:
"Le dossier " +
document.folder?.folder_number +
folder.folder_number +
" - " +
document.folder?.name +
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/${folder?.uid}`,
created_at: new Date(),
updated_at: new Date(),
user: document.folder!.stakeholders || [],
user: 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 || [],

View File

@ -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<OfficeFolderAnchors> {
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<OfficeFolderAnchors> {
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,
});
}
}

View File

@ -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
*/

View File

@ -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,
},
};

View File

@ -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;

View File

@ -8,5 +8,6 @@ enum HttpCodes {
NOT_IMPLEMENTED = 501,
NOT_FOUND = 404,
UNAUTHORIZED = 401,
FORBIDDEN = 403,
}
export default HttpCodes;

View File

@ -53,7 +53,7 @@ export default class AnchoringProofService extends BaseService {
<text fill="black" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="12" letter-spacing="0.5px"><tspan x="333" y="187.364">[[ANCHORING_TIME]]</tspan></text>
<text fill="black" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="21" font-weight="600" letter-spacing="0em"><tspan x="258" y="163.136">Certificat de d&#xe9;p&#xf4;t international</tspan></text>
<text fill="#320756" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="11" font-weight="600" letter-spacing="0.5px"><tspan x="119" y="262">Hash :</tspan></text>
<text fill="black" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="11" letter-spacing="0.5px"><tspan x="267" y="261.5">b834ce9229ee2c2283c837685772a473</tspan></text>
<text fill="black" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="11" letter-spacing="0.5px"><tspan x="267" y="261.5">[[ROOT_HASH]]</tspan></text>
<path d="M119 290H723" stroke="#E7E7E7"/>
<text fill="#320756" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="14" font-weight="600" letter-spacing="0.5px"><tspan x="119" y="327.091">D&#xe9;posant(s)</tspan></text>
<text fill="#320756" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="11" font-weight="600" letter-spacing="0.5px"><tspan x="119" y="356">Auteur :</tspan></text>
@ -62,7 +62,7 @@ export default class AnchoringProofService extends BaseService {
<text fill="black" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="11" letter-spacing="0.5px"><tspan x="267" y="377.5">Smart-Chain</tspan></text>
<path d="M119 406H723" stroke="#E7E7E7"/>
<text fill="#320756" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="14" font-weight="600" letter-spacing="0.5px"><tspan x="119" y="443.091">Explorateur blockchain</tspan></text>
<text fill="black" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="11" letter-spacing="0.5px" text-decoration="underline"><tspan x="119" y="472"><a href="https://tzstats.com/opYWLHH96gbV8HPajRqmoRx3UBVbr6iXz43kuHqm8ey4LLPWqeC">https://tzstats.com/opYWLHH96gbV8HPajRqmoRx3UBVbr6iXz43kuHqm8ey4LLPWqeC</a></tspan></text>
<text fill="black" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="11" letter-spacing="0.5px" text-decoration="underline"><tspan x="119" y="472"><a href="[[TX_LINK]]">[[TX_LINK]]</a></tspan></text>
</g>
</svg>
`;
@ -73,6 +73,7 @@ export default class AnchoringProofService extends BaseService {
public async generate(data: AnchoringProofData): Promise<Buffer> {
const browser = await puppeteer.launch({
headless: "new",
executablePath: process.env['PUPPETEER_EXECUTABLE_PATH'],
args: ["--no-sandbox", "--disable-setuid-sandbox"],
});
const page = await browser.newPage();
@ -85,7 +86,10 @@ export default class AnchoringProofService extends BaseService {
</html>
`;
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({

View File

@ -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);
}
}

View File

@ -110,7 +110,6 @@ export default class Id360Service extends BaseService {
);
const resJson = (await res.json()) as EnrollmentResponse;
console.log(resJson.id);
return {
franceConnectUrl: `${this.variables.DOCAPOST_BASE_URL}static/process_ui/index.html#/enrollment/${resJson.api_key}`,
processId: resJson.id,
@ -139,10 +138,8 @@ export default class Id360Service extends BaseService {
);
const resJson = (await res.json()) as EnrollmentResponse;
console.log(resJson);
const route = await this.getRouteId(resJson.api_key);
await this.selectRoute(resJson.api_key, route);
console.log(route);
await this.uploadDocument(resJson.api_key, documentUid);
return await this.getReport(resJson.id);

View File

@ -33,7 +33,7 @@ interface IRattachementData {
name: string;
};
locationsUrl: string;
}
};
personne: {
numeroTelephonePro: string;
prenom: string;
@ -67,7 +67,7 @@ interface IOfficeData {
};
typeEntite: {
name: string;
}
};
}
interface IOfficeLocation {
@ -193,22 +193,22 @@ export default class IdNotService extends BaseService {
const searchParams = new URLSearchParams({
key: this.variables.IDNOT_API_KEY,
});
let userData = await (await fetch(
let userData = (await (
await fetch(
`${this.variables.IDNOT_API_BASE_URL}/api/pp/v2/rattachements/${user.idNot}_${user.office_membership!.idNot}?` +
searchParams,
{
method: "GET",
},
)).json() as IRattachementData;
)
).json()) as IRattachementData;
if (!userData.statutDuRattachement) {
const rattachements = await (await fetch(
`${this.variables.IDNOT_API_BASE_URL}/api/pp/v2/personnes/${user.idNot}/rattachements?` +
searchParams,
{
const rattachements = (await (
await fetch(`${this.variables.IDNOT_API_BASE_URL}/api/pp/v2/personnes/${user.idNot}/rattachements?` + searchParams, {
method: "GET",
},
)).json() as any;
})
).json()) as any;
if (rattachements.totalResultCount === 0) {
await this.userService.updateCheckedAt(user.uid!);
//await this.userService.delete(user.uid!);
@ -218,13 +218,11 @@ export default class IdNotService extends BaseService {
if (!rattachementsResults) return;
rattachementsResults.forEach(async (rattachement) => {
if (rattachement.statutDuRattachement) {
const officeData = await (await fetch(
`${this.variables.IDNOT_API_BASE_URL + rattachement.entiteUrl}?` +
searchParams,
{
const officeData = (await (
await fetch(`${this.variables.IDNOT_API_BASE_URL + rattachement.entiteUrl}?` + searchParams, {
method: "GET",
},
)).json() as IOfficeData;
})
).json()) as IOfficeData;
if (officeData.typeEntite.name === "office") {
userData = rattachement;
}
@ -279,13 +277,9 @@ export default class IdNotService extends BaseService {
const searchParams = new URLSearchParams({
key: this.variables.IDNOT_API_KEY,
});
const officeRawData = await fetch(
`${this.variables.IDNOT_API_BASE_URL}/api/pp/v2/entities/${office.idNot}?` +
searchParams,
{
const officeRawData = await fetch(`${this.variables.IDNOT_API_BASE_URL}/api/pp/v2/entities/${office.idNot}?` + searchParams, {
method: "GET",
},
);
});
if (officeRawData.status === 404) {
await this.officeService.updateCheckedAt(office.uid!);
//await this.officeService.delete(office.uid!);
@ -318,7 +312,6 @@ export default class IdNotService extends BaseService {
})
).json()) as IRattachementData;
if (!userData.statutDuRattachement || userData.entite.typeEntite.name !== "office") {
return null;
}
@ -327,6 +320,8 @@ export default class IdNotService extends BaseService {
await fetch(`${this.variables.IDNOT_API_BASE_URL + userData.entite.locationsUrl}?` + searchParams, { method: "GET" })
).json()) as IOfficeLocation;
const office = await this.officeService.get({ where: { idNot: decodedToken.entity_idn } });
// if(officeLocationData.result[0]!.adrGeoCodePostal.slice(0,2) !== "35") {
// return null;
// }
@ -362,30 +357,36 @@ export default class IdNotService extends BaseService {
updated_at: null,
},
};
let userHydrated = User.hydrate<User>(userToAdd);
const user = await this.userService.create(userHydrated);
const userOffice = await this.officeService.getByUid(user.office_uid);
userHydrated = User.hydrate<User>(user);
const userOfficeHydrated = Office.hydrate<Office>(userOffice!);
const officeRoles = await this.officeRolesService.get({ where: { office: { idNot: "0000" }}, include: { office: true, rules: true } });
const documentTypes = await this.documentTypesService.get({ where: { office: { idNot: "0000" }}, include: { office: true } });
const deedTypes = await this.deedTypesService.get({ where:{ office: { idNot: "0000" }}, include: { office: true, document_types: true } });
if(office.length === 0) {
const officeRoles = await this.officeRolesService.get({
where: { office: { idNot: "0000" } },
include: { office: true, rules: true },
});
const deedTypes = await this.deedTypesService.get({
where: { office: { idNot: "0000" } },
include: { office: true, document_types: { include: { office: true } } },
});
const documentTypes = await this.documentTypesService.get({
where: { office: { idNot: "0000" } },
include: { office: true },
});
const officeRolesHydrated = OfficeRole.hydrateArray<OfficeRole>(officeRoles);
const documentTypesHydrated = DocumentType.hydrateArray<DocumentType>(documentTypes);
const deedTypesHydrated = DeedType.hydrateArray<DeedType>(deedTypes);
const documentTypesHydrated = DocumentType.hydrateArray<DocumentType>(documentTypes);
officeRolesHydrated.forEach(async (officeRole) => {
officeRole.office.uid = user.office_uid;
await this.officeRolesService.create(officeRole);
});
documentTypesHydrated.forEach(async (documentType) => {
documentType.office!.uid = user.office_uid;
await this.documentTypesService.create(documentType);
});
deedTypesHydrated.forEach(async (deedType) => {
deedType.office!.uid = user.office_uid;
await this.deedTypesService.create(deedType);
});
await this.duplicateOfficeRoles(officeRolesHydrated, userOfficeHydrated);
const documentTypesCreated = await this.duplicateDocumentTypes(documentTypesHydrated, userOfficeHydrated);
await this.duplicateDeedTypes(deedTypesHydrated, documentTypesCreated, userOfficeHydrated);
}
const officeRole = await this.getOfficeRole(userData.typeLien.name, user.office_uid);
@ -400,12 +401,44 @@ export default class IdNotService extends BaseService {
return user;
}
public async duplicateDocumentTypes(documentTypes: DocumentType[], office: Office): Promise<DocumentType[]> {
let newDocumentTypes: DocumentType[] = [];
for(const documentType of documentTypes) {
documentType.office = office;
const documentTypeCreated = await this.documentTypesService.create(documentType);
newDocumentTypes.push(DocumentType.hydrate<DocumentType>(documentTypeCreated));
};
return newDocumentTypes;
}
public async duplicateDeedTypes(deedTypes: DeedType[], documentTypes: DocumentType[], office: Office) {
for (const deedType of deedTypes) {
let newDocumentTypes: DocumentType[] = [];
for (const document of deedType.document_types!) {
const newDocumentType = documentTypes.find((documentType) => documentType.name === document.name);
if(!newDocumentType) continue;
newDocumentTypes.push(newDocumentType!);
};
deedType.document_types = newDocumentTypes;
deedType.office = office;
await this.deedTypesService.create(deedType);
};
}
public async duplicateOfficeRoles(officeRoles: OfficeRole[], office: Office){
for(const officeRole of officeRoles) {
officeRole.office = office;
await this.officeRolesService.create(officeRole);
};
}
public async updateUsers() {
const usersReq = await this.userService.getUsersToBeChecked();
const users = User.hydrateArray<User>(usersReq);
users.forEach(async (user) => {
await this.updateUser(user.uid!);
});
}

View File

@ -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<Customers[]> {
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<Customers | null> {
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<Customers | null> {
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);
}
}

View File

@ -24,9 +24,9 @@ export default class DocumentsService extends BaseService {
* @throws {Error} If document cannot be created
*/
public async create(document: Document): Promise<Documents> {
const otherDocumentType = await this.documentTypeService.get({ where: { name: "Other" } });
const otherDocumentType = await this.documentTypeService.get({ where: { name: "Autres documents" } });
if(otherDocumentType.length < 1) throw new Error("Other document type not found");
if(otherDocumentType.length < 1) throw new Error("Autres documents document type not found");
document.document_type = otherDocumentType[0];
document.document_status = "DEPOSITED";

View File

@ -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);
}
}

View File

@ -23,6 +23,14 @@ export default class CustomersService extends BaseService {
* @throws {Error} If customer cannot be created
*/
public async create(customerEntity: Customer): Promise<Customers> {
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);
}