@appweaver/data 0.0.0-stage → 2.0.1
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/LICENSE +21 -0
- package/README.md +61 -2
- package/config.d.ts +6 -0
- package/config.js +22 -0
- package/database/create-client.d.ts +11 -0
- package/database/create-client.js +145 -0
- package/database/database-config.d.ts +14 -0
- package/database/database-config.js +40 -0
- package/database/database-error.d.ts +49 -0
- package/database/database-error.js +216 -0
- package/database/index.d.ts +3 -0
- package/database/index.js +19 -0
- package/database/prisma-database.d.ts +33 -0
- package/database/prisma-database.js +178 -0
- package/database/run-transaction.d.ts +21 -0
- package/database/run-transaction.js +15 -0
- package/export/export-config.d.ts +7 -0
- package/export/export-config.js +17 -0
- package/export/export-service.d.ts +22 -0
- package/export/export-service.js +188 -0
- package/export/index.d.ts +1 -0
- package/export/index.js +17 -0
- package/factory/create-model.d.ts +11 -0
- package/factory/create-model.js +479 -0
- package/factory/create-policy.d.ts +12 -0
- package/factory/create-policy.js +20 -0
- package/factory/create-service.d.ts +14 -0
- package/factory/create-service.js +116 -0
- package/index.d.ts +22 -0
- package/index.js +38 -0
- package/internal.d.ts +2 -0
- package/internal.js +6 -0
- package/module.d.ts +3 -0
- package/module.js +81 -0
- package/package.json +67 -3
- package/prisma/client/browser.d.ts +39 -0
- package/prisma/client/browser.js +56 -0
- package/prisma/client/client.d.ts +58 -0
- package/prisma/client/client.js +70 -0
- package/prisma/client/commonInputTypes.d.ts +345 -0
- package/prisma/client/commonInputTypes.js +11 -0
- package/prisma/client/enums.d.ts +1 -0
- package/prisma/client/enums.js +11 -0
- package/prisma/client/internal/class.d.ts +198 -0
- package/prisma/client/internal/class.js +85 -0
- package/prisma/client/internal/prismaNamespace.d.ts +1125 -0
- package/prisma/client/internal/prismaNamespace.js +204 -0
- package/prisma/client/internal/prismaNamespaceBrowser.d.ts +137 -0
- package/prisma/client/internal/prismaNamespaceBrowser.js +175 -0
- package/prisma/client/models/ApiKey.d.ts +1167 -0
- package/prisma/client/models/ApiKey.js +2 -0
- package/prisma/client/models/ConnectedAccount.d.ts +1094 -0
- package/prisma/client/models/ConnectedAccount.js +2 -0
- package/prisma/client/models/File.d.ts +1291 -0
- package/prisma/client/models/File.js +2 -0
- package/prisma/client/models/OneTimeToken.d.ts +1055 -0
- package/prisma/client/models/OneTimeToken.js +2 -0
- package/prisma/client/models/Permission.d.ts +1209 -0
- package/prisma/client/models/Permission.js +2 -0
- package/prisma/client/models/Role.d.ts +1209 -0
- package/prisma/client/models/Role.js +2 -0
- package/prisma/client/models/Seeder.d.ts +1023 -0
- package/prisma/client/models/Seeder.js +2 -0
- package/prisma/client/models.d.ts +8 -0
- package/prisma/client/models.js +2 -0
- package/resource/load-data-resources.d.ts +13 -0
- package/resource/load-data-resources.js +37 -0
- package/resource/model-schema.d.ts +34 -0
- package/resource/model-schema.js +54 -0
- package/resource/resource-config.d.ts +16 -0
- package/resource/resource-config.js +62 -0
- package/resource/resource-loader.d.ts +25 -0
- package/resource/resource-loader.js +138 -0
- package/resource/resource-service.d.ts +462 -0
- package/resource/resource-service.js +1042 -0
- package/resource/schemas/index.d.ts +3 -0
- package/resource/schemas/index.js +19 -0
- package/resource/schemas/resource-aggregate-schema.d.ts +53 -0
- package/resource/schemas/resource-aggregate-schema.js +130 -0
- package/resource/schemas/resource-filter-schema.d.ts +39 -0
- package/resource/schemas/resource-filter-schema.js +153 -0
- package/resource/schemas/resource-sort-schema.d.ts +38 -0
- package/resource/schemas/resource-sort-schema.js +113 -0
- package/resource/utils/aggregate-util.d.ts +113 -0
- package/resource/utils/aggregate-util.js +322 -0
- package/resource/utils/cursor-util.d.ts +60 -0
- package/resource/utils/cursor-util.js +116 -0
- package/resource/utils/date-util.d.ts +22 -0
- package/resource/utils/date-util.js +112 -0
- package/resource/utils/delete-util.d.ts +131 -0
- package/resource/utils/delete-util.js +385 -0
- package/resource/utils/filter-util.d.ts +24 -0
- package/resource/utils/filter-util.js +289 -0
- package/resource/utils/prune-util.d.ts +11 -0
- package/resource/utils/prune-util.js +56 -0
- package/resource/utils/relation-util.d.ts +75 -0
- package/resource/utils/relation-util.js +400 -0
- package/resource/utils/sort-util.d.ts +31 -0
- package/resource/utils/sort-util.js +235 -0
- package/resource/utils/text-search-util.d.ts +14 -0
- package/resource/utils/text-search-util.js +39 -0
- package/resources.d.ts +10 -0
- package/resources.js +26 -0
- package/security/auth-config.d.ts +23 -0
- package/security/auth-config.js +43 -0
- package/security/create-auth-resources.d.ts +28 -0
- package/security/create-auth-resources.js +163 -0
- package/security/helper.d.ts +109 -0
- package/security/helper.js +298 -0
- package/security/resources/api-key/model.d.ts +2 -0
- package/security/resources/api-key/model.js +67 -0
- package/security/resources/api-key/policy.d.ts +2 -0
- package/security/resources/api-key/policy.js +27 -0
- package/security/resources/api-key/service.d.ts +3 -0
- package/security/resources/api-key/service.js +34 -0
- package/security/resources/connected-account/model.d.ts +2 -0
- package/security/resources/connected-account/model.js +57 -0
- package/security/resources/connected-account/service.d.ts +2 -0
- package/security/resources/connected-account/service.js +7 -0
- package/security/resources/one-time-token/model.d.ts +2 -0
- package/security/resources/one-time-token/model.js +36 -0
- package/security/resources/permission/model.d.ts +2 -0
- package/security/resources/permission/model.js +16 -0
- package/security/resources/role/model.d.ts +2 -0
- package/security/resources/role/model.js +22 -0
- package/security/store/database-security-store.d.ts +5 -0
- package/security/store/database-security-store.js +54 -0
- package/seeder/create-seeder.d.ts +29 -0
- package/seeder/create-seeder.js +28 -0
- package/seeder/index.d.ts +2 -0
- package/seeder/index.js +18 -0
- package/seeder/resources/seeder/model.d.ts +2 -0
- package/seeder/resources/seeder/model.js +38 -0
- package/seeder/seeder.d.ts +29 -0
- package/seeder/seeder.js +197 -0
- package/storage/file-service.d.ts +133 -0
- package/storage/file-service.js +586 -0
- package/storage/image-config.d.ts +3 -0
- package/storage/image-config.js +11 -0
- package/storage/resources/file/model.d.ts +2 -0
- package/storage/resources/file/model.js +80 -0
- package/types/auth.d.ts +23 -0
- package/types/auth.js +2 -0
- package/types/generated.d.ts +366 -0
- package/types/generated.js +3 -0
- package/types/storage.d.ts +35 -0
- package/types/storage.js +2 -0
- package/utils/export-util.d.ts +8 -0
- package/utils/export-util.js +26 -0
- package/utils/file-util.d.ts +106 -0
- package/utils/file-util.js +254 -0
- package/utils/model-util.d.ts +36 -0
- package/utils/model-util.js +154 -0
- package/utils/policy-util.d.ts +18 -0
- package/utils/policy-util.js +28 -0
- package/utils/virtual-util.d.ts +11 -0
- package/utils/virtual-util.js +53 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Seeder } from './seeder';
|
|
2
|
+
export type CreateSeederParams = {
|
|
3
|
+
/** A relative path where the application source code is located.
|
|
4
|
+
* This is usually the directory from which the createApp() function is called.
|
|
5
|
+
* (default: ./dist/src) */
|
|
6
|
+
scanPath?: string;
|
|
7
|
+
/** A relative path where the seeder functions are located.
|
|
8
|
+
* (default: ./dist/database/seeders) */
|
|
9
|
+
seedersPath?: string;
|
|
10
|
+
/** A flag indication if the seeder should continue execution even if an error occurs.
|
|
11
|
+
* (default: false) */
|
|
12
|
+
continueOnError?: boolean;
|
|
13
|
+
/** A boolean flag indicating whether the seeder should automatically
|
|
14
|
+
* start seeding after being created. (default: true) **/
|
|
15
|
+
autoSeed?: boolean;
|
|
16
|
+
/** A boolean flag indicating whether to automatically fix all warnings
|
|
17
|
+
* related to the seeders like wrong checksum or deleted seeder files.
|
|
18
|
+
* (default: false) **/
|
|
19
|
+
fixWarnings?: boolean;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Creates the database seeder, loading the providers of the project first, and runs the pending seeders unless
|
|
23
|
+
* `autoSeed` is disabled: the seeders of the modules declaring a `seeders` directory first, then the ones of the
|
|
24
|
+
* application.
|
|
25
|
+
*
|
|
26
|
+
* @param {CreateSeederParams} [params={}] - The seeder parameters.
|
|
27
|
+
* @return {Promise<Seeder>} The created seeder.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createSeeder(params?: CreateSeederParams): Promise<Seeder>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSeeder = createSeeder;
|
|
4
|
+
const common_1 = require("@appweaver/common");
|
|
5
|
+
const database_config_1 = require("../database/database-config");
|
|
6
|
+
const core_1 = require("@appweaver/core");
|
|
7
|
+
const internal_1 = require("@appweaver/core/internal");
|
|
8
|
+
const seeder_1 = require("./seeder");
|
|
9
|
+
/**
|
|
10
|
+
* Creates the database seeder, loading the providers of the project first, and runs the pending seeders unless
|
|
11
|
+
* `autoSeed` is disabled: the seeders of the modules declaring a `seeders` directory first, then the ones of the
|
|
12
|
+
* application.
|
|
13
|
+
*
|
|
14
|
+
* @param {CreateSeederParams} [params={}] - The seeder parameters.
|
|
15
|
+
* @return {Promise<Seeder>} The created seeder.
|
|
16
|
+
*/
|
|
17
|
+
async function createSeeder(params = {}) {
|
|
18
|
+
const scanPath = (0, common_1.resolveSourcePath)(common_1.appConfig.BUILD_PATH, common_1.appConfig.SOURCE_PATH, params.scanPath, './dist/src');
|
|
19
|
+
const seedersPath = (0, common_1.resolveSourcePath)(common_1.appConfig.BUILD_PATH, database_config_1.databaseConfig.SEEDERS_DIR_PATH, params.seedersPath, './dist/database/seeders');
|
|
20
|
+
// Define the providers and the services of the modules
|
|
21
|
+
const modules = await (0, core_1.discoverModules)({ scanPath });
|
|
22
|
+
await (0, internal_1.loadProviders)(modules, scanPath);
|
|
23
|
+
const seeder = new seeder_1.Seeder(seedersPath, params.continueOnError, params.fixWarnings, modules.flatMap(({ module }) => module.seeders ? [{ name: module.name, path: module.seeders }] : []));
|
|
24
|
+
if (params.autoSeed !== false) {
|
|
25
|
+
await seeder.seed();
|
|
26
|
+
}
|
|
27
|
+
return seeder;
|
|
28
|
+
}
|
package/seeder/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./create-seeder"), exports);
|
|
18
|
+
__exportStar(require("./seeder"), exports);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const create_model_1 = require("../../../factory/create-model");
|
|
4
|
+
exports.default = (0, create_model_1.createModel)({
|
|
5
|
+
name: 'Seeder',
|
|
6
|
+
tableName: '_seeders',
|
|
7
|
+
generateTypes: false,
|
|
8
|
+
id: {
|
|
9
|
+
type: 'string',
|
|
10
|
+
generator: 'uuid()'
|
|
11
|
+
},
|
|
12
|
+
audit: {
|
|
13
|
+
updatedAt: false,
|
|
14
|
+
createdAt: false,
|
|
15
|
+
createdById: false
|
|
16
|
+
},
|
|
17
|
+
scalars: {
|
|
18
|
+
checksum: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
maxLength: 255
|
|
21
|
+
},
|
|
22
|
+
seederName: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
unique: true,
|
|
25
|
+
maxLength: 511
|
|
26
|
+
},
|
|
27
|
+
startedAt: {
|
|
28
|
+
type: 'dateTime'
|
|
29
|
+
},
|
|
30
|
+
finishedAt: {
|
|
31
|
+
type: 'dateTime'
|
|
32
|
+
},
|
|
33
|
+
logs: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
required: false
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { LifecycleManager } from '@appweaver/core/internal';
|
|
2
|
+
/** The seeders directory of a module, whose seeders run before the ones of the application. */
|
|
3
|
+
export type ModuleSeeders = {
|
|
4
|
+
/** The name of the module, prefixing the names of its seeders, i.e. `billing/1-plans`. */
|
|
5
|
+
name: string;
|
|
6
|
+
/** The absolute path of the seeders directory. */
|
|
7
|
+
path: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Represents a seeder class that manages the application seeders processes.
|
|
11
|
+
*/
|
|
12
|
+
export declare class Seeder extends LifecycleManager {
|
|
13
|
+
private readonly _dirPath;
|
|
14
|
+
private readonly _continueOnError;
|
|
15
|
+
private readonly _fixWarnings;
|
|
16
|
+
private readonly _moduleSeeders;
|
|
17
|
+
constructor(_dirPath: string, _continueOnError?: boolean, _fixWarnings?: boolean, _moduleSeeders?: ModuleSeeders[]);
|
|
18
|
+
/**
|
|
19
|
+
* Executes the seeders by discovering, validating, and running seeder files.
|
|
20
|
+
* Logs the seeding process, including warnings for checksum mismatches
|
|
21
|
+
* and files that do not exist but are present in the database table.
|
|
22
|
+
*
|
|
23
|
+
* @return Resolves when all seeders have been processed. Logs relevant
|
|
24
|
+
* information during execution.
|
|
25
|
+
*/
|
|
26
|
+
seed(): Promise<void>;
|
|
27
|
+
/** Destroys the services the seeder started, i.e. the database connection. */
|
|
28
|
+
close(): Promise<void>;
|
|
29
|
+
}
|
package/seeder/seeder.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Seeder = void 0;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
10
|
+
const common_1 = require("@appweaver/common");
|
|
11
|
+
const internal_1 = require("@appweaver/core/internal");
|
|
12
|
+
const core_1 = require("@appweaver/core");
|
|
13
|
+
/**
|
|
14
|
+
* Represents a seeder class that manages the application seeders processes.
|
|
15
|
+
*/
|
|
16
|
+
class Seeder extends internal_1.LifecycleManager {
|
|
17
|
+
_dirPath;
|
|
18
|
+
_continueOnError;
|
|
19
|
+
_fixWarnings;
|
|
20
|
+
_moduleSeeders;
|
|
21
|
+
/** @internal */
|
|
22
|
+
_db = (0, core_1.inject)(common_1.Database);
|
|
23
|
+
constructor(_dirPath, _continueOnError = false, _fixWarnings = false, _moduleSeeders = []) {
|
|
24
|
+
super();
|
|
25
|
+
this._dirPath = _dirPath;
|
|
26
|
+
this._continueOnError = _continueOnError;
|
|
27
|
+
this._fixWarnings = _fixWarnings;
|
|
28
|
+
this._moduleSeeders = _moduleSeeders;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Executes the seeders by discovering, validating, and running seeder files.
|
|
32
|
+
* Logs the seeding process, including warnings for checksum mismatches
|
|
33
|
+
* and files that do not exist but are present in the database table.
|
|
34
|
+
*
|
|
35
|
+
* @return Resolves when all seeders have been processed. Logs relevant
|
|
36
|
+
* information during execution.
|
|
37
|
+
*/
|
|
38
|
+
async seed() {
|
|
39
|
+
await this.init();
|
|
40
|
+
// The seeders of the modules run first, in their load order
|
|
41
|
+
const directories = [
|
|
42
|
+
...this._moduleSeeders,
|
|
43
|
+
{ name: '', path: this._dirPath }
|
|
44
|
+
];
|
|
45
|
+
const seederFiles = [];
|
|
46
|
+
const missing = [];
|
|
47
|
+
for (const { name, path: dirPath } of directories) {
|
|
48
|
+
try {
|
|
49
|
+
seederFiles.push(...(await this.loadSeederFiles(dirPath, name)));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
console.warn(`Seeders directory ${dirPath} is not accessible`);
|
|
53
|
+
missing.push(name);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (missing.length === directories.length) {
|
|
57
|
+
console.error('No seeders directory is accessible');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
let calledSeedersCount = 0;
|
|
61
|
+
const relativePath = this._dirPath
|
|
62
|
+
.replace(process.cwd(), '.')
|
|
63
|
+
.replace(/\\/g, '/');
|
|
64
|
+
const modulesCount = this._moduleSeeders.length;
|
|
65
|
+
console.log(`${seederFiles.length} seeder${seederFiles.length === 1 ? '' : 's'} found in ${relativePath}${modulesCount > 0
|
|
66
|
+
? ` and ${modulesCount} module${modulesCount === 1 ? '' : 's'}`
|
|
67
|
+
: ''}`);
|
|
68
|
+
console.log('');
|
|
69
|
+
// For each seeder file, skip if it has already been seeded, otherwise
|
|
70
|
+
// execute all of it's exported functions and write results to a database
|
|
71
|
+
for (const seederFile of seederFiles) {
|
|
72
|
+
const seederName = seederFile.name;
|
|
73
|
+
const seeder = await this.getSeeder(seederName);
|
|
74
|
+
if (seeder) {
|
|
75
|
+
const checksum = await this.seederHash(seederFile.path);
|
|
76
|
+
if (checksum !== seeder.checksum) {
|
|
77
|
+
console.warn(`Warning: seeder ${seederName} has different checksum: ${checksum}, expected checksum: ${seeder.checksum}`);
|
|
78
|
+
if (this._fixWarnings) {
|
|
79
|
+
await this._db.client().seeder.update({
|
|
80
|
+
where: { id: seeder.id },
|
|
81
|
+
data: { checksum }
|
|
82
|
+
});
|
|
83
|
+
console.log('Fix: seeder checksum updated');
|
|
84
|
+
}
|
|
85
|
+
console.log('');
|
|
86
|
+
}
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
console.log(`Seeding ${seederName}...`);
|
|
90
|
+
await this.executeSeeder(seederFile);
|
|
91
|
+
console.log(`${seederName} seeded`);
|
|
92
|
+
console.log('');
|
|
93
|
+
calledSeedersCount++;
|
|
94
|
+
}
|
|
95
|
+
// The seeders of a missing directory are not reported as deleted
|
|
96
|
+
const seedersWithoutFiles = (await this.findSeedersWithoutFiles(seederFiles)).filter((seeder) => !missing.includes(this.seederOwner(seeder.seederName)));
|
|
97
|
+
for (const seeder of seedersWithoutFiles) {
|
|
98
|
+
console.warn(`Warning: seeder ${seeder.seederName} does not exist but is present in database table`);
|
|
99
|
+
if (this._fixWarnings) {
|
|
100
|
+
await this._db.client().seeder.delete({ where: { id: seeder.id } });
|
|
101
|
+
console.log('Fix: removed seeder from database');
|
|
102
|
+
}
|
|
103
|
+
console.log('');
|
|
104
|
+
}
|
|
105
|
+
if (calledSeedersCount === 0) {
|
|
106
|
+
console.log(`No pending seeders to execute`);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
console.log(`Executed ${calledSeedersCount} seeder${calledSeedersCount === 1 ? '' : 's'}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/** Destroys the services the seeder started, i.e. the database connection. */
|
|
113
|
+
async close() {
|
|
114
|
+
await this.destroy();
|
|
115
|
+
}
|
|
116
|
+
/** @internal */
|
|
117
|
+
async loadSeederFiles(dirPath, moduleName) {
|
|
118
|
+
const entries = await promises_1.default.readdir(dirPath, { withFileTypes: true });
|
|
119
|
+
return entries
|
|
120
|
+
.filter((e) => e.isFile() && /\.(js|ts)$/.test(e.name) && !e.name.endsWith('.d.ts'))
|
|
121
|
+
.map((e) => e.name)
|
|
122
|
+
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' }))
|
|
123
|
+
.map((fileName) => {
|
|
124
|
+
const name = fileName.replace(/\.(js|ts)$/, '');
|
|
125
|
+
return {
|
|
126
|
+
path: node_path_1.default.join(dirPath, fileName),
|
|
127
|
+
name: moduleName ? `${moduleName}/${name}` : name
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/** @internal */
|
|
132
|
+
async executeSeeder(seederFile) {
|
|
133
|
+
const start = new Date();
|
|
134
|
+
const { value, error } = await (0, common_1.importModule)(seederFile.path);
|
|
135
|
+
if (error) {
|
|
136
|
+
if (this._continueOnError) {
|
|
137
|
+
console.error(error);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
const logs = [];
|
|
143
|
+
// Execute all exported functions from a seeder file and log errors or
|
|
144
|
+
// function responses
|
|
145
|
+
for (const seederFn of Object.values(value)) {
|
|
146
|
+
if ((0, common_1.isFunction)(seederFn)) {
|
|
147
|
+
try {
|
|
148
|
+
const result = await seederFn();
|
|
149
|
+
if (result) {
|
|
150
|
+
logs.push(JSON.stringify(result));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (e) {
|
|
154
|
+
if (!this._continueOnError) {
|
|
155
|
+
throw e;
|
|
156
|
+
}
|
|
157
|
+
console.error(e);
|
|
158
|
+
logs.push(`Error: ${e.message}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const checksum = await this.seederHash(seederFile.path);
|
|
163
|
+
// Insert seeder result into the database
|
|
164
|
+
await this._db.client().seeder.create({
|
|
165
|
+
data: {
|
|
166
|
+
checksum,
|
|
167
|
+
seederName: seederFile.name,
|
|
168
|
+
startedAt: start,
|
|
169
|
+
finishedAt: new Date(),
|
|
170
|
+
logs: logs.length > 0 ? logs.join('\n') : null
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/** @internal */
|
|
175
|
+
async findSeedersWithoutFiles(seederFiles) {
|
|
176
|
+
const seederNames = seederFiles.map((file) => file.name);
|
|
177
|
+
return this._db.client().seeder.findMany({
|
|
178
|
+
where: {
|
|
179
|
+
seederName: { notIn: seederNames }
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
/** @internal */
|
|
184
|
+
seederOwner(seederName) {
|
|
185
|
+
const owner = this._moduleSeeders.find(({ name }) => seederName.startsWith(`${name}/`));
|
|
186
|
+
return owner?.name ?? '';
|
|
187
|
+
}
|
|
188
|
+
/** @internal */
|
|
189
|
+
async getSeeder(seederName) {
|
|
190
|
+
return this._db.client().seeder.findFirst({ where: { seederName } });
|
|
191
|
+
}
|
|
192
|
+
/** @internal */
|
|
193
|
+
async seederHash(seederFile) {
|
|
194
|
+
return (0, common_1.makeHash)(node_fs_1.default.createReadStream(seederFile, 'utf8'));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
exports.Seeder = Seeder;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { ContentStream, Resource, ResourceClient, ResourceId } from '@appweaver/common';
|
|
2
|
+
import { File } from '../types/generated';
|
|
3
|
+
import { FileBuffer, UploadedFile, UploadPart } from '../types/storage';
|
|
4
|
+
export type FileStream = {
|
|
5
|
+
content: ContentStream;
|
|
6
|
+
fileName: string;
|
|
7
|
+
mimeType: string;
|
|
8
|
+
start: number;
|
|
9
|
+
end: number;
|
|
10
|
+
};
|
|
11
|
+
/** A file the clients download from the URL of its storage, i.e. a signed URL of a cloud storage. */
|
|
12
|
+
export type FileRedirect = {
|
|
13
|
+
url: string;
|
|
14
|
+
fileName: string;
|
|
15
|
+
mimeType: string;
|
|
16
|
+
};
|
|
17
|
+
export declare class FileService {
|
|
18
|
+
/**
|
|
19
|
+
* Searches for a file by its name from a database.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} fileName - The name of the file to search for.
|
|
22
|
+
* @return {Promise<File>} A promise that resolves to the file object if found.
|
|
23
|
+
* @throws {FileError} Throws an error if the file name is placed under a reserved storage path, if a database error
|
|
24
|
+
* occurs, or if the file is not found.
|
|
25
|
+
*/
|
|
26
|
+
findByName(fileName: string): Promise<File>;
|
|
27
|
+
/**
|
|
28
|
+
* Streams a file from storage, handling authorization and range-based requests.
|
|
29
|
+
*
|
|
30
|
+
* @param {string} fileName - The name of the file to stream.
|
|
31
|
+
* @param {string} [range] - An optional range header specifying the byte range to stream.
|
|
32
|
+
* @return {Promise<FileStream>} A promise that resolves to a file stream object containing the content, metadata, and
|
|
33
|
+
* range details.
|
|
34
|
+
* @throws {FileError} `FILE_NOT_FOUND` if the file does not exist.
|
|
35
|
+
* @throws {FileError} `FILE_FORBIDDEN` if user access to the file is forbidden.
|
|
36
|
+
* @throws {FileError} `FILE_STORAGE_ERROR` if an error occurs while reading the file from storage.
|
|
37
|
+
*/
|
|
38
|
+
stream(fileName: string, range?: string): Promise<FileStream>;
|
|
39
|
+
/**
|
|
40
|
+
* Opens a file for a download, handling authorization like {@link stream}: the URL of its storage when the storage
|
|
41
|
+
* serves its files directly, otherwise the stream of the file.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} fileName - The name of the file to download.
|
|
44
|
+
* @param {string} [range] - An optional range header specifying the byte range to stream.
|
|
45
|
+
* @return {Promise<FileStream | FileRedirect>} The URL of the file, or its stream.
|
|
46
|
+
* @throws {FileError} The errors of {@link stream}.
|
|
47
|
+
*/
|
|
48
|
+
download(fileName: string, range?: string): Promise<FileStream | FileRedirect>;
|
|
49
|
+
/**
|
|
50
|
+
* Saves a file to storage, validates its properties, and associates it with a specific resource and client.
|
|
51
|
+
*
|
|
52
|
+
* @param {UploadedFile} data The multipart file object containing the file data, field name, filename, and metadata.
|
|
53
|
+
* @param {Resource} resource The resource object with which the file is being associated.
|
|
54
|
+
* @param {ResourceClient} client The database client responsible for handling the resource.
|
|
55
|
+
* @return {Promise<File>} A promise that resolves to the saved file object or rejects with an error if the operation
|
|
56
|
+
* fails.
|
|
57
|
+
* @throws {FileError} Throws an error if file validation, storage, or resource association fails.
|
|
58
|
+
*/
|
|
59
|
+
saveFile(data: UploadedFile, resource: Resource, client: ResourceClient): Promise<File>;
|
|
60
|
+
/**
|
|
61
|
+
* Saves an in-memory file to storage and associates it with a file field of a specific resource. Behaves exactly like
|
|
62
|
+
* {@link saveFile}, except the content is taken from a buffer instead of a multipart upload, which makes it usable
|
|
63
|
+
* outside a file upload request, for an avatar downloaded from an OAuth2 provider or an image the application
|
|
64
|
+
* generates itself.
|
|
65
|
+
*
|
|
66
|
+
* @param {string} fieldName The file field of the resource the file is saved to.
|
|
67
|
+
* @param {FileBuffer} file The file content, along with its original name and media type.
|
|
68
|
+
* @param {Resource} resource The resource object with which the file is being associated.
|
|
69
|
+
* @param {ResourceClient} client The database client responsible for handling the resource.
|
|
70
|
+
* @return {Promise<File>} A promise that resolves to the saved file object or rejects with an error if the operation
|
|
71
|
+
* fails.
|
|
72
|
+
* @throws {FileError} Throws an error if file validation, storage, or resource association fails.
|
|
73
|
+
*/
|
|
74
|
+
saveBuffer(fieldName: string, file: FileBuffer, resource: Resource, client: ResourceClient): Promise<File>;
|
|
75
|
+
/**
|
|
76
|
+
* Saves files received as an asynchronous iterable of multipart data.
|
|
77
|
+
*
|
|
78
|
+
* @param {AsyncIterableIterator<UploadPart>} parts - The asynchronous iterable providing multipart data where each
|
|
79
|
+
* part is expected to represent a file or other data.
|
|
80
|
+
* @param {Resource} resource - The resource associated with the files being saved, providing additional contextual
|
|
81
|
+
* information.
|
|
82
|
+
* @param {ResourceClient} client - The database client responsible for handling resource-related operations, such as
|
|
83
|
+
* database interaction.
|
|
84
|
+
* @return {Promise<File[]>} A promise that resolves to an array of successfully saved files. Rejects with an error
|
|
85
|
+
* if one or more files fail to save.
|
|
86
|
+
*/
|
|
87
|
+
saveFiles(parts: AsyncIterableIterator<UploadPart>, resource: Resource, client: ResourceClient): Promise<File[]>;
|
|
88
|
+
/**
|
|
89
|
+
* Deletes a file from the storage and database if it meets the specified conditions.
|
|
90
|
+
*
|
|
91
|
+
* @param {string} fileName - The name of the file to be deleted.
|
|
92
|
+
* @param {string} fieldName - The field associated with the resource to which the file belongs.
|
|
93
|
+
* @param {Resource} resource - The resource that the file is associated with.
|
|
94
|
+
* @param {ResourceClient} client - The client instance representing the resource's type.
|
|
95
|
+
* @return {Promise<File>} A promise that resolves with the deleted file information upon successful deletion.
|
|
96
|
+
* @throws {FileError} If the file is not associated with the provided resource or client,
|
|
97
|
+
* if the current user does not have permission to delete the file,
|
|
98
|
+
* if there is an error removing the file from storage,
|
|
99
|
+
* or if there is an error deleting the file from the database.
|
|
100
|
+
*/
|
|
101
|
+
deleteFile(fileName: string, fieldName: string, resource: Resource, client: ResourceClient): Promise<File>;
|
|
102
|
+
/**
|
|
103
|
+
* Deletes a list of files associated with a given resource using the provided resource client.
|
|
104
|
+
*
|
|
105
|
+
* @param {Record<string, string | string[]>} fileNames - An object where the keys correspond to file fields and the
|
|
106
|
+
* values are either a string (single file name) or an array of file names to be deleted.
|
|
107
|
+
* @param {Resource} resource - The resource instance associated with the files to be deleted.
|
|
108
|
+
* @param {ResourceClient} client - The resource a client used to handle deletion operations.
|
|
109
|
+
* @return {Promise<File[]>} A promise that resolves to an array of files that were successfully deleted. Rejects with
|
|
110
|
+
* an error if no files could be deleted.
|
|
111
|
+
*/
|
|
112
|
+
deleteFiles(fileNames: Record<string, string | string[]>, resource: Resource, client: ResourceClient): Promise<File[]>;
|
|
113
|
+
/**
|
|
114
|
+
* Deletes all files associated with a resource removed from the database,
|
|
115
|
+
* for the file fields not set to `onResourceDeleted: 'keep'`.
|
|
116
|
+
*
|
|
117
|
+
* @param {string} resourceName - The resource model name.
|
|
118
|
+
* @param {ResourceId} id - The ID of the deleted resource.
|
|
119
|
+
* @return {Promise<File[]>} A promise that resolves to the list of successfully deleted files.
|
|
120
|
+
*/
|
|
121
|
+
deleteResourceFiles(resourceName: string, id: ResourceId): Promise<File[]>;
|
|
122
|
+
/**
|
|
123
|
+
* Deletes all files associated with the given deleted resources of one model from the storage and the database. A
|
|
124
|
+
* resource removed from the database loses the files of every field not set to `onResourceDeleted: 'keep'`, while a
|
|
125
|
+
* soft deleted resource only loses the files of the fields set to `onResourceSoftDeleted: 'delete'`.
|
|
126
|
+
*
|
|
127
|
+
* @param {string} resourceName - The resource model name.
|
|
128
|
+
* @param {ResourceId[]} ids - The IDs of the deleted resources.
|
|
129
|
+
* @param {boolean} [softDeleted=false] - Whether the resources were soft deleted.
|
|
130
|
+
* @return {Promise<File[]>} A promise that resolves to the list of successfully deleted files.
|
|
131
|
+
*/
|
|
132
|
+
deleteResourcesFiles(resourceName: string, ids: ResourceId[], softDeleted?: boolean): Promise<File[]>;
|
|
133
|
+
}
|