@appweaver/data 2.0.1 → 2.1.0
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/database/create-client.d.ts +3 -1
- package/database/create-client.js +24 -6
- package/database/database-config.d.ts +1 -0
- package/database/database-config.js +3 -0
- package/database/prisma-database.js +8 -3
- package/export/export-service.js +4 -3
- package/factory/create-model.js +2 -1
- package/factory/create-policy.js +2 -1
- package/factory/create-service.js +2 -1
- package/package.json +3 -3
- package/resource/load-data-resources.js +2 -1
- package/resource/resource-loader.js +2 -1
- package/resource/resource-service.js +16 -2
- package/storage/file-service.js +7 -6
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PrismaClient } from '../prisma/client/client';
|
|
1
|
+
import { Prisma, PrismaClient } from '../prisma/client/client';
|
|
2
2
|
/**
|
|
3
3
|
* Creates and returns a PrismaClient instance based on the configured database type.
|
|
4
4
|
*
|
|
@@ -9,3 +9,5 @@ import { PrismaClient } from '../prisma/client/client';
|
|
|
9
9
|
* @return {PrismaClient} A PrismaClient instance configured for the selected database type.
|
|
10
10
|
*/
|
|
11
11
|
export declare function createClient(): PrismaClient;
|
|
12
|
+
/** Logs a query, as a warning without its parameters when it is slow. */
|
|
13
|
+
export declare function logQuery(e: Prisma.QueryEvent): void;
|
|
@@ -4,15 +4,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.createClient = createClient;
|
|
7
|
+
exports.logQuery = logQuery;
|
|
7
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
9
|
const common_1 = require("@appweaver/common");
|
|
9
10
|
const database_config_1 = require("./database-config");
|
|
11
|
+
const logger = (0, common_1.createLogger)('database');
|
|
10
12
|
const options = {
|
|
11
13
|
transactionOptions: {
|
|
12
14
|
maxWait: database_config_1.databaseConfig.TRANSACTION_MAX_WAIT,
|
|
13
15
|
timeout: database_config_1.databaseConfig.TRANSACTION_TIMEOUT
|
|
14
16
|
},
|
|
15
|
-
log:
|
|
17
|
+
log: emittedEvents().map((e) => ({ emit: 'event', level: e }))
|
|
16
18
|
};
|
|
17
19
|
/**
|
|
18
20
|
* Creates and returns a PrismaClient instance based on the configured database type.
|
|
@@ -129,17 +131,33 @@ function createPrismaClient(adapter) {
|
|
|
129
131
|
const { PrismaClient } = (0, common_1.requireModule)(clientPath, true).value;
|
|
130
132
|
const client = new PrismaClient({ adapter, ...options });
|
|
131
133
|
// Add configured Prisma event logging
|
|
132
|
-
for (const event of
|
|
134
|
+
for (const event of emittedEvents()) {
|
|
133
135
|
if (event === common_1.DatabaseEvent.Query) {
|
|
134
|
-
client.$on(event,
|
|
135
|
-
common_1.logger.debug(e, 'Prisma query');
|
|
136
|
-
});
|
|
136
|
+
client.$on(event, logQuery);
|
|
137
137
|
}
|
|
138
138
|
else {
|
|
139
139
|
client.$on(event, (e) => {
|
|
140
|
-
|
|
140
|
+
logger[event](e, `Prisma ${event} log`);
|
|
141
141
|
});
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
return client;
|
|
145
145
|
}
|
|
146
|
+
/** The events Prisma emits: the logged ones, and the queries to time with a slow query threshold. */
|
|
147
|
+
function emittedEvents() {
|
|
148
|
+
const events = new Set(database_config_1.databaseConfig.LOG_EVENTS);
|
|
149
|
+
if (database_config_1.databaseConfig.SLOW_QUERY_THRESHOLD > 0) {
|
|
150
|
+
events.add(common_1.DatabaseEvent.Query);
|
|
151
|
+
}
|
|
152
|
+
return [...events];
|
|
153
|
+
}
|
|
154
|
+
/** Logs a query, as a warning without its parameters when it is slow. */
|
|
155
|
+
function logQuery(e) {
|
|
156
|
+
const threshold = database_config_1.databaseConfig.SLOW_QUERY_THRESHOLD;
|
|
157
|
+
if (threshold > 0 && e.duration >= threshold) {
|
|
158
|
+
logger.warn({ query: e.query, duration: e.duration, threshold }, 'Slow database query');
|
|
159
|
+
}
|
|
160
|
+
else if (database_config_1.databaseConfig.LOG_EVENTS.includes(common_1.DatabaseEvent.Query)) {
|
|
161
|
+
logger.debug(e, 'Prisma query');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -35,6 +35,9 @@ exports.databaseConfig = (0, common_1.defineConfig)('database', {
|
|
|
35
35
|
LOG_EVENTS: typebox_1.Type.Array(typebox_1.Type.Enum(common_1.DatabaseEvent), {
|
|
36
36
|
default: []
|
|
37
37
|
}),
|
|
38
|
+
/** Duration in milliseconds from which a query is logged as a warning by the `database` logger, `0` to disable it.
|
|
39
|
+
* Default: `0`. */
|
|
40
|
+
SLOW_QUERY_THRESHOLD: typebox_1.Type.Integer({ default: 0, minimum: 0 }),
|
|
38
41
|
/** Database provider: `auto` or `prisma`, or `none` for an application without a database. Default: `'auto'`. */
|
|
39
42
|
PROVIDER: typebox_1.Type.String({ default: 'auto' })
|
|
40
43
|
});
|
|
@@ -5,6 +5,7 @@ const common_1 = require("@appweaver/common");
|
|
|
5
5
|
const database_config_1 = require("./database-config");
|
|
6
6
|
const create_client_1 = require("./create-client");
|
|
7
7
|
const internal_1 = require("@appweaver/core/internal");
|
|
8
|
+
const logger = (0, common_1.createLogger)('database');
|
|
8
9
|
// Members that act on the connection itself, never on a transaction
|
|
9
10
|
const ROOT_MEMBERS = new Set([
|
|
10
11
|
'$connect',
|
|
@@ -27,14 +28,15 @@ class PrismaDatabase extends common_1.Database {
|
|
|
27
28
|
* failing the start, since most drivers connect lazily and the queries reconnect once it is reachable.
|
|
28
29
|
*/
|
|
29
30
|
async onInit() {
|
|
31
|
+
const dbType = (0, common_1.resolveDatabaseType)(database_config_1.databaseConfig.TYPE, database_config_1.databaseConfig.URL);
|
|
32
|
+
const url = this.redactUrl(database_config_1.databaseConfig.URL);
|
|
30
33
|
try {
|
|
31
34
|
await this.connect();
|
|
32
35
|
await this.clientInstance().$queryRaw `SELECT 1`;
|
|
36
|
+
logger.info(`Connected to the ${dbType} database${url ? ` at ${url}` : ''}`);
|
|
33
37
|
}
|
|
34
38
|
catch (error) {
|
|
35
|
-
|
|
36
|
-
const url = this.redactUrl(database_config_1.databaseConfig.URL);
|
|
37
|
-
common_1.logger.error(error, `Cannot connect to the ${dbType} database${url ? ` at ${url}` : ''}`);
|
|
39
|
+
logger.error(error, `Cannot connect to the ${dbType} database${url ? ` at ${url}` : ''}`);
|
|
38
40
|
// The next query creates a new client, which detects the capabilities of the database again
|
|
39
41
|
await this.disconnect().catch(() => undefined);
|
|
40
42
|
this._client = undefined;
|
|
@@ -55,6 +57,7 @@ class PrismaDatabase extends common_1.Database {
|
|
|
55
57
|
}
|
|
56
58
|
await this._client.$disconnect();
|
|
57
59
|
this._client = undefined;
|
|
60
|
+
logger.debug('Disconnected from the database');
|
|
58
61
|
}
|
|
59
62
|
/**
|
|
60
63
|
* Returns the Prisma client, which runs its queries in the transaction the
|
|
@@ -109,8 +112,10 @@ class PrismaDatabase extends common_1.Database {
|
|
|
109
112
|
catch (e) {
|
|
110
113
|
await (0, internal_1.settleTransaction)(context, false);
|
|
111
114
|
if (attempt < (options.retries ?? 0) && this.isWriteConflict(e)) {
|
|
115
|
+
logger.debug({ attempt: attempt + 1, retries: options.retries }, 'Transaction write conflict, retrying');
|
|
112
116
|
continue;
|
|
113
117
|
}
|
|
118
|
+
logger.debug({ error: e.message }, 'Transaction rolled back');
|
|
114
119
|
throw e;
|
|
115
120
|
}
|
|
116
121
|
await (0, internal_1.settleTransaction)(context, true);
|
package/export/export-service.js
CHANGED
|
@@ -7,6 +7,7 @@ const export_config_1 = require("./export-config");
|
|
|
7
7
|
const core_1 = require("@appweaver/core");
|
|
8
8
|
const file_util_1 = require("../utils/file-util");
|
|
9
9
|
const export_util_1 = require("../utils/export-util");
|
|
10
|
+
const logger = (0, common_1.createLogger)('resource');
|
|
10
11
|
class ExportService {
|
|
11
12
|
/**
|
|
12
13
|
* Exports data as a CSV stream based on the provided filter and sort parameters.
|
|
@@ -59,7 +60,7 @@ class ExportService {
|
|
|
59
60
|
}
|
|
60
61
|
catch (e) {
|
|
61
62
|
// End early rather than fail a download already in flight
|
|
62
|
-
|
|
63
|
+
logger.error(e, `${modelName} export error`);
|
|
63
64
|
next = undefined;
|
|
64
65
|
}
|
|
65
66
|
if (chunk !== undefined) {
|
|
@@ -74,7 +75,7 @@ class ExportService {
|
|
|
74
75
|
throw new common_1.ExportError(common_1.ErrorCode.ExportFailed, `${modelName} export failed`, { model: modelName }, { cause: e });
|
|
75
76
|
}
|
|
76
77
|
const fileName = this.generateExportFileName(modelName, 'csv');
|
|
77
|
-
|
|
78
|
+
logger.debug({ modelName, filter, fileName }, 'CSV File export');
|
|
78
79
|
return { stream: exportStream, mimeType: 'text/csv', fileName };
|
|
79
80
|
}
|
|
80
81
|
/** @internal */
|
|
@@ -143,7 +144,7 @@ class ExportService {
|
|
|
143
144
|
}
|
|
144
145
|
catch (e) {
|
|
145
146
|
mappedValues.push('');
|
|
146
|
-
|
|
147
|
+
logger.error(e, 'Export value mapping error.');
|
|
147
148
|
}
|
|
148
149
|
}
|
|
149
150
|
else if ((0, common_1.isString)(mapValue)) {
|
package/factory/create-model.js
CHANGED
|
@@ -6,6 +6,7 @@ const common_1 = require("@appweaver/common");
|
|
|
6
6
|
const internal_1 = require("@appweaver/core/internal");
|
|
7
7
|
const core_1 = require("@appweaver/core");
|
|
8
8
|
const model_schema_1 = require("../resource/model-schema");
|
|
9
|
+
const logger = (0, common_1.createLogger)('resource');
|
|
9
10
|
/**
|
|
10
11
|
* Creates a resource model from its configuration and defines it in the application context. The names of the
|
|
11
12
|
* declared fields are inferred from the configuration, so the `read`, `create`, and `update` options only accept
|
|
@@ -18,7 +19,7 @@ const model_schema_1 = require("../resource/model-schema");
|
|
|
18
19
|
function createModel(modelConfig, override = false) {
|
|
19
20
|
const config = normalizeRelations(modelConfig);
|
|
20
21
|
const resourceModel = buildModel(config);
|
|
21
|
-
|
|
22
|
+
logger.debug({ modelName: resourceModel.name }, 'Created resource model');
|
|
22
23
|
(0, core_1.define)(resourceModel, undefined, override ? 'override' : undefined);
|
|
23
24
|
// The relation inputs and audit fields reference the primary key of another
|
|
24
25
|
// model, which may not be loaded yet. Loaders call this hook once every model
|
package/factory/create-policy.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createPolicy = createPolicy;
|
|
4
4
|
const common_1 = require("@appweaver/common");
|
|
5
5
|
const core_1 = require("@appweaver/core");
|
|
6
|
+
const logger = (0, common_1.createLogger)('resource');
|
|
6
7
|
/**
|
|
7
8
|
* Creates the policy of a model, checking the access to its records and restricting the reads and writes. Once the
|
|
8
9
|
* types are generated, the records the callbacks receive are typed by the model name.
|
|
@@ -14,7 +15,7 @@ const core_1 = require("@appweaver/core");
|
|
|
14
15
|
function createPolicy(config, override = false) {
|
|
15
16
|
config[common_1.RESOURCE_NAME] = (0, common_1.capitalize)(config.modelName);
|
|
16
17
|
config[common_1.RESOURCE_TYPE] = common_1.RESOURCE_POLICY_TYPE;
|
|
17
|
-
|
|
18
|
+
logger.debug({ modelName: config.modelName }, 'Created resource policy');
|
|
18
19
|
(0, core_1.define)(config, undefined, override ? 'override' : undefined);
|
|
19
20
|
return config;
|
|
20
21
|
}
|
|
@@ -5,6 +5,7 @@ const common_1 = require("@appweaver/common");
|
|
|
5
5
|
const core_1 = require("@appweaver/core");
|
|
6
6
|
const resource_service_1 = require("../resource/resource-service");
|
|
7
7
|
const text_search_util_1 = require("../resource/utils/text-search-util");
|
|
8
|
+
const logger = (0, common_1.createLogger)('resource');
|
|
8
9
|
/**
|
|
9
10
|
* Creates the resource service of a model, running the configured hooks around its operations and applying the
|
|
10
11
|
* policy of the model. The update hooks run around both an update and a replacement. Once the types are generated,
|
|
@@ -110,7 +111,7 @@ function createService(config, override = false) {
|
|
|
110
111
|
Service[common_1.CONFIG] = config;
|
|
111
112
|
Service[common_1.RESOURCE_NAME] = name;
|
|
112
113
|
Service[common_1.RESOURCE_TYPE] = common_1.RESOURCE_SERVICE_TYPE;
|
|
113
|
-
|
|
114
|
+
logger.debug({ modelName: config.modelName }, 'Created resource service');
|
|
114
115
|
(0, core_1.define)(Service, undefined, override ? 'override' : undefined);
|
|
115
116
|
return Service;
|
|
116
117
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appweaver/data",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Appweaver - the backend framework for AI-first development (@data)",
|
|
5
5
|
"author": "Luka Matosevic",
|
|
6
6
|
"license": "MIT",
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"csv-stringify": "6.8.0"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"@appweaver/common": "^2.0
|
|
55
|
-
"@appweaver/core": "^2.0
|
|
54
|
+
"@appweaver/common": "^2.1.0",
|
|
55
|
+
"@appweaver/core": "^2.1.0",
|
|
56
56
|
"@prisma/client": "^7.9.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
@@ -6,6 +6,7 @@ const common_1 = require("@appweaver/common");
|
|
|
6
6
|
const core_1 = require("@appweaver/core");
|
|
7
7
|
const internal_1 = require("@appweaver/core/internal");
|
|
8
8
|
const resource_loader_1 = require("./resource-loader");
|
|
9
|
+
const logger = (0, common_1.createLogger)('resource');
|
|
9
10
|
/**
|
|
10
11
|
* The setup of `@appweaver/data`: loads the models, services and policies of the application, then the resources of
|
|
11
12
|
* the modules, unless the `autoLoadResources` parameter of `createApp` is `false`.
|
|
@@ -31,7 +32,7 @@ function warnUnprocessedImages() {
|
|
|
31
32
|
.filter(([, field]) => field?.image)
|
|
32
33
|
.map(([name]) => `${model.name}.${name}`));
|
|
33
34
|
if (fields.length > 0) {
|
|
34
|
-
|
|
35
|
+
logger.warn({ fields }, 'The images of these file fields are stored unprocessed, since no image processor is provided: install ' +
|
|
35
36
|
'@appweaver/sharp, or select one in IMAGE_PROVIDER');
|
|
36
37
|
}
|
|
37
38
|
}
|
|
@@ -8,6 +8,7 @@ const file_util_1 = require("../utils/file-util");
|
|
|
8
8
|
const model_util_1 = require("../utils/model-util");
|
|
9
9
|
const internal_1 = require("@appweaver/core/internal");
|
|
10
10
|
const prune_util_1 = require("./utils/prune-util");
|
|
11
|
+
const logger = (0, common_1.createLogger)('resource');
|
|
11
12
|
/**
|
|
12
13
|
* Loads the application resources: models, services and policies.
|
|
13
14
|
*
|
|
@@ -124,7 +125,7 @@ async function findExports(pattern, cwd, moduleResources) {
|
|
|
124
125
|
for (const filePath of await (0, common_1.findFilesByPattern)(pattern, cwd)) {
|
|
125
126
|
const { value, error } = await (0, common_1.importModule)(filePath);
|
|
126
127
|
if (error) {
|
|
127
|
-
|
|
128
|
+
logger.error(error, 'Resource path import error');
|
|
128
129
|
continue;
|
|
129
130
|
}
|
|
130
131
|
if (value) {
|
|
@@ -15,6 +15,7 @@ const relation_util_1 = require("./utils/relation-util");
|
|
|
15
15
|
const cursor_util_1 = require("./utils/cursor-util");
|
|
16
16
|
const filter_util_1 = require("./utils/filter-util");
|
|
17
17
|
const sort_util_1 = require("./utils/sort-util");
|
|
18
|
+
const logger = (0, common_1.createLogger)('resource');
|
|
18
19
|
class ResourceService {
|
|
19
20
|
modelName;
|
|
20
21
|
/** @internal */
|
|
@@ -404,6 +405,7 @@ class ResourceService {
|
|
|
404
405
|
this._events.emitResourceEvent(this._client.name, 'create', {
|
|
405
406
|
current: resource
|
|
406
407
|
});
|
|
408
|
+
logger.debug({ model: this._client.name, id: resource.id }, 'Resource created');
|
|
407
409
|
return this.projectResource(resource);
|
|
408
410
|
}
|
|
409
411
|
/**
|
|
@@ -535,6 +537,7 @@ class ResourceService {
|
|
|
535
537
|
this._events.emitResourceEvent(this._client.name, 'delete', {
|
|
536
538
|
current: resource
|
|
537
539
|
});
|
|
540
|
+
logger.debug({ model: this._client.name, id, softDelete }, 'Resource deleted');
|
|
538
541
|
return this.projectResource(resource);
|
|
539
542
|
}
|
|
540
543
|
/**
|
|
@@ -678,6 +681,7 @@ class ResourceService {
|
|
|
678
681
|
previous: updateResource,
|
|
679
682
|
current: resource
|
|
680
683
|
});
|
|
684
|
+
logger.debug({ model: this._client.name, id }, 'Resource updated');
|
|
681
685
|
return {
|
|
682
686
|
previous: this.projectResource(updateResource),
|
|
683
687
|
current: this.projectResource(resource)
|
|
@@ -826,7 +830,15 @@ class ResourceService {
|
|
|
826
830
|
* @internal
|
|
827
831
|
*/
|
|
828
832
|
async applyAccessCheck(action, resource) {
|
|
829
|
-
|
|
833
|
+
if ((0, policy_util_1.arePoliciesSkipped)() || (await this.checkAccess(action, resource))) {
|
|
834
|
+
return true;
|
|
835
|
+
}
|
|
836
|
+
logger.debug({
|
|
837
|
+
model: this._client.name,
|
|
838
|
+
action,
|
|
839
|
+
id: resource?.id
|
|
840
|
+
}, 'Resource access denied by its policy');
|
|
841
|
+
return false;
|
|
830
842
|
}
|
|
831
843
|
/**
|
|
832
844
|
* Builds the database conditions of a query filter: the mapped filter, its
|
|
@@ -984,7 +996,7 @@ class ResourceService {
|
|
|
984
996
|
*/
|
|
985
997
|
async bulk(items, operation, prepare) {
|
|
986
998
|
try {
|
|
987
|
-
|
|
999
|
+
const results = await this._db.client().$transaction(async () => {
|
|
988
1000
|
await prepare?.();
|
|
989
1001
|
const results = [];
|
|
990
1002
|
for (const item of items) {
|
|
@@ -995,6 +1007,8 @@ class ResourceService {
|
|
|
995
1007
|
}
|
|
996
1008
|
return results;
|
|
997
1009
|
}, { timeout: resource_config_1.resourceConfig.BULK_TRANSACTION_TIMEOUT });
|
|
1010
|
+
logger.debug({ model: this._client.name, count: results.length }, 'Bulk operation committed');
|
|
1011
|
+
return results;
|
|
998
1012
|
}
|
|
999
1013
|
catch (e) {
|
|
1000
1014
|
// The errors of the items are translated already, which leaves the
|
package/storage/file-service.js
CHANGED
|
@@ -9,6 +9,7 @@ const policy_util_1 = require("../utils/policy-util");
|
|
|
9
9
|
const file_util_1 = require("../utils/file-util");
|
|
10
10
|
const model_util_1 = require("../utils/model-util");
|
|
11
11
|
const internal_1 = require("@appweaver/core/internal");
|
|
12
|
+
const logger = (0, common_1.createLogger)('storage');
|
|
12
13
|
class FileService {
|
|
13
14
|
/** @internal */
|
|
14
15
|
_db = (0, core_1.inject)(common_1.Database);
|
|
@@ -217,7 +218,7 @@ class FileService {
|
|
|
217
218
|
where: { name: fileName }
|
|
218
219
|
}));
|
|
219
220
|
deletedFile.url = (0, file_util_1.buildFileUrl)(deletedFile);
|
|
220
|
-
|
|
221
|
+
logger.debug({ deletedFile }, 'File deleted');
|
|
221
222
|
return deletedFile;
|
|
222
223
|
}
|
|
223
224
|
catch (e) {
|
|
@@ -302,7 +303,7 @@ class FileService {
|
|
|
302
303
|
}));
|
|
303
304
|
}
|
|
304
305
|
catch (e) {
|
|
305
|
-
|
|
306
|
+
logger.error({ resourceName, resourceIds, error: e }, 'Error finding resource files for deletion');
|
|
306
307
|
return [];
|
|
307
308
|
}
|
|
308
309
|
const deletedFiles = [];
|
|
@@ -312,11 +313,11 @@ class FileService {
|
|
|
312
313
|
deletedFiles.push(file);
|
|
313
314
|
}
|
|
314
315
|
else {
|
|
315
|
-
|
|
316
|
+
logger.error({ resourceName, resourceId: file.resourceId, fileName: file.name }, 'Failed to delete file');
|
|
316
317
|
}
|
|
317
318
|
}
|
|
318
319
|
if (deletedFiles.length > 0) {
|
|
319
|
-
|
|
320
|
+
logger.debug({ resourceName, resourceIds, deletedCount: deletedFiles.length }, 'Resource files deleted');
|
|
320
321
|
}
|
|
321
322
|
return deletedFiles;
|
|
322
323
|
}
|
|
@@ -448,7 +449,7 @@ class FileService {
|
|
|
448
449
|
// pipe() does not forward source errors to destinations, so both branches
|
|
449
450
|
// must be destroyed manually to avoid hanging on a failed upload stream.
|
|
450
451
|
fileStream.on('error', (e) => {
|
|
451
|
-
|
|
452
|
+
logger.error(e, 'Error calculating file checksum');
|
|
452
453
|
storageStream.destroy(e);
|
|
453
454
|
checksumStream.destroy(e);
|
|
454
455
|
});
|
|
@@ -502,7 +503,7 @@ class FileService {
|
|
|
502
503
|
file = result[data.fieldName].find((f) => f.name === createFile.name);
|
|
503
504
|
}
|
|
504
505
|
file.url = (0, file_util_1.buildFileUrl)(file);
|
|
505
|
-
|
|
506
|
+
logger.debug({ file }, 'File saved');
|
|
506
507
|
return file;
|
|
507
508
|
}
|
|
508
509
|
catch (e) {
|