Merge branch 'staging' into preprod

This commit is contained in:
Vins 2023-09-20 10:30:19 +02:00
commit a26865c318
17 changed files with 655 additions and 219 deletions

View File

@ -1,6 +1,6 @@
dockerPullSecret: docker-pull-secret
scwSecretKey: AgChoEnPitXp4Ny/rVMEcevaWKNVpyj2cJYAcq+yFqKwVwnLB+ffDvwqz9XBHu+6d4Nyyjkf37zUAMoaM21lEDWA7x3zfG2/D/j+rvX1qxzZgLD0mjBk7fGElVm332I6JA83oInes8AMMYEDPLElzHnpKRb9KtkIP4NzgOcCeW0ijft3N7Vroez6LEHsBPCA1I9XjKSkGEDvrO0MhWX3iJOlfz+SPMfJAV7rPawOs0ZmohTHrPW8qIvGDn8HCzKyU8zRBoMt+Ogpf5pH4U3JryEFuqD61KAQgablAM8edPIvsgNno9HAEuC2QtRLYA9aUhuKdaKuS58c9P2E80PHWXIlbpFCg6EugQTgNfnYp+3qDUNz8edeCfapYLvF4s9eCMGyMsGnpDR8EDNOyuGy7Y3l7okX8Xqu464gMp9E+hX7bHkcD6a4xfyIgJcWxsku0tm1TH1dpn4M1UXRuyZZif8P08nuE6MTUL67sAR9J1lpn4lVEL4kflk0pP2tZ5ncgPQFafJrRz05krMb0eU5tb2H4gs7ao/LL6idWo8MM9K1yr8lIuT5x2WW5CX+RjA+i50ex114V6vX3PNP5oVyt+DynTUB9QmXzVm3oLfDc3Cae1uqh7X0CFd+xiztJBtg0VtJaD/xUJcuWfY4cV2lERo9fRrykltzlJqiXHO4nowt8OtN0BcViVV8NJhPhYFzyb4ympxpOlTjm3GETuT2TYhUqdgS9nzleEAbOmOHZdIO2COunPE=
scwSecretKey: AgCgjF5QEzxT3GYTS5B6cmQ0e+0/qFWzKaUDSi+Vjc7RoameuvaIJvTXMBkS3he1oy1ulbB34v6vpZI2kxnGNqERA/U5BaYDAyfKSBwMAy4br7HVKhhuwkoF5qoG5JzJXseSmqB1U9vncVIGOZWzJc1Y4/eGlWcvLcLyfw2z/WEpyeNiWJfEhTYpJOB7gv0XnRb2U/JM3jRy1QgEUIk1WR6kgBalF+xaczPQ6uKh+PR2pqkbZa3WaKUrddmzNsgEz4d8PZMWt8IBwR2JOQEHUqCd34p/pJNyLdUgcdDhg02DKwn1oRoAxKTbAio/a7WrMbodjCb3TNWIYGal5mFmItZ7Ok/EBmUf4E85eOkTR+j8ynuuiexld3Q5Kw3o8LsHjgzVL9uP+T2rYaKkjtVt+YQRX1U8l9CrsdUEz0/wEBA0jwCWMfnh1qhD5pM/xwwjsEEAcK4rYV+Q7iAgGZZvZBCQ5aEHzrtn5D95tr1GZCV2hmrW6Seu+LKKLVBS1JmsuEsOuhudYsEK9m2RYVcxbjuS5eokKEjNrGobf2oB8rhBByavfw1JTBixR5JrI8lcYlnCa+oEhxXKJY+4Fx5SAB4YaLCMSo5vw6zsFQ3WKQzlEmCFt+EnapS+a+MGrdlwq07OHTDpvgk/1z39hopoCuhhKckGGfErLXsTYQvDOkFu+EPzgY7m7qDw/d9pSiht5tuSOkAqeOgm7tpNkUufZhaXmP+1aT7i+H5gq1JILGAmXzTI5Wc=
lecoffreBack:
serviceAccountName: lecoffre-back-sa

View File

@ -54,7 +54,7 @@ export default class CustomerController extends ApiController {
});
//success
this.httpSuccess(response, accessToken);
this.httpSuccess(response, {accessToken});
} catch (error) {
this.httpInternalError(response);
return;

View File

@ -5,10 +5,13 @@ import { Service } from "typedi";
import AuthService from "@Services/common/AuthService/AuthService";
import { JwtPayload } from "jsonwebtoken";
import IdNotService from "@Services/common/IdNotService/IdNotService";
@Controller()
@Service()
export default class UserController extends ApiController {
constructor(private authService: AuthService) {
constructor(private authService: AuthService, private idNotService: IdNotService) {
super();
}
@ -19,16 +22,21 @@ export default class UserController extends ApiController {
*/
@Post("/api/v1/idnot/user/:code")
protected async getUserInfosFromIdnot(req: Request, response: Response) {
console.warn("/api/v1/idnot/user/:code used for test, should be removed");
try {
const code = req.params["code"];
if (!code) throw new Error("code is required");
const token = await fetch("https://qual-connexion.idnot.fr/IdPOAuth2/token/idnot_idp_v1", { method: "POST" });
console.log(token);
//const user = await this.authService.getUserFromIdNotTokens(code!);
//success
this.httpSuccess(response);
const idNotToken = await this.idNotService.getIdNotToken(code);
const user = await this.idNotService.getOrCreateUser(idNotToken);
await this.idNotService.updateUser(user.uid);
await this.idNotService.updateOffice(user.office_uid);
const payload = await this.authService.getUserJwtPayload(user.idNot);
const accessToken = this.authService.generateAccessToken(payload);
const refreshToken = this.authService.generateRefreshToken(payload);
this.httpSuccess(response, {accessToken, refreshToken});
} catch (error) {
console.log(error);
this.httpInternalError(response);
@ -55,7 +63,7 @@ export default class UserController extends ApiController {
}
}
@Post("/api/v1/idnot/user/refresh-token")
@Post("/api/v1/idnot/user/auth/refresh-token")
protected async refreshToken(req: Request, response: Response) {
try {
const authHeader = req.headers["authorization"];
@ -69,6 +77,7 @@ export default class UserController extends ApiController {
let accessToken;
this.authService.verifyRefreshToken(token, (err, userPayload) => {
if (err) {
console.log(err)
this.httpUnauthorized(response);
return;
}
@ -80,7 +89,7 @@ export default class UserController extends ApiController {
});
//success
this.httpSuccess(response, accessToken);
this.httpSuccess(response, {accessToken});
} catch (error) {
this.httpInternalError(response);
return;

View File

@ -3,7 +3,7 @@ import { Controller, Get } from "@ControllerPattern/index";
import ApiController from "@Common/system/controller-pattern/ApiController";
import UsersService from "@Services/notary/UsersService/UsersService";
import { Service } from "typedi";
import User from "le-coffre-resources/dist/Notary";
import User from "le-coffre-resources/dist/Notary/User";
import { Prisma } from "@prisma/client";
import authHandler from "@App/middlewares/AuthHandler";
import ruleHandler from "@App/middlewares/RulesHandler";
@ -34,7 +34,7 @@ export default class UsersController extends ApiController {
//call service to get prisma entity
const usersEntities = await this.usersService.get(query);
//Hydrate ressource with prisma entity
const users = User.hydrateArray<User>(usersEntities, { strategy: "excludeAll" });

View File

@ -34,6 +34,12 @@ export class BackendVariables {
@IsNotEmpty()
public readonly APP_ROOT_URL!: string;
@IsNotEmpty()
public readonly IDNOT_BASE_URL!: string;
@IsNotEmpty()
public readonly IDNOT_API_BASE_URL!: string;
@IsNotEmpty()
public readonly IDNOT_CONNEXION_URL!: string;
@ -46,6 +52,9 @@ export class BackendVariables {
@IsNotEmpty()
public readonly IDNOT_REDIRECT_URL!: string;
@IsNotEmpty()
public readonly IDNOT_API_KEY!: string;
@IsNotEmpty()
public readonly PINATA_API_KEY!: string;
@ -80,10 +89,13 @@ export class BackendVariables {
this.APP_PORT = process.env["APP_PORT"]!;
this.APP_ROOT_URL = process.env["APP_ROOT_URL"]!;
this.APP_LABEL = process.env["APP_LABEL"]!;
this.IDNOT_BASE_URL = process.env["IDNOT_BASE_URL"]!;
this.IDNOT_API_BASE_URL = process.env["IDNOT_API_BASE_URL"]!;
this.IDNOT_CONNEXION_URL = process.env["IDNOT_CONNEXION_URL"]!;
this.IDNOT_CLIENT_ID = process.env["IDNOT_CLIENT_ID"]!;
this.IDNOT_CLIENT_SECRET = process.env["IDNOT_CLIENT_SECRET"]!;
this.IDNOT_REDIRECT_URL = process.env["IDNOT_REDIRECT_URL"]!;
this.IDNOT_API_KEY = process.env["IDNOT_API_KEY"]!;
this.PINATA_API_KEY = process.env["PINATA_API_KEY"]!;
this.PINATA_API_SECRET = process.env["PINATA_API_SECRET"]!;
this.PINATA_GATEWAY = process.env["PINATA_GATEWAY"]!;

View File

@ -0,0 +1,5 @@
-- AlterTable
ALTER TABLE "offices" ADD COLUMN "checked_at" TIMESTAMP(3);
-- AlterTable
ALTER TABLE "users" ADD COLUMN "checked_at" TIMESTAMP(3);

View File

@ -6,8 +6,8 @@ generator client {
}
datasource db {
provider = "postgresql"
url = env("DEV_PRISMA_STUDIO_DB_URL")
provider = "postgresql"
url = env("DEV_PRISMA_STUDIO_DB_URL")
shadowDatabaseUrl = env("DEV_PRISMA_STUDIO_SHADOW_URL")
}
@ -61,6 +61,7 @@ model Users {
office_role_uid String? @db.VarChar(255)
created_at DateTime? @default(now())
updated_at DateTime? @updatedAt
checked_at DateTime?
office_membership Offices @relation(fields: [office_uid], references: [uid], onDelete: Cascade)
office_uid String @db.VarChar(255)
notifications Notifications[] @relation("UserHasNotifications")
@ -81,6 +82,7 @@ model Offices {
office_status EOfficeStatus @default(DESACTIVATED)
created_at DateTime? @default(now())
updated_at DateTime? @updatedAt
checked_at DateTime?
deed_types DeedTypes[]
users Users[]
office_folders OfficeFolders[]

View File

@ -598,20 +598,8 @@ export default async function main() {
updated_at: new Date(),
},
{
name: "PUT users",
label: "Modification des utilisateurs",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "PUT offices",
label: "Modification des offices",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "PUT customers",
label: "Modification des clients",
name: "POST deeds",
label: "Création des actes",
created_at: new Date(),
updated_at: new Date(),
},
@ -622,38 +610,8 @@ export default async function main() {
updated_at: new Date(),
},
{
name: "PUT deed-types",
label: "Modification des types d'actes",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "PUT documents",
label: "Modification des documents",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "PUT document-types",
label: "Modification des types de documents",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "PUT files",
label: "Modification des fichiers",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "PUT folders",
label: "Modification des dossiers",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "PUT roles",
label: "Modification des rôles",
name: "DELETE deeds",
label: "Suppression des actes",
created_at: new Date(),
updated_at: new Date(),
},
@ -664,128 +622,8 @@ export default async function main() {
updated_at: new Date(),
},
{
name: "PUT rules",
label: "Modification des droits",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "PUT office-roles",
label: "Modification des rôles d'office",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "POST users",
label: "Création des utilisateurs",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "POST offices",
label: "Création des offices",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "POST customers",
label: "Création des clients",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "POST deeds",
label: "Création des actes",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "POST deed-types",
label: "Création des types d'actes",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "POST documents",
label: "Création des documents",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "POST document-types",
label: "Création des types de documents",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "POST files",
label: "Création des fichiers",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "POST roles",
label: "Création des rôles",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "POST rules",
label: "Création des droits",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "POST office-roles",
label: "Création des rôles d'office",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "DELETE users",
label: "Suppression des utilisateurs",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "DELETE offices",
label: "Suppression des offices",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "DELETE customers",
label: "Suppression des clients",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "DELETE deeds",
label: "Suppression des actes",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "DELETE deed-types",
label: "Suppression des types d'actes",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "DELETE documents",
label: "Suppression des documents",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "DELETE document-types",
label: "Suppression des types de documents",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "DELETE files",
label: "Suppression des fichiers",
name: "PUT folders",
label: "Modification des dossiers",
created_at: new Date(),
updated_at: new Date(),
},
@ -796,14 +634,86 @@ export default async function main() {
updated_at: new Date(),
},
{
name: "DELETE roles",
label: "Suppression des rôles",
name: "POST documents",
label: "Création des documents",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "DELETE rules",
label: "Suppression des droits",
name: "PUT documents",
label: "Modification des documents",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "DELETE documents",
label: "Suppression des documents",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "POST customers",
label: "Création des clients",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "PUT customers",
label: "Modification des clients",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "DELETE customers",
label: "Suppression des clients",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "POST deed-types",
label: "Création des types d'actes",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "PUT deed-types",
label: "Modification des types d'actes",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "DELETE deed-types",
label: "Suppression des types d'actes",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "POST document-types",
label: "Création des types de documents",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "PUT document-types",
label: "Modification des types de documents",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "DELETE document-types",
label: "Suppression des types de documents",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "POST office-roles",
label: "Création des rôles d'office",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "PUT office-roles",
label: "Modification des rôles d'office",
created_at: new Date(),
updated_at: new Date(),
},
@ -813,6 +723,24 @@ export default async function main() {
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(),
},
{
name: "PUT users",
label: "Modification des utilisateurs",
created_at: new Date(),
updated_at: new Date(),
},
{
name: "PUT offices",
label: "Modification des offices",
created_at: new Date(),
updated_at: new Date(),
},
];
const roles: Role[] = [
@ -842,7 +770,7 @@ export default async function main() {
label: "Utilisateur",
created_at: new Date(),
updated_at: new Date(),
rules: rules.slice(0, 11),
rules: rules.slice(0,1),
},
];
@ -1378,7 +1306,7 @@ export default async function main() {
city: office.address!.city,
},
},
office_status: EOfficeStatus.DESACTIVATED,
office_status: EOfficeStatus.ACTIVATED,
},
});
office.uid = officeCreated.uid;

View File

@ -72,6 +72,31 @@ export default class OfficesRepository extends BaseRepository {
return this.model.update(updateArgs);
}
/**
* @description : Update check date of an office
*/
public async updateCheckedAt(uid: string) {
return this.model.update({
where: {
uid: uid,
},
data: {
checked_at: new Date(),
},
});
}
/**
* @description : Delete an office
*/
public async delete(uid: string): Promise<Offices> {
return this.model.delete({
where: {
uid: uid,
},
});
}
/**
* @description : Find one office
*/
@ -93,4 +118,25 @@ export default class OfficesRepository extends BaseRepository {
include: query,
});
}
/**
* @description : Find offices which need to be checked with idNot API
*/
public async findManyToCheck() {
return this.model.findMany({
where: {
OR: [
{
checked_at: null,
},
{
checked_at: { lt: new Date(Date.now() - 3 * 60 * 1000) }, // less than 24h ago
},
],
},
include: {
address: true,
},
});
}
}

View File

@ -87,7 +87,7 @@ export default class UsersRepository extends BaseRepository {
/**
* @description : Update data from a user
*/
public async update(uid: string, user: User): Promise<Users> {
const updateArgs: Prisma.UsersUpdateArgs = {
where: {
@ -108,7 +108,7 @@ export default class UsersRepository extends BaseRepository {
},
};
if(user.office_membership) {
if (user.office_membership) {
updateArgs.data.office_membership = {
connect: {
idNot: user.office_membership?.idNot,
@ -124,7 +124,7 @@ export default class UsersRepository extends BaseRepository {
};
}
if(user.office_role) {
if (user.office_role) {
updateArgs.data.office_role = {
connect: {
uid: user.office_role.uid,
@ -132,7 +132,7 @@ export default class UsersRepository extends BaseRepository {
};
}
if(user.role) {
if (user.role) {
updateArgs.data.role = {
connect: {
uid: user.role.uid,
@ -144,17 +144,31 @@ export default class UsersRepository extends BaseRepository {
}
/**
* @description : Update role from a user
* @description : Update check date of a user
*/
public async updateRole(uid: string, user: User): Promise<Users> {
public async updateCheckedAt(uid: string) {
return this.model.update({
where: {
uid: uid,
},
data: {
checked_at: new Date(),
},
});
}
/**
* @description : Update office role from a user
*/
public async updateOfficeRole(uid: string, user: User): Promise<Users> {
const updateArgs: Prisma.UsersUpdateArgs = {
where: {
uid: uid,
},
data: {}
data: {},
};
if(user.office_role) {
if (user.office_role) {
updateArgs.data.office_role = {
connect: {
uid: user.office_role.uid,
@ -162,17 +176,20 @@ export default class UsersRepository extends BaseRepository {
};
}
if(user.role) {
updateArgs.data.role = {
connect: {
uid: user.role.uid,
},
};
}
return this.model.update({ ...updateArgs, include: { contact: true, office_membership: { include: { address: true } } } });
}
/**
* @description : Delete one user
*/
public async delete(uid: string): Promise<Users> {
return this.model.delete({
where: {
uid: uid,
},
});
}
/**
* @description : Find one user
*/
@ -200,7 +217,7 @@ export default class UsersRepository extends BaseRepository {
/**
* @description : Find one user with office
*/
public async findOneByUidWithRole(uid: string): Promise<((Users & {role: Role} )| null)> {
public async findOneByUidWithRole(uid: string): Promise<(Users & { role: Role }) | null> {
return this.model.findUnique({
where: {
uid: uid,
@ -222,4 +239,27 @@ export default class UsersRepository extends BaseRepository {
},
});
}
/**
* @description : Find users which need to be checked with idNot API
*/
public async findManyToCheck() {
return this.model.findMany({
where: {
OR: [
{
checked_at: null,
},
{
checked_at: { lt: new Date(Date.now() - 3 * 60 * 1000) }, // less than 24h ago
},
],
},
include: {
contact: true,
role: true,
office_membership: true,
},
});
}
}

View File

@ -8,6 +8,7 @@ import CronService from "@Services/common/CronService/CronService";
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();
}

View File

@ -19,20 +19,12 @@ export default class UsersService extends BaseService {
return this.userRepository.findMany(query);
}
/**
* @description : Create a user
* @throws {Error} If user couldn't be created
*/
public create(userEntity: User): Promise<Users> {
return this.userRepository.create(userEntity);
}
/**
* @description : Modify a user
* @throws {Error} If user modification failed
*/
public async update(uid: string, userEntity: User): Promise<Users> {
return this.userRepository.updateRole(uid, userEntity);
return this.userRepository.updateOfficeRole(uid, userEntity);
}
/**

View File

@ -15,7 +15,12 @@ interface ICustomerJwtPayload {
customerId: string;
email: string;
}
export interface IdNotJwtPayload {
sub: string,
profile_idn: string,
entity_idn: string,
}
interface IUserJwtPayload {
userId: string;
@ -79,7 +84,7 @@ export default class AuthService extends BaseService {
};
}
public generateAccessToken(user: any): string {
return jwt.sign({ ...user }, this.variables.ACCESS_TOKEN_SECRET, { expiresIn: "1h" });
return jwt.sign({ ...user }, this.variables.ACCESS_TOKEN_SECRET, { expiresIn: "15m" });
}
public generateRefreshToken(user: any): string {

View File

@ -2,11 +2,12 @@ import { Service } from "typedi";
import { CronJob } from "cron";
import MailchimpService from "../MailchimpService/MailchimpService";
import FilesService from "../FilesService/FilesService";
import IdNotService from "../IdNotService/IdNotService";
@Service()
export default class CronService {
constructor(private mailchimpService: MailchimpService, private filesService: FilesService) {}
constructor(private mailchimpService: MailchimpService, private filesService: FilesService, private idNotService: IdNotService) {}
public async sendMails() {
const cronJob = new CronJob("*/15 * * * *", async () => { // Every 15 minutes
@ -37,4 +38,18 @@ export default class CronService {
cronJob.start();
}
}
public async updateUsers() {
const cronJob = new CronJob("*/15 * * * *", async () => { // Every 15 minutes
try {
await this.idNotService.updateOffices();
await this.idNotService.updateUsers();
} catch (e) {
console.error(e);
}
});
// Start job
if (!cronJob.running) {
cronJob.start();
}
}
}

View File

@ -0,0 +1,332 @@
import BaseService from "@Services/BaseService";
import { BackendVariables } from "@Common/config/variables/Variables";
import { Service } from "typedi";
import jwt from "jsonwebtoken";
import UsersService from "@Services/super-admin/UsersService/UsersService";
import { IdNotJwtPayload } from "../AuthService/AuthService";
import User, { Office, Role } from "le-coffre-resources/dist/SuperAdmin";
import RolesService from "@Services/super-admin/RolesService/RolesService";
import OfficesService from "@Services/super-admin/OfficesService/OfficesService";
import { EOfficeStatus } from "@prisma/client";
interface IIdNotToken {
access_token: string;
expires_in: number;
id_token: string;
token_type: string;
}
interface IRattachementData {
entiteUrl: string;
personneUrl: string;
entite: {
codeCrpcen: string;
typeEntite: {
name: string;
};
ou: string;
denominationSociale: string;
statutEntite: {
name: string;
};
locationsUrl: string;
}
personne: {
numeroTelephonePro: string;
prenom: string;
nomUsuel: string;
mobilePro: string;
numeroAdherentCrpcen: string;
civilite: string;
};
typeLien: {
name: string;
};
numeroMobile: string;
numeroTelephone: string;
statutDuRattachement: boolean;
mailRattachement: string;
}
interface IOfficeData {
ou: string;
intitule: string;
denominationSociale: string;
departementResidence: {
libelle: string;
code: string;
}[];
libelle: string;
codeCrpcen: string;
locationsUrl: string;
statutEntite: {
name: string;
};
}
interface IOfficeLocation {
totalResultCount: number;
page: number;
size: number;
result: {
mail: string;
numeroTelephone: string;
adrGeo4: string;
adrGeoCodePostal: string;
adrGeoVille: string;
ou: string;
lastModified: string;
deleted: boolean;
}[];
}
enum EIdnotRole {
DIRECTEUR = "Directeur général du CSN",
NOTAIRE_TITULAIRE = "Notaire titulaire",
NOTAIRE_ASSOCIE = "Notaire associé",
NOTAIRE_SALARIE = "Notaire salarié",
COLLABORATEUR = "Collaborateur",
SECRETAIRE_GENERAL = "Secrétaire général",
SUPPLEANT = "Suppléant",
ADMINISTRATEUR = "Administrateur",
RESPONSABLE = "Responsable",
}
@Service()
export default class IdNotService extends BaseService {
constructor(
protected variables: BackendVariables,
private userService: UsersService,
private officeService: OfficesService,
private rolesService: RolesService,
) {
super();
}
public async getIdNotToken(code: string) {
const query = new URLSearchParams({
client_id: this.variables.IDNOT_CLIENT_ID,
client_secret: this.variables.IDNOT_CLIENT_SECRET,
redirect_uri: `http://0.0.0.0:3000/authorized-client`,
code: code,
grant_type: "authorization_code",
});
const token = await fetch(this.variables.IDNOT_BASE_URL + this.variables.IDNOT_CONNEXION_URL + "?" + query, { method: "POST" });
const decodedToken = (await token.json()) as IIdNotToken;
const decodedIdToken = jwt.decode(decodedToken.id_token) as IdNotJwtPayload;
return decodedIdToken;
}
public async getRole(roleName: string): Promise<Role> {
switch (roleName) {
case EIdnotRole.DIRECTEUR:
return (await this.rolesService.get({ where: { name: "admin" } }))[0]!;
case EIdnotRole.NOTAIRE_TITULAIRE:
return (await this.rolesService.get({ where: { name: "notary" } }))[0]!;
case EIdnotRole.NOTAIRE_ASSOCIE:
return (await this.rolesService.get({ where: { name: "notary" } }))[0]!;
case EIdnotRole.NOTAIRE_SALARIE:
return (await this.rolesService.get({ where: { name: "notary" } }))[0]!;
case EIdnotRole.COLLABORATEUR:
return (await this.rolesService.get({ where: { name: "collaborator" } }))[0]!;
case EIdnotRole.SECRETAIRE_GENERAL:
return (await this.rolesService.get({ where: { name: "notary" } }))[0]!;
case EIdnotRole.SUPPLEANT:
return (await this.rolesService.get({ where: { name: "notary" } }))[0]!;
case EIdnotRole.ADMINISTRATEUR:
return (await this.rolesService.get({ where: { name: "admin" } }))[0]!;
case EIdnotRole.RESPONSABLE:
return (await this.rolesService.get({ where: { name: "collaborator" } }))[0]!;
default:
return (await this.rolesService.get({ where: { name: "default" } }))[0]!;
}
}
public getOfficeStatus(statusName: string) {
switch (statusName) {
case "Pourvu":
return EOfficeStatus.ACTIVATED;
case "Pourvu mais décédé":
return EOfficeStatus.ACTIVATED;
case "Sans titulaire":
return EOfficeStatus.ACTIVATED;
case "Vacance":
return EOfficeStatus.ACTIVATED;
case "En activité":
return EOfficeStatus.ACTIVATED;
default:
return EOfficeStatus.DESACTIVATED;
}
}
public async updateUser(userId: string) {
const userInfos = await this.userService.getByUid(userId, { contact: true, role: true, office_membership: true });
const user = User.hydrate<User>(userInfos!);
const searchParams = new URLSearchParams({
key: this.variables.IDNOT_API_KEY,
});
const userRawData = await (await fetch(
`${this.variables.IDNOT_API_BASE_URL}/api/pp/v2/personnes/${user.idNot}/rattachements?` +
searchParams,
{
method: "GET",
},
)).json() as any;
if (userRawData.totalResultCount === 0) {
await this.userService.updateCheckedAt(user.uid!);
//await this.userService.delete(user.uid!);
return;
}
const userData = userRawData.result[0] as IRattachementData;
const roleFromIdNot = await this.getRole(userData.typeLien.name);
let updates = 0;
if (user.role!.uid !== roleFromIdNot.uid) {
updates++;
user.role = roleFromIdNot;
}
if (user.office_membership!.idNot !== userData.entiteUrl.split("/")[5]!) {
updates++;
let officeData = (await this.officeService.get({ where: { idNot: userData.entiteUrl.split("/")[5]! } }))[0];
if (!officeData) {
const officeIdNotData = (await (
await fetch(`${this.variables.IDNOT_API_BASE_URL + userData.entiteUrl}?` + searchParams, { method: "GET" })
).json()) as IOfficeData;
const office = {
idNot: userData.entiteUrl.split("/")[5]!,
name: officeIdNotData.denominationSociale
? officeIdNotData.denominationSociale
: `office ${userData.entiteUrl.split("/")[5]!}`,
crpcen: officeIdNotData.codeCrpcen,
office_status: this.getOfficeStatus(officeIdNotData.statutEntite.name),
address: {
address: officeIdNotData.departementResidence[0]!.libelle, //officeLocationData.result[0]!.adrPostale4,
city: "city", //officeLocationData.result[0]!.adrPostaleVille,
zip_code: Number(officeIdNotData.departementResidence[0]!.code),
created_at: null,
updated_at: null,
},
created_at: null,
updated_at: null,
};
officeData = await this.officeService.create(office);
}
user.office_membership = officeData;
}
if (user.contact!.email !== userData.mailRattachement) {
updates++;
user.contact!.email = userData.mailRattachement;
}
if (user.contact!.cell_phone_number !== userData.numeroMobile) {
updates++;
user.contact!.cell_phone_number = userData.numeroMobile;
}
if (updates != 0) await this.userService.update(user.uid!, user);
await this.userService.updateCheckedAt(user.uid!);
}
public async updateOffice(officeId: string) {
const officeInfos = await this.officeService.getByUid(officeId);
const office = Office.hydrate<Office>(officeInfos!);
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,
{
method: "GET",
},
);
if (officeRawData.status === 404) {
await this.officeService.updateCheckedAt(office.uid!);
//await this.officeService.delete(office.uid!);
return;
}
const officeData = (await officeRawData.json()) as IOfficeData;
let updates = 0;
if(office.name !== officeData.denominationSociale) {
updates++;
office.name = officeData.denominationSociale;
}
if(office.office_status !== this.getOfficeStatus(officeData.statutEntite.name)) {
updates++;
office.office_status = this.getOfficeStatus(officeData.statutEntite.name);
}
if(updates != 0) await this.officeService.update(office.uid!, office);
await this.officeService.updateCheckedAt(office.uid!);
}
public async getOrCreateUser(decodedToken: IdNotJwtPayload) {
let user = await this.userService.getByProvider("idNot", decodedToken.sub);
if (!user) {
const searchParams = new URLSearchParams({
key: this.variables.IDNOT_API_KEY,
});
const userData = (await (
await fetch(`${this.variables.IDNOT_API_BASE_URL}/api/pp/v2/rattachements/${decodedToken.profile_idn}?` + searchParams, {
method: "GET",
})
).json()) as IRattachementData;
const officeLocationData = (await (
await fetch(`${this.variables.IDNOT_API_BASE_URL + userData.entite.locationsUrl}?` + searchParams, { method: "GET" })
).json()) as IOfficeLocation;
const role = await this.getRole(userData.typeLien.name);
const userToAdd = {
idNot: decodedToken.sub,
office_membership: {
idNot: decodedToken.entity_idn,
name: userData.entite.denominationSociale,
crpcen: userData.entite.codeCrpcen,
office_status: this.getOfficeStatus(userData.entite.statutEntite.name),
address: {
address: officeLocationData.result[0]!.adrGeo4,
city: officeLocationData.result[0]!.adrGeoVille.split(" ")[0] ?? officeLocationData.result[0]!.adrGeoVille, //officeLocationData.result[0]!.adrPostaleVille,
zip_code: Number(officeLocationData.result[0]!.adrGeoCodePostal),
created_at: null,
updated_at: null,
},
created_at: null,
updated_at: null,
},
role: role,
contact: {
first_name: userData.personne.prenom,
last_name: userData.personne.nomUsuel,
email: userData.mailRattachement,
phone_number: userData.numeroTelephone,
cell_phone_number: userData.numeroMobile ?? userData.numeroTelephone,
civility: userData.personne.civilite,
created_at: null,
updated_at: null,
},
};
const userToHydrate = User.hydrate<User>(userToAdd);
return await this.userService.create(userToHydrate);
}
return user;
}
public async updateUsers() {
const usersReq = await this.userService.getUsersToBeChecked();
const users = User.hydrateArray<User>(usersReq);
users.forEach(async (user) => {
await this.updateUser(user.uid!);
});
}
public async updateOffices() {
const officesReq = await this.officeService.getOfficesToBeChecked();
const offices = Office.hydrateArray<Office>(officesReq);
offices.forEach(async (office) => {
await this.updateOffice(office.uid!);
});
}
}

View File

@ -34,6 +34,23 @@ export default class OfficesService extends BaseService {
return this.officeRepository.update(uid, officeEntity);
}
/**
* @description : Modify an office check date
* @throws {Error} If user modification failed
* @deprecate
*/
public updateCheckedAt(uid: string): Promise<Offices> {
return this.officeRepository.updateCheckedAt(uid);
}
/**
* @description : delete an office
* @throws {Error} If office cannot be modified
*/
public async delete(uid: string): Promise<Offices> {
return this.officeRepository.delete(uid);
}
/**
* @description : Get a office by uid
* @throws {Error} If office cannot be get
@ -41,4 +58,11 @@ export default class OfficesService extends BaseService {
public async getByUid(uid: string, query?: any): Promise<Offices | null> {
return this.officeRepository.findOneByUid(uid, query);
}
/**
* @description : Get users to be checked with IdNot API
*/
public getOfficesToBeChecked() {
return this.officeRepository.findManyToCheck();
}
}

View File

@ -35,6 +35,23 @@ export default class UsersService extends BaseService {
return this.userRepository.update(uid, userEntity);
}
/**
* @description : Modify a user check date
* @throws {Error} If user modification failed
* @deprecate
*/
public updateCheckedAt(uid: string): Promise<Users> {
return this.userRepository.updateCheckedAt(uid);
}
/**
* @description : Delete a user
* @throws {Error} If user modification failed
*/
public delete(uid: string): Promise<Users> {
return this.userRepository.delete(uid);
}
/**
* @description : Get a user by uid
* @throws {Error} If user cannot be get by uid
@ -66,4 +83,12 @@ export default class UsersService extends BaseService {
public getByProvider(providerName: string, id: string) {
return this.userRepository.findOneByProvider(providerName, id);
}
/**
* @description : Get users to be checked with IdNot API
*/
public getUsersToBeChecked() {
return this.userRepository.findManyToCheck();
}
}