@appweaver/core 1.0.15 → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appweaver/core",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "description": "Appweaver - the backend framework for AI-first development (@core)",
5
5
  "author": "Luka Matosevic",
6
6
  "license": "MIT",
@@ -123,6 +123,7 @@ function resourceRoutes(name, routesConfig = {}) {
123
123
  config: deleteConfig
124
124
  }, async (request, reply) => {
125
125
  const response = await service.delete(request.params.id);
126
+ await (0, context_1.inject)(storage_1.FileService).deleteResourceFiles(name, request.params.id);
126
127
  return reply.send(response);
127
128
  });
128
129
  }
@@ -78,4 +78,14 @@ export declare class FileService {
78
78
  * an error if no files could be deleted.
79
79
  */
80
80
  deleteFiles(fileNames: Record<string, string | string[]>, resource: Resource, client: ResourceClient): Promise<File[]>;
81
+ /**
82
+ * Deletes all files associated with a resource for file fields configured
83
+ * with `onResourceDeleted: 'delete'`. Files belonging to fields without this
84
+ * setting (or set to `'keep'`) are left untouched.
85
+ *
86
+ * @param {string} resourceName - The resource model name.
87
+ * @param {number} resourceId - The ID of the deleted resource.
88
+ * @return {Promise<File[]>} A promise that resolves to the list of successfully deleted files.
89
+ */
90
+ deleteResourceFiles(resourceName: string, resourceId: number): Promise<File[]>;
81
91
  }
@@ -327,6 +327,56 @@ class FileService {
327
327
  await this._cacheService.invalidateCache(client.name, 'deleteFiles');
328
328
  return deletedFiles;
329
329
  }
330
+ /**
331
+ * Deletes all files associated with a resource for file fields configured
332
+ * with `onResourceDeleted: 'delete'`. Files belonging to fields without this
333
+ * setting (or set to `'keep'`) are left untouched.
334
+ *
335
+ * @param {string} resourceName - The resource model name.
336
+ * @param {number} resourceId - The ID of the deleted resource.
337
+ * @return {Promise<File[]>} A promise that resolves to the list of successfully deleted files.
338
+ */
339
+ async deleteResourceFiles(resourceName, resourceId) {
340
+ const resourceModel = (0, context_1.injectModel)(resourceName, false);
341
+ if (!resourceModel) {
342
+ return [];
343
+ }
344
+ const filesConfig = resourceModel.config.files ?? {};
345
+ const deleteFields = Object.entries(filesConfig)
346
+ .filter(([_, config]) => config.onResourceDeleted !== 'keep')
347
+ .map(([field]) => field);
348
+ if (deleteFields.length === 0) {
349
+ return [];
350
+ }
351
+ let files;
352
+ try {
353
+ files = (await this._db.client().file.findMany({
354
+ where: {
355
+ resourceName,
356
+ resourceId,
357
+ resourceField: { in: deleteFields }
358
+ }
359
+ }));
360
+ }
361
+ catch (e) {
362
+ common_1.logger.error({ resourceName, resourceId, error: e }, 'Error finding resource files for deletion');
363
+ return [];
364
+ }
365
+ const deletedFiles = [];
366
+ for (const file of files) {
367
+ const success = await this.deleteSafe(file.name);
368
+ if (success) {
369
+ deletedFiles.push(file);
370
+ }
371
+ else {
372
+ common_1.logger.error({ resourceName, resourceId, fileName: file.name }, 'Failed to delete file');
373
+ }
374
+ }
375
+ if (deletedFiles.length > 0) {
376
+ common_1.logger.debug({ resourceName, resourceId, deletedCount: deletedFiles.length }, 'Resource files deleted');
377
+ }
378
+ return deletedFiles;
379
+ }
330
380
  /** @internal */
331
381
  buildFileUrl(file, policy) {
332
382
  return `${common_1.config.APP_HOSTNAME}/files/${policy.accessType === 'public' ? 'public' : 'protected'}/${file.name}`;