Merge Dev in staging (#81)
This commit is contained in:
commit
7e60ae5f15
813
package-lock.json
generated
813
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -51,9 +51,11 @@
|
||||
"cors": "^2.8.5",
|
||||
"cron": "^2.3.1",
|
||||
"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.73",
|
||||
"le-coffre-resources": "git@github.com:smart-chain-fr/leCoffre-resources.git#v2.77",
|
||||
"module-alias": "^2.2.2",
|
||||
"monocle-ts": "^2.3.13",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"next": "^13.1.5",
|
||||
"node-cache": "^5.1.2",
|
||||
|
111
src/app/api/notary/OfficeFolderAnchorsController.ts
Normal file
111
src/app/api/notary/OfficeFolderAnchorsController.ts
Normal file
@ -0,0 +1,111 @@
|
||||
import { Response, Request } from "express";
|
||||
import { Controller, Get, Post } from "@ControllerPattern/index";
|
||||
import ApiController from "@Common/system/controller-pattern/ApiController";
|
||||
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 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";
|
||||
|
||||
@Controller()
|
||||
@Service()
|
||||
export default class OfficeFoldersController extends ApiController {
|
||||
constructor(private secureService: SecureService, private officeFoldersService: OfficeFoldersService) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Create a new folder anchor
|
||||
*/
|
||||
@Post("/api/v1/notary/anchors/:uid", [authHandler, ruleHandler, folderHandler])
|
||||
protected async post(req: Request, response: Response) {
|
||||
try {
|
||||
const uid = req.params["uid"];
|
||||
|
||||
if (!uid) {
|
||||
this.httpBadRequest(response, "No uid provided");
|
||||
return;
|
||||
}
|
||||
|
||||
const query = {
|
||||
documents: {
|
||||
include: {
|
||||
files: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const officeFolderFound = await this.officeFoldersService.getByUid(uid, query);
|
||||
|
||||
if (!officeFolderFound) {
|
||||
this.httpNotFoundRequest(response, "Office folder not found");
|
||||
return;
|
||||
}
|
||||
|
||||
const officeFolder = OfficeFolder.hydrate<OfficeFolder>(officeFolderFound, { strategy: "excludeAll" });
|
||||
const folderHashes = getFolderHashes(officeFolder);
|
||||
|
||||
if (folderHashes.length === 0) {
|
||||
this.httpNotFoundRequest(response, "No file hash to anchor");
|
||||
return;
|
||||
}
|
||||
|
||||
const sortedHashes = [...folderHashes].sort();
|
||||
const anchor = await this.secureService.anchor(sortedHashes);
|
||||
|
||||
this.httpSuccess(response, anchor);
|
||||
} catch (error) {
|
||||
this.httpInternalError(response, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Verify a folder anchor status
|
||||
*/
|
||||
@Get("/api/v1/notary/anchors/:uid", [authHandler, ruleHandler, folderHandler])
|
||||
protected async get(req: Request, response: Response) {
|
||||
try {
|
||||
const uid = req.params["uid"];
|
||||
|
||||
if (!uid) {
|
||||
this.httpBadRequest(response, "No uid provided");
|
||||
return;
|
||||
}
|
||||
|
||||
const query = {
|
||||
documents: {
|
||||
include: {
|
||||
files: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const officeFolderFound = await this.officeFoldersService.getByUid(uid, query);
|
||||
|
||||
if (!officeFolderFound) {
|
||||
this.httpNotFoundRequest(response, "Office folder not found");
|
||||
return;
|
||||
}
|
||||
|
||||
const officeFolder = OfficeFolder.hydrate<OfficeFolder>(officeFolderFound, { strategy: "excludeAll" });
|
||||
const folderHashes = getFolderHashes(officeFolder);
|
||||
|
||||
if (folderHashes.length === 0) {
|
||||
this.httpNotFoundRequest(response, "No file hash to anchor");
|
||||
return;
|
||||
}
|
||||
|
||||
const sortedHashes = [...folderHashes].sort();
|
||||
const anchor = await this.secureService.verify(sortedHashes);
|
||||
|
||||
this.httpSuccess(response, anchor);
|
||||
} catch (error) {
|
||||
this.httpInternalError(response, error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
@ -41,6 +41,7 @@ import OfficeRolesControllerNotary from "./api/notary/OfficeRolesController";
|
||||
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";
|
||||
@ -100,6 +101,7 @@ export default {
|
||||
Container.get(FilesControllerCustomer);
|
||||
Container.get(DocumentsControllerCustomer);
|
||||
Container.get(OfficeFoldersController);
|
||||
Container.get(OfficeFolderAnchorsController);
|
||||
Container.get(CustomersController)
|
||||
},
|
||||
};
|
||||
|
@ -76,11 +76,16 @@ export class BackendVariables {
|
||||
@IsNotEmpty()
|
||||
public readonly MAILCHIMP_API_KEY!: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
public readonly SECURE_API_KEY!: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
public readonly SECURE_API_BASE_URL!: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
public readonly ENV!: string;
|
||||
|
||||
public constructor() {
|
||||
|
||||
dotenv.config();
|
||||
this.DATABASE_PORT = process.env["DATABASE_PORT"]!;
|
||||
this.DATABASE_HOST = process.env["DATABASE_HOST"]!;
|
||||
@ -106,8 +111,9 @@ export class BackendVariables {
|
||||
this.ACCESS_TOKEN_SECRET = process.env["ACCESS_TOKEN_SECRET"]!;
|
||||
this.REFRESH_TOKEN_SECRET = process.env["REFRESH_TOKEN_SECRET"]!;
|
||||
this.MAILCHIMP_API_KEY = process.env["MAILCHIMP_API_KEY"]!;
|
||||
this.SECURE_API_KEY = process.env["SECURE_API_KEY"]!;
|
||||
this.SECURE_API_BASE_URL = process.env["SECURE_API_BASE_URL"]!;
|
||||
this.ENV = process.env["ENV"]!;
|
||||
|
||||
}
|
||||
public async validate(groups?: string[]) {
|
||||
const validationOptions = groups ? { groups } : undefined;
|
||||
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- The values [ANCHORED] on the enum `EDocumentStatus` will be removed. If these variants are still used in the database, this will fail.
|
||||
- You are about to drop the column `blockchain_anchor_uid` on the `documents` table. All the data in the column will be lost.
|
||||
- You are about to drop the `blockchain_anchors` table. If the table is not empty, all the data it contains will be lost.
|
||||
- A unique constraint covering the columns `[folder_anchor_uid]` on the table `office_folders` will be added. If there are existing duplicate values, this will fail.
|
||||
- Added the required column `hash` to the `files` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- CreateEnum
|
||||
CREATE TYPE "EBlockchainName" AS ENUM ('TEZOS');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "EAnchoringStatus" AS ENUM ('QUEUED', 'ATTEMPTING', 'VERIFIED_ON_CHAIN', 'VERIFYING_ON_CHAIN', 'ABANDONED');
|
||||
|
||||
-- AlterEnum
|
||||
BEGIN;
|
||||
CREATE TYPE "EDocumentStatus_new" AS ENUM ('ASKED', 'DEPOSITED', 'VALIDATED', 'REFUSED');
|
||||
ALTER TABLE "documents" ALTER COLUMN "document_status" DROP DEFAULT;
|
||||
ALTER TABLE "document_history" ALTER COLUMN "document_status" DROP DEFAULT;
|
||||
ALTER TABLE "documents" ALTER COLUMN "document_status" TYPE "EDocumentStatus_new" USING ("document_status"::text::"EDocumentStatus_new");
|
||||
ALTER TABLE "document_history" ALTER COLUMN "document_status" TYPE "EDocumentStatus_new" USING ("document_status"::text::"EDocumentStatus_new");
|
||||
ALTER TYPE "EDocumentStatus" RENAME TO "EDocumentStatus_old";
|
||||
ALTER TYPE "EDocumentStatus_new" RENAME TO "EDocumentStatus";
|
||||
DROP TYPE "EDocumentStatus_old";
|
||||
ALTER TABLE "documents" ALTER COLUMN "document_status" SET DEFAULT 'ASKED';
|
||||
ALTER TABLE "document_history" ALTER COLUMN "document_status" SET DEFAULT 'ASKED';
|
||||
COMMIT;
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "documents" DROP CONSTRAINT "documents_blockchain_anchor_uid_fkey";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "documents" DROP COLUMN "blockchain_anchor_uid";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "files" ADD COLUMN "hash" VARCHAR(255) NOT NULL;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "office_folders" ADD COLUMN "folder_anchor_uid" VARCHAR(255);
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "blockchain_anchors";
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "office_folder_anchors" (
|
||||
"uid" TEXT NOT NULL,
|
||||
"hash_sources" TEXT[],
|
||||
"root_hash" VARCHAR(255) NOT NULL,
|
||||
"blockchain" "EBlockchainName" NOT NULL DEFAULT 'TEZOS',
|
||||
"status" "EAnchoringStatus" NOT NULL DEFAULT 'QUEUED',
|
||||
"anchor_nb_try" INTEGER NOT NULL DEFAULT 0,
|
||||
"anchored_at" TIMESTAMP(3),
|
||||
"tx_id" VARCHAR(255),
|
||||
"tx_link" VARCHAR(255),
|
||||
"tx_hash" VARCHAR(255),
|
||||
"created_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "office_folder_anchors_pkey" PRIMARY KEY ("uid")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "office_folder_anchors_uid_key" ON "office_folder_anchors"("uid");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "office_folders_folder_anchor_uid_key" ON "office_folders"("folder_anchor_uid");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "office_folders" ADD CONSTRAINT "office_folders_folder_anchor_uid_fkey" FOREIGN KEY ("folder_anchor_uid") REFERENCES "office_folder_anchors"("uid") ON DELETE SET NULL ON UPDATE CASCADE;
|
@ -133,17 +133,41 @@ model OfficeFolders {
|
||||
customers Customers[] @relation("OfficeFolderHasCustomers")
|
||||
documents Documents[]
|
||||
|
||||
folder_anchor OfficeFolderAnchors? @relation(fields: [folder_anchor_uid], references: [uid])
|
||||
folder_anchor_uid String? @unique @db.VarChar(255)
|
||||
|
||||
@@unique([folder_number, office_uid])
|
||||
@@map("office_folders")
|
||||
}
|
||||
|
||||
model OfficeFolderAnchors {
|
||||
uid String @id @unique @default(uuid())
|
||||
|
||||
hash_sources String[]
|
||||
root_hash String @db.VarChar(255)
|
||||
|
||||
blockchain EBlockchainName @default(TEZOS)
|
||||
status EAnchoringStatus @default(QUEUED)
|
||||
anchor_nb_try Int @default(0)
|
||||
|
||||
anchored_at DateTime?
|
||||
tx_id String? @db.VarChar(255)
|
||||
tx_link String? @db.VarChar(255)
|
||||
tx_hash String? @db.VarChar(255)
|
||||
|
||||
folder OfficeFolders?
|
||||
|
||||
created_at DateTime? @default(now())
|
||||
updated_at DateTime? @updatedAt
|
||||
|
||||
@@map("office_folder_anchors")
|
||||
}
|
||||
|
||||
model Documents {
|
||||
uid String @id @unique @default(uuid())
|
||||
document_status EDocumentStatus @default(ASKED)
|
||||
document_type DocumentTypes @relation(fields: [document_type_uid], references: [uid])
|
||||
document_type_uid String @db.VarChar(255)
|
||||
blockchain_anchor BlockchainAnchors? @relation(fields: [blockchain_anchor_uid], references: [uid])
|
||||
blockchain_anchor_uid String? @db.VarChar(255)
|
||||
folder OfficeFolders @relation(fields: [folder_uid], references: [uid])
|
||||
folder_uid String @db.VarChar(255)
|
||||
depositor Customers @relation(fields: [depositor_uid], references: [uid], onDelete: Cascade)
|
||||
@ -175,6 +199,7 @@ model Files {
|
||||
file_path String @unique @db.VarChar(255)
|
||||
file_name String @db.VarChar(255)
|
||||
mimetype String @db.VarChar(255)
|
||||
hash String @db.VarChar(255)
|
||||
size Int
|
||||
archived_at DateTime?
|
||||
key String? @db.VarChar(255)
|
||||
@ -184,16 +209,6 @@ model Files {
|
||||
@@map("files")
|
||||
}
|
||||
|
||||
model BlockchainAnchors {
|
||||
uid String @id @unique @default(uuid())
|
||||
smartSigJobId String @unique @db.VarChar(255)
|
||||
created_at DateTime? @default(now())
|
||||
updated_at DateTime? @updatedAt
|
||||
documents Documents[]
|
||||
|
||||
@@map("blockchain_anchors")
|
||||
}
|
||||
|
||||
model DocumentTypes {
|
||||
uid String @id @unique @default(uuid())
|
||||
name String @db.VarChar(255)
|
||||
@ -346,7 +361,6 @@ enum EDocumentStatus {
|
||||
ASKED
|
||||
DEPOSITED
|
||||
VALIDATED
|
||||
ANCHORED
|
||||
REFUSED
|
||||
}
|
||||
|
||||
@ -359,3 +373,15 @@ enum EAppointmentStatus {
|
||||
OPEN
|
||||
CLOSED
|
||||
}
|
||||
|
||||
enum EBlockchainName {
|
||||
TEZOS
|
||||
}
|
||||
|
||||
enum EAnchoringStatus {
|
||||
QUEUED
|
||||
ATTEMPTING
|
||||
VERIFIED_ON_CHAIN
|
||||
VERIFYING_ON_CHAIN
|
||||
ABANDONED
|
||||
}
|
||||
|
@ -669,6 +669,18 @@ export default async function main() {
|
||||
created_at: new Date(),
|
||||
updated_at: new Date(),
|
||||
},
|
||||
{
|
||||
name: "POST anchors",
|
||||
label: "Ancrer un dossier",
|
||||
created_at: new Date(),
|
||||
updated_at: new Date(),
|
||||
},
|
||||
{
|
||||
name: "GET anchors",
|
||||
label: "Vérifier l'ancrage un dossier",
|
||||
created_at: new Date(),
|
||||
updated_at: new Date(),
|
||||
},
|
||||
{
|
||||
name: "POST deed-types",
|
||||
label: "Création des types d'actes",
|
||||
@ -756,14 +768,14 @@ export default async function main() {
|
||||
label: "Administrateur",
|
||||
created_at: new Date(),
|
||||
updated_at: new Date(),
|
||||
rules: rules.slice(0, 33),
|
||||
rules: rules.slice(0, 35),
|
||||
},
|
||||
{
|
||||
name: "notary",
|
||||
label: "Notaire",
|
||||
created_at: new Date(),
|
||||
updated_at: new Date(),
|
||||
rules: rules.slice(0, 23),
|
||||
rules: rules.slice(0, 25),
|
||||
},
|
||||
{
|
||||
name: "default",
|
||||
@ -780,7 +792,7 @@ export default async function main() {
|
||||
created_at: new Date(),
|
||||
updated_at: new Date(),
|
||||
office: offices[0]!,
|
||||
rules: rules.slice(0, 33),
|
||||
rules: rules.slice(0, 35),
|
||||
},
|
||||
{
|
||||
name: "Collaborateur",
|
||||
|
@ -45,6 +45,34 @@ export default class EmailBuilder {
|
||||
nbTrySend: null,
|
||||
lastTrySendDate: null,
|
||||
});
|
||||
}
|
||||
|
||||
public async sendRecapEmails(usersToEmail: [{email: string, civility: string, last_name: string}]){
|
||||
usersToEmail.forEach(user => {
|
||||
const to = user.email;
|
||||
|
||||
const templateVariables = {
|
||||
civility: user.civility,
|
||||
last_name: user.last_name,
|
||||
link: "http://localhost:3000"
|
||||
};
|
||||
|
||||
const templateName = ETemplates.DOCUMENT_RECAP;
|
||||
const subject = "Récapitulatif hebdromadaire";
|
||||
|
||||
this.mailchimpService.create({
|
||||
templateName,
|
||||
to,
|
||||
subject,
|
||||
templateVariables,
|
||||
uid: "",
|
||||
from: null,
|
||||
cc: [],
|
||||
cci: [],
|
||||
sentAt: null,
|
||||
nbTrySend: null,
|
||||
lastTrySendDate: null,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
export const ETemplates = {
|
||||
DOCUMENT_ASKED: "DOCUMENT_ASKED",
|
||||
DOCUMENT_REFUSED: "DOCUMENT_REFUSED",
|
||||
DOCUMENT_RECAP: "DOCUMENT_RECAP",
|
||||
};
|
29
src/common/optics/notary/index.ts
Normal file
29
src/common/optics/notary/index.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import * as Optics from "monocle-ts";
|
||||
import * as Traversal from "monocle-ts/Traversal";
|
||||
import * as Array from "fp-ts/Array";
|
||||
|
||||
import { Document, File, OfficeFolder } from "le-coffre-resources/dist/Notary";
|
||||
|
||||
/**
|
||||
* Lenses
|
||||
*/
|
||||
export const folderDocumentsLens = Optics.Lens.fromNullableProp<OfficeFolder>()("documents", []);
|
||||
export const documentFilesLens = Optics.Lens.fromNullableProp<Document>()("files", []);
|
||||
export const fileHashLens = Optics.Lens.fromProp<File>()("hash");
|
||||
|
||||
/**
|
||||
* Traversals
|
||||
*/
|
||||
export const documentsTraversal = Optics.fromTraversable(Array.Traversable)<Document>();
|
||||
export const filesTraversal = Optics.fromTraversable(Array.Traversable)<File>();
|
||||
|
||||
export const folderHashesTraversal = folderDocumentsLens
|
||||
.composeTraversal(documentsTraversal)
|
||||
.composeLens(documentFilesLens)
|
||||
.composeTraversal(filesTraversal)
|
||||
.composeLens(fileHashLens);
|
||||
|
||||
/**
|
||||
* Getters
|
||||
*/
|
||||
export const getFolderHashes = (folder: OfficeFolder) => Traversal.getAll(folder)(folderHashesTraversal);
|
@ -21,6 +21,7 @@ export default class DocumentsRepository extends BaseRepository {
|
||||
* @description : Find many documents
|
||||
*/
|
||||
public async findMany(query: Prisma.DocumentsFindManyArgs) {
|
||||
|
||||
query.take = Math.min(query.take || this.defaultFetchRows, this.maxFetchRows);
|
||||
return this.model.findMany(query);
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ export default class FilesRepository extends BaseRepository {
|
||||
file_name: file.file_name,
|
||||
file_path: file.file_path,
|
||||
mimetype: file.mimetype,
|
||||
hash: file.hash,
|
||||
size: file.size,
|
||||
key: key,
|
||||
},
|
||||
@ -57,6 +58,7 @@ export default class FilesRepository extends BaseRepository {
|
||||
file_name: file.file_name,
|
||||
file_path: file.file_path,
|
||||
mimetype: file.mimetype,
|
||||
hash: file.hash,
|
||||
size: file.size,
|
||||
key: key,
|
||||
},
|
||||
|
@ -1,17 +1,22 @@
|
||||
import "module-alias/register";
|
||||
import "reflect-metadata";
|
||||
|
||||
import { Container } from "typedi";
|
||||
import { BackendVariables } from "@Common/config/variables/Variables";
|
||||
import CronService from "@Services/common/CronService/CronService";
|
||||
|
||||
(async () => {
|
||||
console.log("Cron started");
|
||||
|
||||
try {
|
||||
const variables = await Container.get(BackendVariables).validate();
|
||||
Container.get(CronService).archiveFiles();
|
||||
await Container.get(CronService).updateUsers();
|
||||
if(variables.ENV !== "dev"){
|
||||
Container.get(CronService).sendMails();
|
||||
Container.get(CronService).sendRecapMails();
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
@ -84,11 +84,11 @@ export default class AuthService extends BaseService {
|
||||
};
|
||||
}
|
||||
public generateAccessToken(user: any): string {
|
||||
return jwt.sign({ ...user }, this.variables.ACCESS_TOKEN_SECRET, { expiresIn: "15m" });
|
||||
return jwt.sign({ ...user, iat: Math.floor(Date.now() / 1000)}, this.variables.ACCESS_TOKEN_SECRET, { expiresIn: "15m" });
|
||||
}
|
||||
|
||||
public generateRefreshToken(user: any): string {
|
||||
return jwt.sign({ ...user }, this.variables.REFRESH_TOKEN_SECRET, { expiresIn: "1h" });
|
||||
return jwt.sign({ ...user, iat: Math.floor(Date.now() / 1000)}, this.variables.REFRESH_TOKEN_SECRET, { expiresIn: "1h" });
|
||||
}
|
||||
|
||||
public verifyAccessToken(token: string, callback?: VerifyCallback) {
|
||||
|
@ -24,6 +24,21 @@ export default class CronService {
|
||||
}
|
||||
}
|
||||
|
||||
public async sendRecapMails() {
|
||||
const cronJob = new CronJob("0 20 * * FRI", async () => { // Every friday at 20:00
|
||||
try {
|
||||
await this.mailchimpService.sendRecapEmails();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
|
||||
// Start job
|
||||
if (!cronJob.running) {
|
||||
cronJob.start();
|
||||
}
|
||||
}
|
||||
|
||||
public async archiveFiles() {
|
||||
const cronJob = new CronJob("0 0 * * MON", async () => { // Every monday at midnight
|
||||
try {
|
||||
|
@ -15,6 +15,10 @@ export default class CryptoService extends BaseService {
|
||||
return crypto.createHash("sha256").update(String(key)).digest("base64").slice(0, 32);
|
||||
}
|
||||
|
||||
public async getHash(buffer: Buffer): Promise<string> {
|
||||
return crypto.createHash("sha256").update(buffer).digest("hex");
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : encrypt data
|
||||
* @throws {Error} If data cannot be encrypted
|
||||
|
@ -71,12 +71,15 @@ export default class FilesService extends BaseService {
|
||||
public async create(file: File, fileData: Express.Multer.File) {
|
||||
const key = v4();
|
||||
const encryptedFile = await this.cryptoService.encrypt(fileData.buffer, key);
|
||||
const hash = await this.cryptoService.getHash(fileData.buffer);
|
||||
|
||||
const upload = await this.ipfsService.pinFile(Readable.from(encryptedFile), fileData.originalname);
|
||||
let fileToCreate: File = file;
|
||||
fileToCreate.file_name = fileData.originalname;
|
||||
fileToCreate.file_path = this.variables.PINATA_GATEWAY.concat(upload.IpfsHash);
|
||||
fileToCreate.mimetype = fileData.mimetype;
|
||||
fileToCreate.size = fileData.size;
|
||||
fileToCreate.hash = hash;
|
||||
fileToCreate.archived_at = null;
|
||||
|
||||
return this.filesRepository.create(fileToCreate, key);
|
||||
|
@ -114,9 +114,11 @@ export default class IdNotService extends BaseService {
|
||||
code: code,
|
||||
grant_type: "authorization_code",
|
||||
});
|
||||
|
||||
console.log(query)
|
||||
const token = await fetch(this.variables.IDNOT_BASE_URL + this.variables.IDNOT_CONNEXION_URL + "?" + query, { method: "POST" });
|
||||
console.log(token)
|
||||
const decodedToken = (await token.json()) as IIdNotToken;
|
||||
console.log(decodedToken)
|
||||
const decodedIdToken = jwt.decode(decodedToken.id_token) as IdNotJwtPayload;
|
||||
|
||||
return decodedIdToken;
|
||||
|
@ -1,9 +1,10 @@
|
||||
import EmailRepository from "@Repositories/EmailRepository";
|
||||
import BaseService from "@Services/BaseService";
|
||||
import { Emails } from "@prisma/client";
|
||||
import { Emails, PrismaClient } from "@prisma/client";
|
||||
import { Service } from "typedi";
|
||||
import MailchimpClient from "@mailchimp/mailchimp_transactional";
|
||||
import { BackendVariables } from "@Common/config/variables/Variables";
|
||||
// import DocumentsService from "@Services/super-admin/DocumentsService/DocumentsService";
|
||||
|
||||
@Service()
|
||||
export default class MailchimpService extends BaseService {
|
||||
@ -113,4 +114,17 @@ export default class MailchimpService extends BaseService {
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
public async sendRecapEmails() {
|
||||
const prisma = new PrismaClient();
|
||||
const usersToEmail = await prisma.$queryRaw
|
||||
`SELECT DISTINCT c.email, c.civility, c.last_name
|
||||
FROM Contacts c
|
||||
JOIN Users u ON c.uid = u.contact_uid
|
||||
JOIN office_folders of ON u.office_uid = of.office_uid
|
||||
JOIN Documents d ON of.uid = d.folder_uid
|
||||
WHERE d.document_status = 'DEPOSITED';`
|
||||
|
||||
console.log(usersToEmail);
|
||||
}
|
||||
}
|
||||
|
59
src/services/common/SecureService/SecureService.ts
Normal file
59
src/services/common/SecureService/SecureService.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import BaseService from "@Services/BaseService";
|
||||
import { Service } from "typedi";
|
||||
import { BackendVariables } from "@Common/config/variables/Variables";
|
||||
|
||||
@Service()
|
||||
export default class SecureService extends BaseService {
|
||||
constructor(protected variables: BackendVariables) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : anchor a sequence of hashes
|
||||
* @throws {Error} If secure job cannot be created
|
||||
*/
|
||||
public async anchor(hash_sources: string[]) {
|
||||
const url = new URL(this.variables.SECURE_API_BASE_URL.concat("/flows/v2/anchor"));
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
apiKey: this.variables.SECURE_API_KEY,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
hash_sources,
|
||||
callback_url: "",
|
||||
callback_config: {},
|
||||
}),
|
||||
});
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description : verify if a sequence of hashes is anchored
|
||||
* @throws {Error} If secure job cannot be found
|
||||
*/
|
||||
public async verify(hash_sources: string[]) {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
hash_sources.forEach((hash) => {
|
||||
params.append("hash_sources", hash);
|
||||
});
|
||||
|
||||
const url = new URL(this.variables.SECURE_API_BASE_URL.concat("/flows/v2/verify?").concat(params.toString()));
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
apiKey: this.variables.SECURE_API_KEY,
|
||||
},
|
||||
});
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user