Skip to content

Commit 5076675

Browse files
committed
fix(backend): analyze filesize documents
1 parent 4304077 commit 5076675

File tree

10 files changed

+194
-11
lines changed

10 files changed

+194
-11
lines changed

.talismanrc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ fileignoreconfig:
88
- filename: _docs/dev/0.run-local-env.md
99
checksum: 855b6f29ace746c86597d36382dbe7e40f771137734e3bf5eb53c948d6592a74
1010
- filename: _scripts/db/dumps/domifa_test.postgres.restore-data-only.sql
11-
checksum: f106304727d41ce64bb581f8772896ed8664df3a5e41489587be64afab48f453
11+
checksum: 77fe0574f840079590823778e65a418f3ec05925d1e7ec4c1c9925446b7bccbd
1212
- filename: _scripts/db/dumps/domifa_test.postgres.truncate-restore-data-only.sql
13-
checksum: 4d2f01ba7fbd8c1109994a0831576e6341dc7e925fb75b9f447e2b3e1c9ef1c6
13+
checksum: 4319e1dd76aa224c9d3f05c8834726b98eda791fe972458a5c46743b434f51eb
1414
- filename: packages/backend/package.json
1515
checksum: 2f5813dbc35cdc3659516beabf9f93e2978eb62d1b5403cfbb2907f22bb25355
1616
- filename: packages/backend/src/_migrations/1757532174131-auto-migration.ts
53 Bytes
Binary file not shown.

_scripts/db/dumps/domifa_test.postgres.restore-data-only.sql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,10 @@ COPY public.typeorm_metadata (type, schema, name, value) FROM stdin;
209209
-- Data for Name: usager_docs; Type: TABLE DATA; Schema: public; Owner: -
210210
--
211211

212-
COPY public.usager_docs (uuid, "createdAt", "updatedAt", version, "usagerUUID", "structureId", "usagerRef", path, label, filetype, "createdBy", "encryptionContext", "encryptionVersion", shared) FROM stdin;
213-
2eb5e74d-4b25-4aa6-ad2a-7d963ae66072 2019-10-07 20:51:31.578+02 2022-06-28 22:52:59.282479+02 1 860ffa4c-88c4-4e1c-ad42-5a05cdf39830 1 1 373144a3d9d0b3f4c84bd527a5cff880.jpg CNI image/jpeg Patrick Roméro ffe17c48-7a1a-42c9-8494-0b72ca8b3686 0 f
214-
a77729a9-1b28-4090-bda8-760590bff982 2019-10-07 20:53:32.922+02 2022-06-28 22:52:59.28757+02 1 4dcdcddc-fad2-4827-aac5-0acf1df7b5bc 1 5 8242ba1bc7f3c3971f761b6a347fc1c4.jpg Carte identité image/jpeg Patrick Roméro ffe17c48-7a1a-42c9-8494-0b72ca8b3686 0 f
215-
542a0da1-ea1c-48ab-8026-67a4248b1c47 2024-10-30 22:35:12.346+01 2024-10-30 22:35:13.112661+01 1 b2c26e55-ab37-457d-b307-6fe161050a9b 1 7 ddda9bf211821bec99b90230bd0f52dc.jpg Document à éditer par la suite image/jpeg Patrick Roméro f36cb085-f3a5-4333-afbf-ba6c1c28c5d8 0 f
212+
COPY public.usager_docs (uuid, "createdAt", "updatedAt", version, "usagerUUID", "structureId", "usagerRef", path, label, filetype, "createdBy", "encryptionContext", "encryptionVersion", shared, filesize) FROM stdin;
213+
2eb5e74d-4b25-4aa6-ad2a-7d963ae66072 2019-10-07 20:51:31.578+02 2025-09-16 16:47:26.946388+02 2 860ffa4c-88c4-4e1c-ad42-5a05cdf39830 1 1 373144a3d9d0b3f4c84bd527a5cff880.jpg CNI image/jpeg Patrick Roméro ffe17c48-7a1a-42c9-8494-0b72ca8b3686 0 f 0
214+
a77729a9-1b28-4090-bda8-760590bff982 2019-10-07 20:53:32.922+02 2025-09-16 16:47:27.115593+02 2 4dcdcddc-fad2-4827-aac5-0acf1df7b5bc 1 5 8242ba1bc7f3c3971f761b6a347fc1c4.jpg Carte identité image/jpeg Patrick Roméro ffe17c48-7a1a-42c9-8494-0b72ca8b3686 0 f 0
215+
542a0da1-ea1c-48ab-8026-67a4248b1c47 2024-10-30 22:35:12.346+01 2025-09-16 16:47:27.271753+02 2 b2c26e55-ab37-457d-b307-6fe161050a9b 1 7 ddda9bf211821bec99b90230bd0f52dc.jpg Document à éditer par la suite image/jpeg Patrick Roméro f36cb085-f3a5-4333-afbf-ba6c1c28c5d8 0 f 0
216216
\.
217217

218218

_scripts/db/dumps/domifa_test.postgres.truncate-restore-data-only.sql

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,10 +245,10 @@ COPY public.typeorm_metadata (type, schema, name, value) FROM stdin;
245245
-- Data for Name: usager_docs; Type: TABLE DATA; Schema: public; Owner: -
246246
--
247247

248-
COPY public.usager_docs (uuid, "createdAt", "updatedAt", version, "usagerUUID", "structureId", "usagerRef", path, label, filetype, "createdBy", "encryptionContext", "encryptionVersion", shared) FROM stdin;
249-
2eb5e74d-4b25-4aa6-ad2a-7d963ae66072 2019-10-07 20:51:31.578+02 2022-06-28 22:52:59.282479+02 1 860ffa4c-88c4-4e1c-ad42-5a05cdf39830 1 1 373144a3d9d0b3f4c84bd527a5cff880.jpg CNI image/jpeg Patrick Roméro ffe17c48-7a1a-42c9-8494-0b72ca8b3686 0 f
250-
a77729a9-1b28-4090-bda8-760590bff982 2019-10-07 20:53:32.922+02 2022-06-28 22:52:59.28757+02 1 4dcdcddc-fad2-4827-aac5-0acf1df7b5bc 1 5 8242ba1bc7f3c3971f761b6a347fc1c4.jpg Carte identité image/jpeg Patrick Roméro ffe17c48-7a1a-42c9-8494-0b72ca8b3686 0 f
251-
542a0da1-ea1c-48ab-8026-67a4248b1c47 2024-10-30 22:35:12.346+01 2024-10-30 22:35:13.112661+01 1 b2c26e55-ab37-457d-b307-6fe161050a9b 1 7 ddda9bf211821bec99b90230bd0f52dc.jpg Document à éditer par la suite image/jpeg Patrick Roméro f36cb085-f3a5-4333-afbf-ba6c1c28c5d8 0 f
248+
COPY public.usager_docs (uuid, "createdAt", "updatedAt", version, "usagerUUID", "structureId", "usagerRef", path, label, filetype, "createdBy", "encryptionContext", "encryptionVersion", shared, filesize) FROM stdin;
249+
2eb5e74d-4b25-4aa6-ad2a-7d963ae66072 2019-10-07 20:51:31.578+02 2025-09-16 16:47:26.946388+02 2 860ffa4c-88c4-4e1c-ad42-5a05cdf39830 1 1 373144a3d9d0b3f4c84bd527a5cff880.jpg CNI image/jpeg Patrick Roméro ffe17c48-7a1a-42c9-8494-0b72ca8b3686 0 f 0
250+
a77729a9-1b28-4090-bda8-760590bff982 2019-10-07 20:53:32.922+02 2025-09-16 16:47:27.115593+02 2 4dcdcddc-fad2-4827-aac5-0acf1df7b5bc 1 5 8242ba1bc7f3c3971f761b6a347fc1c4.jpg Carte identité image/jpeg Patrick Roméro ffe17c48-7a1a-42c9-8494-0b72ca8b3686 0 f 0
251+
542a0da1-ea1c-48ab-8026-67a4248b1c47 2024-10-30 22:35:12.346+01 2025-09-16 16:47:27.271753+02 2 b2c26e55-ab37-457d-b307-6fe161050a9b 1 7 ddda9bf211821bec99b90230bd0f52dc.jpg Document à éditer par la suite image/jpeg Patrick Roméro f36cb085-f3a5-4333-afbf-ba6c1c28c5d8 0 f 0
252252
\.
253253

254254

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { MigrationInterface, QueryRunner } from "typeorm";
2+
import { domifaConfig } from "../config";
3+
4+
export class AutoMigration1758029089533 implements MigrationInterface {
5+
name = "AutoMigration1758029089533";
6+
7+
public async up(queryRunner: QueryRunner): Promise<void> {
8+
if (
9+
domifaConfig().envId === "prod" ||
10+
domifaConfig().envId === "preprod" ||
11+
domifaConfig().envId === "local"
12+
) {
13+
await queryRunner.query(
14+
`ALTER TABLE "usager_docs" ADD "filesize" integer`
15+
);
16+
}
17+
}
18+
19+
public async down(queryRunner: QueryRunner): Promise<void> {
20+
if (
21+
domifaConfig().envId === "prod" ||
22+
domifaConfig().envId === "preprod" ||
23+
domifaConfig().envId === "local"
24+
) {
25+
await queryRunner.query(
26+
`ALTER TABLE "usager_docs" DROP COLUMN "filesize"`
27+
);
28+
}
29+
}
30+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { MigrationInterface, QueryRunner } from "typeorm";
2+
import { domifaConfig } from "../config";
3+
import { usagerDocsRepository } from "../database";
4+
import { appLogger, cleanPath, FileManagerService } from "../util";
5+
import { join } from "node:path";
6+
7+
export class AutoMigration1758029089535 implements MigrationInterface {
8+
name = "AutoMigration1758029089535";
9+
10+
public async up(): Promise<void> {
11+
if (
12+
domifaConfig().envId === "prod" ||
13+
domifaConfig().envId === "preprod" ||
14+
domifaConfig().envId === "local"
15+
) {
16+
await this.processExistingDocuments();
17+
}
18+
}
19+
20+
public async down(queryRunner: QueryRunner): Promise<void> {
21+
await queryRunner.query(`ALTER TABLE "usager_docs" DROP COLUMN "filesize"`);
22+
}
23+
24+
private async processExistingDocuments(): Promise<void> {
25+
appLogger.warn(
26+
"[Migration] Starting filesize processing for existing documents..."
27+
);
28+
29+
const fileManagerService = new FileManagerService();
30+
const batchSize = 500;
31+
let offset = 0;
32+
let totalProcessed = 0;
33+
let totalUpdated = 0;
34+
let totalNotFound = 0;
35+
36+
while (true) {
37+
// Récupérer un batch de documents sans fileSize avec jointure pour avoir structure.uuid
38+
const documents = await usagerDocsRepository
39+
.createQueryBuilder("doc")
40+
.leftJoin("structure", "structure", "structure.id = doc.structureId")
41+
.leftJoin("usager", "usager", "usager.uuid = doc.usagerUUID")
42+
.select([
43+
"doc.uuid as uuid",
44+
"doc.path as path",
45+
"doc.label as label",
46+
"structure.uuid as structure_uuid",
47+
"usager.uuid as usager_uuid",
48+
])
49+
.where("doc.filesize IS NULL")
50+
.orderBy("doc.createdAt", "ASC")
51+
.limit(batchSize)
52+
.offset(offset)
53+
.getRawMany();
54+
55+
if (documents.length === 0) {
56+
break;
57+
}
58+
59+
appLogger.warn(
60+
`[Migration] Processing batch ${
61+
Math.floor(offset / batchSize) + 1
62+
}, documents ${offset + 1} to ${offset + documents.length}`
63+
);
64+
65+
// Traiter chaque document du batch
66+
for (const doc of documents) {
67+
try {
68+
// Reconstituer le chemin complet du fichier comme dans votre logique existante
69+
const filePath = join(
70+
"usager-documents",
71+
cleanPath(doc.structure_uuid),
72+
cleanPath(doc.usager_uuid),
73+
`${doc.path}.sfe`
74+
);
75+
76+
const filesize = await fileManagerService.getFileSize(filePath);
77+
await usagerDocsRepository.update({ uuid: doc.uuid }, { filesize });
78+
79+
if (filesize > 0) {
80+
totalUpdated++;
81+
appLogger.debug(
82+
`[Migration] Updated document ${doc.uuid} (${doc.label}) with size ${filesize} bytes, path: ${filePath}`
83+
);
84+
} else if (filesize === -1) {
85+
totalNotFound++;
86+
appLogger.warn(
87+
`[Migration] File not found for document ${doc.uuid} (${doc.label}), path: ${filePath}`
88+
);
89+
} else {
90+
console.log(`🟥 ${filePath} - ${filesize} `);
91+
}
92+
} catch (error) {
93+
appLogger.error(
94+
`[Migration] Error processing document ${doc.uuid}: ${error.message}`
95+
);
96+
}
97+
}
98+
99+
totalProcessed += documents.length;
100+
offset += batchSize;
101+
102+
// Log de progression tous les 10 batches
103+
if (Math.floor(offset / batchSize) % 10 === 0) {
104+
appLogger.warn(
105+
`[Migration] Progress: ${totalProcessed} documents processed, ${totalUpdated} updated, ${totalNotFound} not found`
106+
);
107+
}
108+
109+
// Petite pause pour ne pas surcharger S3
110+
await new Promise((resolve) => setTimeout(resolve, 100));
111+
}
112+
113+
appLogger.warn(`[Migration] FileSize processing completed:`);
114+
appLogger.warn(
115+
`[Migration] - Total documents processed: ${totalProcessed}`
116+
);
117+
appLogger.warn(`[Migration] - Successfully updated: ${totalUpdated}`);
118+
appLogger.warn(`[Migration] - Files not found in S3: ${totalNotFound}`);
119+
appLogger.warn(
120+
`[Migration] - Errors: ${totalProcessed - totalUpdated - totalNotFound}`
121+
);
122+
}
123+
}

packages/backend/src/_migrations/_init-db/domifa_test_schema.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,8 @@ CREATE TABLE public.usager_docs (
328328
"createdBy" text NOT NULL,
329329
"encryptionContext" text,
330330
"encryptionVersion" integer,
331-
shared boolean DEFAULT false NOT NULL
331+
shared boolean DEFAULT false NOT NULL,
332+
filesize integer
332333
);
333334
CREATE TABLE public.usager_entretien (
334335
uuid uuid DEFAULT public.uuid_generate_v4() NOT NULL,

packages/backend/src/database/entities/usager/UsagerDocsTable.typeorm.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ export class UsagerDocsTable
3737
@Column({ type: "text", nullable: false })
3838
public filetype: string;
3939

40+
@Column({ type: "integer", nullable: true })
41+
public filesize: number;
42+
4043
@Column({ type: "text", nullable: false })
4144
public createdBy: string;
4245

packages/backend/src/util/file-manager/file-manager.service.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,4 +284,29 @@ export class FileManagerService {
284284
`Unsupported input type for toNodeReadable: ${typeof input}`
285285
);
286286
}
287+
288+
public async getFileSize(filePath: string): Promise<number> {
289+
try {
290+
const headResult = await this.s3.send(
291+
new HeadObjectCommand({
292+
Bucket: domifaConfig().upload.bucketName,
293+
Key: `${domifaConfig().upload.bucketRootDir}/${filePath}`,
294+
})
295+
);
296+
297+
return headResult.ContentLength || 0;
298+
} catch (error) {
299+
if (
300+
error.name === "NotFound" ||
301+
error.$metadata?.httpStatusCode === 404
302+
) {
303+
return -1;
304+
} else {
305+
appLogger.error(
306+
`Error getting file size for ${filePath}: ${error.message}`
307+
);
308+
return -2;
309+
}
310+
}
311+
}
287312
}

packages/common/src/_core/interfaces/CommonDoc.interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ export interface CommonDoc extends AppEntity {
88
structureId: number;
99
encryptionContext?: string;
1010
encryptionVersion?: number;
11+
filesize?: number;
1112
}

0 commit comments

Comments
 (0)