diff --git a/apps/api/src/dashboard/dashboard-images.service.spec.ts b/apps/api/src/dashboard/dashboard-images.service.spec.ts index 0de835d..16dcc70 100644 --- a/apps/api/src/dashboard/dashboard-images.service.spec.ts +++ b/apps/api/src/dashboard/dashboard-images.service.spec.ts @@ -340,6 +340,37 @@ describe('DashboardImagesService (quick-260921-pi9)', () => { expect(Buffer.from(result.data).equals(PNG)).toBe(true); }); + it('Test 10b: getBytes — Datei fehlt, aber die alte Spalte `data` traegt die Bytes noch: wiederherstellen statt 404', async () => { + // Fall aus dem Browser-Rundgang 22.09.2026: ein `pg_dump` von vor dem Umzug + // traegt die Bytes noch, das Volume `user-files` wird getrennt gesichert — + // wer nur den Abzug zurueckspielt, haette sonst Zeilen ohne Datei. + const row = makeRow({ + id: 'img-alt', + data: PNG, + storagePath: 'user-files/dashboard-images/user-1/img-alt.png', + }); + const prisma = makeFakePrisma([row]); + expect(fs.existsSync(storedFile('user-1', 'img-alt'))).toBe(false); + + const result = await makeService(prisma).getBytes('img-alt', 'user-1', 'tenant-1'); + + expect(Buffer.from(result.data).equals(PNG)).toBe(true); + expect(fs.existsSync(storedFile('user-1', 'img-alt'))).toBe(true); + expect(fs.readFileSync(storedFile('user-1', 'img-alt')).equals(PNG)).toBe(true); + }); + + it('Test 10c: getBytes — Datei fehlt UND `data` ist leer -> 404', async () => { + const row = makeRow({ + id: 'img-weg', + data: null, + storagePath: 'user-files/dashboard-images/user-1/img-weg.png', + }); + const prisma = makeFakePrisma([row]); + await expect(makeService(prisma).getBytes('img-weg', 'user-1', 'tenant-1')).rejects.toThrow( + NotFoundException, + ); + }); + it('Test 11: remove — eigenes Bild wird geloescht und { id } geliefert; fremdes (Benutzer ODER Mandant) -> 404 ohne Loeschung', async () => { const prisma = makeFakePrisma([ makeStoredRow({ id: 'eigen' }), diff --git a/apps/api/src/dashboard/dashboard-images.service.ts b/apps/api/src/dashboard/dashboard-images.service.ts index 6461599..757d306 100644 --- a/apps/api/src/dashboard/dashboard-images.service.ts +++ b/apps/api/src/dashboard/dashboard-images.service.ts @@ -321,6 +321,26 @@ export class DashboardImagesService implements OnApplicationBootstrap { const data = await fs.readFile(absolute); return { mimeType: row.mimeType, data }; } catch (error) { + // Selbstheilung waehrend der Umstellung (T-HK4-03): fehlt die Datei, + // steckt aber noch die alte Spalte `data` in der Zeile, wird die Datei + // daraus neu geschrieben und ausgeliefert. Der Fall ist real: ein + // `pg_dump` aus der Zeit vor dem Umzug traegt die Bytes noch, das + // Volume `user-files` wird getrennt gesichert — wer nur den Abzug + // zurueckspielt, haette sonst Zeilen ohne Datei. Nach dem Entfernen der + // Spalte (eigenes Todo) faellt dieser Zweig ersatzlos weg. + if (row.data !== null) { + try { + await this.writeImageFile(row.userId, row.id, row.mimeType, row.data); + this.logger.log(`Bilderrahmen-Bild ${id} aus der Datenbank wiederhergestellt`); + return { mimeType: row.mimeType, data: row.data }; + } catch (writeError) { + this.logger.error( + `Bilderrahmen-Bild ${id} konnte nicht wiederhergestellt werden: ${ + writeError instanceof Error ? writeError.message : String(writeError) + }`, + ); + } + } this.logger.warn( `Bilderrahmen-Bild ${id} fehlt im Dateibereich: ${ error instanceof Error ? error.message : String(error)