diff --git a/packages/backend/package.json b/packages/backend/package.json index 6866462d28..7f072e5b5b 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -99,6 +99,7 @@ "sanitize-filename": "^1.6.3", "sanitize-html": "^2.13.0", "sharp": "^0.33.5", + "slug": "^9.1.0", "sort-object": "^3.0.3", "source-map-support": "^0.5.21", "striptags": "^3.2.0", @@ -135,6 +136,8 @@ "@types/pizzip": "^3.0.5", "@types/rimraf": "^4.0.5", "@types/sanitize-html": "^2.11.0", + "@types/slug": "^5.0.9", + "@types/striptags": "^3.1.1", "@types/supertest": "^6.0.2", "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^7.14.1", diff --git a/packages/backend/src/_migrations/1723547616571-create-structure-information-migration.ts b/packages/backend/src/_migrations/1723547616571-create-structure-information-migration.ts deleted file mode 100644 index 68c7654561..0000000000 --- a/packages/backend/src/_migrations/1723547616571-create-structure-information-migration.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; -import { domifaConfig } from "../config"; - -export class CreateStructureInformationMigration1723547616571 - implements MigrationInterface -{ - name = "CreateStructureInformationMigration1723547616571"; - - public async up(queryRunner: QueryRunner): Promise { - if ( - domifaConfig().envId === "prod" || - domifaConfig().envId === "preprod" || - domifaConfig().envId === "local" - ) { - await queryRunner.query( - `CREATE TABLE "structure_information" ("uuid" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "version" integer NOT NULL, "title" character varying NOT NULL, "description" character varying, "isTemporary" boolean NOT NULL DEFAULT false, "startDate" TIMESTAMP, "endDate" TIMESTAMP, "type" character varying NOT NULL, "createdBy" jsonb, "structureId" integer NOT NULL, CONSTRAINT "PK_b51c75b37769abf1fdf28fc89ef" PRIMARY KEY ("uuid"))` - ); - await queryRunner.query( - `CREATE INDEX "IDX_17cd35c9fdcd9ab82015a46b22" ON "structure_information" ("structureId") ` - ); - await queryRunner.query( - `ALTER TABLE "structure_information" ADD CONSTRAINT "FK_17cd35c9fdcd9ab82015a46b22c" FOREIGN KEY ("structureId") REFERENCES "structure"("id") ON DELETE CASCADE ON UPDATE NO ACTION` - ); - } - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "structure_information" DROP CONSTRAINT "FK_17cd35c9fdcd9ab82015a46b22c"` - ); - - await queryRunner.query(`DROP TABLE "structure_information"`); - } -} diff --git a/packages/backend/src/_migrations/1728487458329-manual-migration.ts b/packages/backend/src/_migrations/1728487458329-manual-migration.ts new file mode 100644 index 0000000000..91ef16247f --- /dev/null +++ b/packages/backend/src/_migrations/1728487458329-manual-migration.ts @@ -0,0 +1,39 @@ +import { IsNull, MigrationInterface } from "typeorm"; +import { openDataPlaceRepository } from "../database"; +import { getStructureType } from "../open-data-places/functions"; +import { appLogger } from "../util"; +import { domifaConfig } from "../config"; + +export class UpdateStructureTypeMigration1728487458329 + implements MigrationInterface +{ + name = "UpdateStructureTypeMigration1728487458329"; + public async up(): Promise { + if ( + domifaConfig().envId === "prod" || + domifaConfig().envId === "preprod" || + domifaConfig().envId === "local" + ) { + appLogger.info("[MIGRATION] Update structureType for OpenData"); + const places = await openDataPlaceRepository.find({ + where: { + domifaStructureId: IsNull(), + }, + select: ["nom", "uuid"], + }); + + for (const place of places) { + await openDataPlaceRepository.update( + { uuid: place.uuid }, + { + structureType: getStructureType(place.nom), + } + ); + } + } + } + + public async down(): Promise { + // + } +} diff --git a/packages/backend/src/_migrations/1728487663488-manual-migration.ts b/packages/backend/src/_migrations/1728487663488-manual-migration.ts new file mode 100644 index 0000000000..4cc8003346 --- /dev/null +++ b/packages/backend/src/_migrations/1728487663488-manual-migration.ts @@ -0,0 +1,25 @@ +import { MigrationInterface } from "typeorm"; +import { appLogsRepository } from "../database"; +import { domifaConfig } from "../config"; + +export class DeleteUselessLogsMigration1728487663488 + implements MigrationInterface +{ + name = "DeleteUselessLogsMigration1728487663488"; + + public async up(): Promise { + if ( + domifaConfig().envId === "prod" || + domifaConfig().envId === "preprod" || + domifaConfig().envId === "local" + ) { + await appLogsRepository.delete({ + action: "USAGERS_DOCS_DOWNLOAD", + }); + } + } + + public async down(): Promise { + // + } +} diff --git a/packages/backend/src/_portail-admin/admin-structures/controllers/admin-structures.controller.ts b/packages/backend/src/_portail-admin/admin-structures/controllers/admin-structures.controller.ts index 282cd0cd21..fe074e2af7 100644 --- a/packages/backend/src/_portail-admin/admin-structures/controllers/admin-structures.controller.ts +++ b/packages/backend/src/_portail-admin/admin-structures/controllers/admin-structures.controller.ts @@ -48,7 +48,7 @@ import { } from "@domifa/common"; import { MetabaseStatsDto } from "../../_dto/MetabaseStats.dto"; import { domifaConfig } from "../../../config"; -import jwt from "jsonwebtoken"; +import { sign } from "jsonwebtoken"; import { FindOptionsWhere } from "typeorm"; import { AppLogsService } from "../../../modules/app-logs/app-logs.service"; @@ -142,7 +142,7 @@ export class AdminStructuresController { @Get("stats") @AllowUserProfiles("super-admin-domifa") public async stats(): Promise { - return this.adminStructuresService.getStatsDomifaAdminDashboard(); + return await this.adminStructuresService.getStatsDomifaAdminDashboard(); } @Get("last-update") @@ -260,7 +260,7 @@ export class AdminStructuresController { exp: Math.round(Date.now() / 1000) + 100 * 60, // 10 minute expiration }; - const token = jwt.sign(payload, domifaConfig().metabase.token); + const token = sign(payload, domifaConfig().metabase.token); const url = `${METABASE_URL}embed/dashboard/${token}#bordered=false&titled=false`; return { url }; @@ -278,7 +278,7 @@ export class AdminStructuresController { verified: true, }; - return structureRepository.find({ + return await structureRepository.find({ where: params, select: ["id", "nom", "ville", "codePostal"], order: { diff --git a/packages/backend/src/open-data-places/functions/getStructureType.ts b/packages/backend/src/open-data-places/functions/getStructureType.ts new file mode 100644 index 0000000000..60acc238e1 --- /dev/null +++ b/packages/backend/src/open-data-places/functions/getStructureType.ts @@ -0,0 +1,47 @@ +import { StructureType } from "@domifa/common"; +import slug from "slug"; +import striptags from "striptags"; + +export const getStructureType = (input: string): StructureType | null => { + if (!input) { + return null; + } + const strippedInput = striptags(input).toLowerCase().trim(); + + const normalizedInput = slug(strippedInput, { + mode: "rfc3986" as const, + lower: true, + replacement: " ", // Utiliser des espaces au lieu des tirets + remove: /[^a-z0-9\s]/g, // Supprimer tous les caractères non alphanumériques sauf les espaces + locale: "fr", + trim: true, + }); + + if (!normalizedInput) { + return null; + } + + if ( + normalizedInput.includes("ccas") || + normalizedInput.startsWith("commune") || + normalizedInput.startsWith("centre communal") || + normalizedInput.startsWith("centre communal daction sociale") || + normalizedInput.includes("centre daction sociale") || + normalizedInput.startsWith("mairie") || + normalizedInput.startsWith("hotel de ville") + ) { + return "ccas"; + } + + if ( + normalizedInput.startsWith("cias") || + normalizedInput.startsWith("centre intercommunal") || + normalizedInput.includes("communaute de communes") || + normalizedInput.includes("centre intercommunal daction sociale") || + normalizedInput.includes("intercommunal") + ) { + return "cias"; + } + + return "asso"; +}; diff --git a/packages/backend/src/open-data-places/functions/index.ts b/packages/backend/src/open-data-places/functions/index.ts new file mode 100644 index 0000000000..b839dd463c --- /dev/null +++ b/packages/backend/src/open-data-places/functions/index.ts @@ -0,0 +1,2 @@ +// @index('./*.ts', f => `export * from '${f.path}'`) +export * from "./getStructureType"; diff --git a/packages/backend/src/open-data-places/functions/tests/STRUCTURE_NAMES.const.ts b/packages/backend/src/open-data-places/functions/tests/STRUCTURE_NAMES.const.ts new file mode 100644 index 0000000000..613ca43c82 --- /dev/null +++ b/packages/backend/src/open-data-places/functions/tests/STRUCTURE_NAMES.const.ts @@ -0,0 +1,19 @@ +export const STRUCTURE_NAMES = { + "4AJ Plateforme Logement Jeunes CLLAJ ARRAS": "asso", + "A fratellanza": "asso", + "Abri de la Providence": "asso", + "ACCEPTESS-T": "asso", + ACCES: "asso", + "Accueil de Jour Association Eole": "asso", + "Accueil de jour de Douai Croix rouge Française": "asso", + "AHLIS 46": "asso", + "AIDE A L'INSERTION DES DEMANDEURS D'ASILE": "asso", + AVAF: "asso", + "BOUTIQUE SOLIDARITÉ EMMAÜS DE BEAUVAIS": "asso", + "Boutique Solidarité": "asso", + Bruillot: "asso", + "C.C.A.S de la commune de Martillac": "ccas", + "C.C.A.S DE PAMIERS": "ccas", + "CIAS de Domène": "cias", + Vista: "asso", +}; diff --git a/packages/backend/src/open-data-places/functions/tests/getStructureType.spec.ts b/packages/backend/src/open-data-places/functions/tests/getStructureType.spec.ts new file mode 100644 index 0000000000..11342ab116 --- /dev/null +++ b/packages/backend/src/open-data-places/functions/tests/getStructureType.spec.ts @@ -0,0 +1,41 @@ +import { getStructureType } from "../getStructureType"; +import { STRUCTURE_NAMES } from "./STRUCTURE_NAMES.const"; + +describe("checkOrganizationType", () => { + it("should correctly identify organization types for all entries", () => { + Object.entries(STRUCTURE_NAMES).forEach(([name, expectedType]) => { + const result = getStructureType(name); + expect(result).toBe(expectedType); + }); + }); + + it("should return an empty string for unrecognized inputs", () => { + const unrecognizedInputs = [ + "Random Organization", + "Just a regular company", + ]; + + unrecognizedInputs.forEach((input) => { + expect(getStructureType(input)).toBe("asso"); + }); + }); + + it("should handle edge cases correctly", () => { + const edgeCases = { + "": null, + " ": null, + CCAS: "ccas", + cias: "cias", + Association: "asso", + "": "ccas", + "C.C.A.S.": "ccas", + "Centre Communal d'Action Sociale": "ccas", + "MAIRIE-CCAS": "ccas", + "Organisme-Association": "asso", + }; + + Object.entries(edgeCases).forEach(([input, expected]) => { + expect(getStructureType(input)).toBe(expected); + }); + }); +}); diff --git a/packages/backend/src/open-data-places/interfaces/OpenDataPlace.interface.ts b/packages/backend/src/open-data-places/interfaces/OpenDataPlace.interface.ts index 7e1cdc54d2..d30b1ebed6 100644 --- a/packages/backend/src/open-data-places/interfaces/OpenDataPlace.interface.ts +++ b/packages/backend/src/open-data-places/interfaces/OpenDataPlace.interface.ts @@ -1,4 +1,4 @@ -import { AppEntity } from "@domifa/common"; +import { AppEntity, StructureType } from "@domifa/common"; export interface OpenDataPlace extends AppEntity { nom: string; @@ -17,4 +17,5 @@ export interface OpenDataPlace extends AppEntity { mail: string | null; soliguideStructureId: number; mssId: string | null; + structureType: StructureType | null; } diff --git a/packages/backend/src/open-data-places/load-data-inclusion.ts b/packages/backend/src/open-data-places/load-data-inclusion.ts index d8207dfefa..bb3fb490f3 100644 --- a/packages/backend/src/open-data-places/load-data-inclusion.ts +++ b/packages/backend/src/open-data-places/load-data-inclusion.ts @@ -13,23 +13,11 @@ import { getDepartementFromCodePostal, getRegionCodeFromDepartement, } from "@domifa/common"; +import { getStructureType } from "./functions"; let page = 1; let nbResults = 0; -export const loadDataInclusionData = async (structureType: "CCAS" | "CIAS") => { - if ( - !domifaConfig().openDataApps.dataInclusionUrl || - !domifaConfig().openDataApps.dataInclusionToken - ) { - console.log("[IMPORT DATA INCLUSION] Fail, token or url is not in env"); - return; - } - appLogger.info("Import data-inclusion start 🏃‍♂️... "); - - await getFromDataInclusion(structureType); -}; - const getFromDataInclusion = async (structureType: "CCAS" | "CIAS") => { let datInclusionData: DataInclusionPlace[] = []; @@ -80,6 +68,7 @@ const getFromDataInclusion = async (structureType: "CCAS" | "CIAS") => { departement, region: getRegionCodeFromDepartement(departement), software: "other", + structureType: getStructureType(place.nom), latitude: place?.latitude, longitude: place?.longitude, source: "data-inclusion", @@ -121,3 +110,15 @@ const getFromDataInclusion = async (structureType: "CCAS" | "CIAS") => { appLogger.error("[IMPORT] Something happen", e); } }; +export const loadDataInclusionData = async (structureType: "CCAS" | "CIAS") => { + if ( + !domifaConfig().openDataApps.dataInclusionUrl || + !domifaConfig().openDataApps.dataInclusionToken + ) { + console.log("[IMPORT DATA INCLUSION] Fail, token or url is not in env"); + return; + } + appLogger.info("Import data-inclusion start 🏃‍♂️... "); + + await getFromDataInclusion(structureType); +}; diff --git a/packages/backend/src/open-data-places/load-mss.ts b/packages/backend/src/open-data-places/load-mss.ts index 62a62824f4..c42b3899ad 100644 --- a/packages/backend/src/open-data-places/load-mss.ts +++ b/packages/backend/src/open-data-places/load-mss.ts @@ -9,22 +9,11 @@ import { OpenDataPlace } from "./interfaces/OpenDataPlace.interface"; import { getDepartementFromCodePostal, getRegionCodeFromDepartement, + getStructureType, } from "@domifa/common"; import { MssPlace } from "./interfaces"; import { getLocation } from "../structures/services/location.service"; -export const loadMssData = async () => { - if ( - !domifaConfig().openDataApps.mssUrl || - !domifaConfig().openDataApps.mssToken - ) { - appLogger.info("[IMPORT DATA MSS] Fail, token or url is not in env"); - return; - } - appLogger.info("Import MSS start 🏃‍♂️... "); - await getFromMss(); -}; - const getFromMss = async () => { let newPlaces = 0; let updatedPlaces = 0; @@ -40,11 +29,11 @@ const getFromMss = async () => { for await (const place of response.data) { const postalCode = place.zipcode.replace(/\W/g, ""); - const address = place.address + ", " + postalCode; + const address = `${place.address}, ${postalCode}`; const position = await getLocation(address); if (!position) { - appLogger.warn("Adresse not found " + address); + appLogger.warn(`Adresse not found ${address}`); continue; } @@ -55,6 +44,7 @@ const getFromMss = async () => { codePostal: postalCode, ville: cleanCity(place?.city), departement, + structureType: getStructureType(place.name), region: getRegionCodeFromDepartement(departement), latitude: position.coordinates[1], longitude: position.coordinates[0], @@ -123,3 +113,15 @@ const getFromMss = async () => { ); } }; + +export const loadMssData = async () => { + if ( + !domifaConfig().openDataApps.mssUrl || + !domifaConfig().openDataApps.mssToken + ) { + appLogger.info("[IMPORT DATA MSS] Fail, token or url is not in env"); + return; + } + appLogger.info("Import MSS start 🏃‍♂️... "); + await getFromMss(); +}; diff --git a/packages/backend/src/open-data-places/load-soliguide.ts b/packages/backend/src/open-data-places/load-soliguide.ts index 0a0dfedada..999a751d18 100644 --- a/packages/backend/src/open-data-places/load-soliguide.ts +++ b/packages/backend/src/open-data-places/load-soliguide.ts @@ -9,23 +9,13 @@ import { OpenDataPlace } from "./interfaces/OpenDataPlace.interface"; import { getDepartementFromCodePostal, getRegionCodeFromDepartement, + getStructureType, } from "@domifa/common"; let page = 1; let nbResults = 0; let newPlaces = 0; let updatedPlaces = 0; -export const loadSoliguideData = async () => { - if ( - !domifaConfig().openDataApps.soliguideUrl || - !domifaConfig().openDataApps.soliguideToken - ) { - console.log("[IMPORT DATA] Fail, token or url is not in env"); - return; - } - appLogger.info("Import Soliguide start 🏃‍♂️... "); - await getFromSoliguide(); -}; const getFromSoliguide = async () => { let soliguideData: SoliguidePlace[] = []; @@ -71,6 +61,7 @@ const getFromSoliguide = async () => { codePostal: place.position.codePostal, ville: cleanCity(place?.position?.ville), departement, + structureType: getStructureType(place.name), region: getRegionCodeFromDepartement(departement), latitude: place.position.location.coordinates[1], longitude: place.position.location.coordinates[0], @@ -125,12 +116,9 @@ const getFromSoliguide = async () => { if (soliguideData.length * page < nbResults) { appLogger.warn( - "Import 'soliguide' data page N°" + - page + - " : " + - soliguideData.length * page + - "/" + - nbResults + `Import 'soliguide' data page N°${page} : ${ + soliguideData.length * page + }/${nbResults}` ); page++; await getFromSoliguide(); @@ -145,3 +133,15 @@ const getFromSoliguide = async () => { console.error("[IMPORT] Something happen during soliguide import"); } }; +export const loadSoliguideData = async () => { + if ( + !domifaConfig().openDataApps.soliguideUrl || + !domifaConfig().openDataApps.soliguideToken + ) { + // skipcq: JS-0002 + console.log("[IMPORT DATA] Fail, token or url is not in env"); + return; + } + appLogger.info("Import Soliguide start 🏃‍♂️... "); + await getFromSoliguide(); +}; diff --git a/packages/backend/src/open-data-places/utils/getStructureType.ts b/packages/backend/src/open-data-places/utils/getStructureType.ts new file mode 100644 index 0000000000..17fcfcddd1 --- /dev/null +++ b/packages/backend/src/open-data-places/utils/getStructureType.ts @@ -0,0 +1,41 @@ +import { StructureType } from "@domifa/common"; + +export const checkStructureType = (input: string): StructureType | null => { + const lowerInput = input.toLowerCase().replace(/[^\w\s]/gi, ""); + + if ( + lowerInput.includes("ccas") || + lowerInput.includes("centre communal") || + lowerInput.includes("centre communal daction sociale") || + lowerInput.includes("mairie") || + lowerInput.includes("hotel de ville") || + lowerInput.includes("action sociale") || + (lowerInput.includes("centre") && lowerInput.includes("social")) + ) { + return "ccas"; + } + + if ( + lowerInput.includes("association") || + lowerInput.includes("organisme") || + lowerInput.includes("ong") || + lowerInput.includes("organisation non gouvernementale") || + lowerInput.includes("fondation") || + lowerInput.includes("collectif") || + lowerInput.includes("entraide") + ) { + return "asso"; + } + + if ( + lowerInput.includes("cias") || + lowerInput.includes("centre intercommunal") || + lowerInput.includes("centre intercommunal daction sociale") || + (lowerInput.includes("intercommunal") && lowerInput.includes("social")) + ) { + return "cias"; + } + + // Si aucune correspondance n'est trouvée + return null; +}; diff --git a/packages/backend/src/stats/controllers/stats.private.controller.ts b/packages/backend/src/stats/controllers/stats.private.controller.ts index cba90bab67..20324b1e5d 100644 --- a/packages/backend/src/stats/controllers/stats.private.controller.ts +++ b/packages/backend/src/stats/controllers/stats.private.controller.ts @@ -173,7 +173,7 @@ export class StatsPrivateController { const endDateUTCExclusive = statsQuestionsCoreBuilder.removeUTCHours( addDays(new Date(end), 1) ); - return structureStatsInPeriodGenerator.buildStatsInPeriod({ + return await structureStatsInPeriodGenerator.buildStatsInPeriod({ structureId, startDateUTC, endDateUTCExclusive, diff --git a/packages/backend/src/stats/controllers/stats.public.controller.ts b/packages/backend/src/stats/controllers/stats.public.controller.ts index 5a77d95b11..bc7048eb7f 100644 --- a/packages/backend/src/stats/controllers/stats.public.controller.ts +++ b/packages/backend/src/stats/controllers/stats.public.controller.ts @@ -16,7 +16,7 @@ export class StatsPublicController { public async getPublicStats( @Param("regionId", new ParseRegionPipe()) regionId: string ): Promise { - return this.publicStatsService.generatePublicStats({ + return await this.publicStatsService.generatePublicStats({ updateCache: false, regionId, }); diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 4e7b368d4b..db61ad703c 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -56,6 +56,7 @@ "ngx-countup": "^13.2.0", "ngx-matomo-client": "^5.0.4", "rxjs": "^7.8.1", + "slug": "^9.1.0", "tslib": "^2.6.3", "validator": "^13.12.0", "zone.js": "~0.14.7" @@ -77,6 +78,7 @@ "@types/file-saver": "^2.0.7", "@types/google-libphonenumber": "^7.4.30", "@types/jest": "^29.5.12", + "@types/slug": "^5.0.9", "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "7.18.0", "eslint": "^8.57.1", diff --git a/packages/frontend/src/app/modules/structures/components/structure-edit-form/structure-edit-form.component.html b/packages/frontend/src/app/modules/structures/components/structure-edit-form/structure-edit-form.component.html index ad5b5573bc..b2354ef16d 100644 --- a/packages/frontend/src/app/modules/structures/components/structure-edit-form/structure-edit-form.component.html +++ b/packages/frontend/src/app/modules/structures/components/structure-edit-form/structure-edit-form.component.html @@ -92,6 +92,7 @@

Modifier les informations de votre structure

maxlength="200" autocomplete="organization" formControlName="nom" + appCleanStr name="nom" [ngClass]="{ 'is-invalid': (f.nom.dirty || submitted) && f.nom.errors diff --git a/packages/frontend/src/app/modules/structures/utils/structure-validators.ts b/packages/frontend/src/app/modules/structures/utils/structure-validators.ts index 0edec0207d..5a2b5b2fcc 100644 --- a/packages/frontend/src/app/modules/structures/utils/structure-validators.ts +++ b/packages/frontend/src/app/modules/structures/utils/structure-validators.ts @@ -9,18 +9,31 @@ import { getDepartementFromCodePostal, getRegionCodeFromDepartement, } from "@domifa/common"; +import slug from "slug"; export function isInvalidStructureName(structureName: string): boolean { if (!structureName) { return false; } - const name = structureName.toLowerCase().trim(); - return ( - name === "ccas" || - name === "c.c.a.s." || - name === "centre communal d'action sociale" || - name === "mairie" - ); + + const normalizedInput = slug(structureName, { + mode: "rfc3986" as const, + lower: true, + replacement: " ", // Utiliser des espaces au lieu des tirets + remove: /[^a-z0-9\s]/g, // Supprimer tous les caractères non alphanumériques sauf les espaces + locale: "fr", // Utiliser explicitement la locale française + trim: true, + }); + + return [ + "ccas", + "commune", + "centre communal", + "centre communal daction sociale", + "centre daction sociale", + "mairie", + "hotel de ville", + ].includes(normalizedInput); } export const getPostalCodeValidator = ( diff --git a/yarn.lock b/yarn.lock index 312c6be0e1..87f7efa6e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4063,7 +4063,9 @@ __metadata: "@types/pizzip": "npm:^3.0.5" "@types/rimraf": "npm:^4.0.5" "@types/sanitize-html": "npm:^2.11.0" + "@types/slug": "npm:^5.0.9" "@types/source-map-support": "npm:^0.5.10" + "@types/striptags": "npm:^3.1.1" "@types/supertest": "npm:^6.0.2" "@types/uuid": "npm:^9.0.8" "@typescript-eslint/eslint-plugin": "npm:^7.14.1" @@ -4116,6 +4118,7 @@ __metadata: sanitize-filename: "npm:^1.6.3" sanitize-html: "npm:^2.13.0" sharp: "npm:^0.33.5" + slug: "npm:^9.1.0" sort-object: "npm:^3.0.3" source-map-support: "npm:^0.5.21" striptags: "npm:^3.2.0" @@ -4210,6 +4213,7 @@ __metadata: "@types/file-saver": "npm:^2.0.7" "@types/google-libphonenumber": "npm:^7.4.30" "@types/jest": "npm:^29.5.12" + "@types/slug": "npm:^5.0.9" "@typescript-eslint/eslint-plugin": "npm:^7.18.0" "@typescript-eslint/parser": "npm:7.18.0" angular-user-idle: "npm:^4.0.0" @@ -4236,6 +4240,7 @@ __metadata: ngx-matomo-client: "npm:^5.0.4" prettier: "npm:2.8.8" rxjs: "npm:^7.8.1" + slug: "npm:^9.1.0" source-map-explorer: "npm:^2.5.3" ts-jest: "npm:^29.1.5" ts-node: "npm:~10.9.2" @@ -9830,6 +9835,13 @@ __metadata: languageName: node linkType: hard +"@types/slug@npm:^5.0.9": + version: 5.0.9 + resolution: "@types/slug@npm:5.0.9" + checksum: 10/dcb188c783eb3e494e842a102ffd97efc1df742bd95841287cbd4d900029da23546609b7450e29ea336924d79042cd225fcd39271d0053c3ca05a7f4af923c6b + languageName: node + linkType: hard + "@types/sockjs@npm:^0.3.33": version: 0.3.33 resolution: "@types/sockjs@npm:0.3.33" @@ -9869,6 +9881,15 @@ __metadata: languageName: node linkType: hard +"@types/striptags@npm:^3.1.1": + version: 3.1.1 + resolution: "@types/striptags@npm:3.1.1" + dependencies: + striptags: "npm:*" + checksum: 10/7badac89aa2b5f87772b2b03a277bc3941238cda23ed6ef38b7d15a651960de5298ac8597bba5f00890b25832c85e217b00c12a342cd78a4158fc04c5d03aeea + languageName: node + linkType: hard + "@types/superagent@npm:^8.1.0": version: 8.1.1 resolution: "@types/superagent@npm:8.1.1" @@ -25318,6 +25339,15 @@ __metadata: languageName: node linkType: hard +"slug@npm:^9.1.0": + version: 9.1.0 + resolution: "slug@npm:9.1.0" + bin: + slug: cli.js + checksum: 10/b386fe7363749251c27a08b1d566befaf943ac04c9da145086c537167cda586d3d6fd893f1394d017ec932a90c02b58e815d96fb94f34b9cb83f8acf15f33620 + languageName: node + linkType: hard + "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -25997,7 +26027,7 @@ __metadata: languageName: node linkType: hard -"striptags@npm:^3.2.0": +"striptags@npm:*, striptags@npm:^3.2.0": version: 3.2.0 resolution: "striptags@npm:3.2.0" checksum: 10/0d430af5c2d702cfe6e3591b5d353773811d912264b9112e11596ac4d1c6e4185a9f9d92125d324d93cfebeb8ee658ba397f1a5d7a4e44ad7cff98ce40afcfc9