@modular-rest/server 1.18.1 → 1.20.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/.nvmrc +1 -1
- package/dist/application.js +5 -4
- package/dist/class/collection_definition.d.ts +28 -3
- package/dist/class/collection_definition.js +72 -2
- package/dist/class/combinator.js +7 -3
- package/dist/class/directory.d.ts +2 -4
- package/dist/class/directory.js +42 -64
- package/dist/helper/data_insertion.js +93 -26
- package/dist/index.d.ts +12 -1
- package/dist/index.js +13 -1
- package/dist/services/data_provider/model_registry.d.ts +72 -0
- package/dist/services/data_provider/model_registry.js +214 -0
- package/dist/services/data_provider/service.js +73 -14
- package/dist/services/file/service.d.ts +47 -78
- package/dist/services/file/service.js +114 -155
- package/dist/services/functions/service.js +4 -4
- package/dist/services/jwt/router.js +2 -1
- package/dist/services/user_manager/router.js +1 -1
- package/dist/services/user_manager/service.js +48 -17
- package/jest.config.ts +18 -0
- package/package.json +11 -2
- package/src/application.ts +5 -4
- package/src/class/collection_definition.ts +94 -4
- package/src/class/combinator.ts +10 -3
- package/src/class/directory.ts +40 -58
- package/src/helper/data_insertion.ts +101 -27
- package/src/index.ts +13 -1
- package/src/services/data_provider/model_registry.ts +243 -0
- package/src/services/data_provider/service.ts +74 -14
- package/src/services/file/service.ts +136 -178
- package/src/services/functions/service.ts +4 -4
- package/src/services/jwt/router.ts +2 -1
- package/src/services/user_manager/router.ts +1 -1
- package/src/services/user_manager/service.ts +49 -20
- package/tests/helpers/test-app.ts +182 -0
- package/tests/router/data-provider.router.int.test.ts +192 -0
- package/tests/router/file.router.int.test.ts +104 -0
- package/tests/router/functions.router.int.test.ts +91 -0
- package/tests/router/jwt.router.int.test.ts +69 -0
- package/tests/router/user-manager.router.int.test.ts +85 -0
- package/tests/setup/jest.setup.ts +5 -0
package/.nvmrc
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
v22.12.0
|
package/dist/application.js
CHANGED
|
@@ -206,16 +206,17 @@ async function createRest(options) {
|
|
|
206
206
|
extension: '.js',
|
|
207
207
|
},
|
|
208
208
|
});
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
209
|
+
}
|
|
210
|
+
// 5. Plug in additional defined functions
|
|
211
|
+
if (config_1.config.functions) {
|
|
212
|
+
combinator_1.default.addFunctionsByArray(config_1.config.functions);
|
|
213
213
|
}
|
|
214
214
|
// 4. Setting up default services
|
|
215
215
|
try {
|
|
216
216
|
await require('./helper/presetup_services').setup(config_1.config);
|
|
217
217
|
}
|
|
218
218
|
catch (e) {
|
|
219
|
+
console.error(`[createRest] Error in setup:`, e);
|
|
219
220
|
return Promise.reject(e);
|
|
220
221
|
}
|
|
221
222
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Schema } from 'mongoose';
|
|
1
|
+
import { Schema, Model } from 'mongoose';
|
|
2
2
|
import { Permission } from './security';
|
|
3
3
|
import { DatabaseTrigger } from './database_trigger';
|
|
4
|
+
import { MongoOption } from '../services/data_provider/service';
|
|
4
5
|
/**
|
|
5
6
|
* Configuration options for creating a collection definition.
|
|
6
7
|
* This interface defines the structure for configuring MongoDB collections with their associated
|
|
@@ -24,6 +25,11 @@ interface CollectionDefinitionOptions {
|
|
|
24
25
|
* @see https://mongoosejs.com/docs/5.x/docs/guide.html
|
|
25
26
|
*/
|
|
26
27
|
schema: Schema<any>;
|
|
28
|
+
/**
|
|
29
|
+
* Optional MongoDB connection options. If not provided, will use config.mongo if available.
|
|
30
|
+
* This is used to pre-create the model before server startup.
|
|
31
|
+
*/
|
|
32
|
+
mongoOption?: MongoOption;
|
|
27
33
|
}
|
|
28
34
|
/**
|
|
29
35
|
* To have define any collection in your database you haveto use below method in your `db.[js|ts]` file and export an array of CollectionDefinition instances.
|
|
@@ -31,7 +37,7 @@ interface CollectionDefinitionOptions {
|
|
|
31
37
|
* @param {CollectionDefinitionOptions} options - The options for the collection
|
|
32
38
|
* @expandType CollectionDefinitionOptions
|
|
33
39
|
*
|
|
34
|
-
* @returns A
|
|
40
|
+
* @returns A CollectionDefinition instance with a model property that returns the mongoose model
|
|
35
41
|
*
|
|
36
42
|
* @public
|
|
37
43
|
*
|
|
@@ -48,9 +54,16 @@ interface CollectionDefinitionOptions {
|
|
|
48
54
|
* // trigger: DatabaseTrigger[]
|
|
49
55
|
* })
|
|
50
56
|
* ]
|
|
57
|
+
*
|
|
58
|
+
* // Access the model directly:
|
|
59
|
+
* const userCollection = defineCollection({...});
|
|
60
|
+
* const UserModel = userCollection.model;
|
|
61
|
+
* const users = await UserModel.find();
|
|
51
62
|
* ```
|
|
52
63
|
*/
|
|
53
|
-
export declare function defineCollection(options: CollectionDefinitionOptions): CollectionDefinition
|
|
64
|
+
export declare function defineCollection(options: CollectionDefinitionOptions): CollectionDefinition & {
|
|
65
|
+
model: Model<any>;
|
|
66
|
+
};
|
|
54
67
|
/**
|
|
55
68
|
* A class that represents a MongoDB collection configuration. Provides full support for schema validation, access control through permissions,
|
|
56
69
|
* and custom triggers for various database operations.
|
|
@@ -99,6 +112,8 @@ export declare class CollectionDefinition {
|
|
|
99
112
|
permissions: Permission[];
|
|
100
113
|
/** @readonly Optional database triggers */
|
|
101
114
|
triggers?: DatabaseTrigger[];
|
|
115
|
+
/** Optional mongoose model for this collection */
|
|
116
|
+
private _model;
|
|
102
117
|
/**
|
|
103
118
|
* Creates a new CollectionDefinition instance
|
|
104
119
|
*
|
|
@@ -108,5 +123,15 @@ export declare class CollectionDefinition {
|
|
|
108
123
|
* @beta
|
|
109
124
|
*/
|
|
110
125
|
constructor({ database, collection, schema, permissions, triggers, }: CollectionDefinitionOptions);
|
|
126
|
+
/**
|
|
127
|
+
* Get the mongoose model for this collection
|
|
128
|
+
* @returns {Model<any> | null} The mongoose model or null if not set
|
|
129
|
+
*/
|
|
130
|
+
getModel(): Model<any> | null;
|
|
131
|
+
/**
|
|
132
|
+
* Set the mongoose model for this collection
|
|
133
|
+
* @param {Model<any>} model - The mongoose model
|
|
134
|
+
*/
|
|
135
|
+
setModel(model: Model<any>): void;
|
|
111
136
|
}
|
|
112
137
|
export {};
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.CollectionDefinition = void 0;
|
|
4
7
|
exports.defineCollection = defineCollection;
|
|
8
|
+
const model_registry_1 = __importDefault(require("../services/data_provider/model_registry"));
|
|
9
|
+
const config_1 = require("../config");
|
|
5
10
|
/**
|
|
6
11
|
* To have define any collection in your database you haveto use below method in your `db.[js|ts]` file and export an array of CollectionDefinition instances.
|
|
7
12
|
*
|
|
8
13
|
* @param {CollectionDefinitionOptions} options - The options for the collection
|
|
9
14
|
* @expandType CollectionDefinitionOptions
|
|
10
15
|
*
|
|
11
|
-
* @returns A
|
|
16
|
+
* @returns A CollectionDefinition instance with a model property that returns the mongoose model
|
|
12
17
|
*
|
|
13
18
|
* @public
|
|
14
19
|
*
|
|
@@ -25,10 +30,59 @@ exports.defineCollection = defineCollection;
|
|
|
25
30
|
* // trigger: DatabaseTrigger[]
|
|
26
31
|
* })
|
|
27
32
|
* ]
|
|
33
|
+
*
|
|
34
|
+
* // Access the model directly:
|
|
35
|
+
* const userCollection = defineCollection({...});
|
|
36
|
+
* const UserModel = userCollection.model;
|
|
37
|
+
* const users = await UserModel.find();
|
|
28
38
|
* ```
|
|
29
39
|
*/
|
|
30
40
|
function defineCollection(options) {
|
|
31
|
-
|
|
41
|
+
const definition = new CollectionDefinition(options);
|
|
42
|
+
// Try to get mongoOption from options or config
|
|
43
|
+
let mongoOption = options.mongoOption;
|
|
44
|
+
if (!mongoOption && config_1.config.mongo) {
|
|
45
|
+
mongoOption = config_1.config.mongo;
|
|
46
|
+
}
|
|
47
|
+
// If mongoOption is available, register the collection and create the model
|
|
48
|
+
if (mongoOption) {
|
|
49
|
+
const model = model_registry_1.default.registerCollection(definition, mongoOption);
|
|
50
|
+
definition.setModel(model);
|
|
51
|
+
}
|
|
52
|
+
// Create a proxy object that includes both CollectionDefinition properties and model
|
|
53
|
+
const result = definition;
|
|
54
|
+
// Add model property getter
|
|
55
|
+
Object.defineProperty(result, 'model', {
|
|
56
|
+
get() {
|
|
57
|
+
// If model was already set, return it
|
|
58
|
+
const existingModel = definition.getModel();
|
|
59
|
+
if (existingModel) {
|
|
60
|
+
return existingModel;
|
|
61
|
+
}
|
|
62
|
+
// Otherwise, try to get from registry
|
|
63
|
+
const registryModel = model_registry_1.default.getModel(definition.database, definition.collection);
|
|
64
|
+
if (registryModel) {
|
|
65
|
+
definition.setModel(registryModel);
|
|
66
|
+
return registryModel;
|
|
67
|
+
}
|
|
68
|
+
// If still not found, try to get mongoOption from config and register now
|
|
69
|
+
let currentMongoOption = mongoOption;
|
|
70
|
+
if (!currentMongoOption && config_1.config.mongo) {
|
|
71
|
+
currentMongoOption = config_1.config.mongo;
|
|
72
|
+
}
|
|
73
|
+
if (currentMongoOption) {
|
|
74
|
+
const newModel = model_registry_1.default.registerCollection(definition, currentMongoOption);
|
|
75
|
+
definition.setModel(newModel);
|
|
76
|
+
return newModel;
|
|
77
|
+
}
|
|
78
|
+
throw new Error(`Model for ${definition.database}.${definition.collection} is not available. ` +
|
|
79
|
+
`Ensure mongoOption is provided in defineCollection options, config.mongo is set, ` +
|
|
80
|
+
`or the collection is registered via addCollectionDefinitionByList before accessing the model.`);
|
|
81
|
+
},
|
|
82
|
+
enumerable: true,
|
|
83
|
+
configurable: true,
|
|
84
|
+
});
|
|
85
|
+
return result;
|
|
32
86
|
}
|
|
33
87
|
/**
|
|
34
88
|
* A class that represents a MongoDB collection configuration. Provides full support for schema validation, access control through permissions,
|
|
@@ -77,11 +131,27 @@ class CollectionDefinition {
|
|
|
77
131
|
* @beta
|
|
78
132
|
*/
|
|
79
133
|
constructor({ database, collection, schema, permissions, triggers, }) {
|
|
134
|
+
/** Optional mongoose model for this collection */
|
|
135
|
+
this._model = null;
|
|
80
136
|
this.database = database;
|
|
81
137
|
this.collection = collection;
|
|
82
138
|
this.schema = schema;
|
|
83
139
|
this.permissions = permissions;
|
|
84
140
|
this.triggers = triggers;
|
|
85
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* Get the mongoose model for this collection
|
|
144
|
+
* @returns {Model<any> | null} The mongoose model or null if not set
|
|
145
|
+
*/
|
|
146
|
+
getModel() {
|
|
147
|
+
return this._model;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Set the mongoose model for this collection
|
|
151
|
+
* @param {Model<any>} model - The mongoose model
|
|
152
|
+
*/
|
|
153
|
+
setModel(model) {
|
|
154
|
+
this._model = model;
|
|
155
|
+
}
|
|
86
156
|
}
|
|
87
157
|
exports.CollectionDefinition = CollectionDefinition;
|
package/dist/class/combinator.js
CHANGED
|
@@ -43,7 +43,7 @@ class Combinator {
|
|
|
43
43
|
// find route paths
|
|
44
44
|
const option = {
|
|
45
45
|
name: 'router',
|
|
46
|
-
filter: ['.js'],
|
|
46
|
+
filter: ['.js', '.ts'],
|
|
47
47
|
};
|
|
48
48
|
let routerPaths = [];
|
|
49
49
|
try {
|
|
@@ -52,6 +52,8 @@ class Combinator {
|
|
|
52
52
|
catch (e) {
|
|
53
53
|
console.log(e);
|
|
54
54
|
}
|
|
55
|
+
// ignore type declarations
|
|
56
|
+
routerPaths = routerPaths.filter(routePath => !routePath.endsWith('.d.ts'));
|
|
55
57
|
// create and combine routes into the app
|
|
56
58
|
for (let i = 0; i < routerPaths.length; i++) {
|
|
57
59
|
const service = require(routerPaths[i]);
|
|
@@ -70,7 +72,7 @@ class Combinator {
|
|
|
70
72
|
let rootObject_temp;
|
|
71
73
|
const option = {
|
|
72
74
|
name: filename.name,
|
|
73
|
-
filter: [filename.extension],
|
|
75
|
+
filter: [filename.extension, '.ts'],
|
|
74
76
|
};
|
|
75
77
|
let modulesPath = [];
|
|
76
78
|
try {
|
|
@@ -79,6 +81,7 @@ class Combinator {
|
|
|
79
81
|
catch (e) {
|
|
80
82
|
console.log(e);
|
|
81
83
|
}
|
|
84
|
+
modulesPath = modulesPath.filter(modulePath => !modulePath.endsWith('.d.ts'));
|
|
82
85
|
// create and combine routes into the app
|
|
83
86
|
for (let i = 0; i < modulesPath.length; i++) {
|
|
84
87
|
const moduleObject = require(modulesPath[i]);
|
|
@@ -117,7 +120,7 @@ class Combinator {
|
|
|
117
120
|
// find route paths
|
|
118
121
|
const option = {
|
|
119
122
|
name: filename.name,
|
|
120
|
-
filter: [filename.extension],
|
|
123
|
+
filter: [filename.extension, '.ts'],
|
|
121
124
|
};
|
|
122
125
|
let functionsPaths = [];
|
|
123
126
|
try {
|
|
@@ -126,6 +129,7 @@ class Combinator {
|
|
|
126
129
|
catch (e) {
|
|
127
130
|
console.log(e);
|
|
128
131
|
}
|
|
132
|
+
functionsPaths = functionsPaths.filter(functionPath => !functionPath.endsWith('.d.ts'));
|
|
129
133
|
// create and combine routes into the app
|
|
130
134
|
for (let i = 0; i < functionsPaths.length; i++) {
|
|
131
135
|
const modularFunctions = require(functionsPaths[i]);
|
|
@@ -2,14 +2,12 @@ interface DirectorySettings {
|
|
|
2
2
|
name?: string;
|
|
3
3
|
filter?: string[];
|
|
4
4
|
}
|
|
5
|
-
type WalkCallback = (err: Error | null, results: string[]) => void;
|
|
6
5
|
/**
|
|
7
|
-
* Walk through a directory and its subdirectories
|
|
6
|
+
* Walk through a directory and its subdirectories efficiently
|
|
8
7
|
* @param dir - Directory to walk
|
|
9
8
|
* @param settings - Settings for filtering files
|
|
10
|
-
* @param done - Callback function
|
|
11
9
|
*/
|
|
12
|
-
declare function walk(dir: string, settings: DirectorySettings
|
|
10
|
+
declare function walk(dir: string, settings: DirectorySettings): Promise<string[]>;
|
|
13
11
|
/**
|
|
14
12
|
* Find files in a directory with Promise API
|
|
15
13
|
* @param dir - Directory to search
|
package/dist/class/directory.js
CHANGED
|
@@ -8,66 +8,45 @@ exports.find = find;
|
|
|
8
8
|
const fs_1 = __importDefault(require("fs"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
/**
|
|
11
|
-
* Walk through a directory and its subdirectories
|
|
11
|
+
* Walk through a directory and its subdirectories efficiently
|
|
12
12
|
* @param dir - Directory to walk
|
|
13
13
|
* @param settings - Settings for filtering files
|
|
14
|
-
* @param done - Callback function
|
|
15
14
|
*/
|
|
16
|
-
function walk(dir, settings
|
|
15
|
+
async function walk(dir, settings) {
|
|
17
16
|
let results = [];
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
17
|
+
const list = await fs_1.default.promises.readdir(dir);
|
|
18
|
+
for (const file of list) {
|
|
19
|
+
const filePath = path_1.default.join(dir, file);
|
|
20
|
+
const stat = await fs_1.default.promises.stat(filePath);
|
|
21
|
+
if (stat && stat.isDirectory()) {
|
|
22
|
+
// Ignore common directories that should not be scanned
|
|
23
|
+
if (file === 'node_modules' || file === '.git' || file === 'dist') {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const res = await walk(filePath, settings);
|
|
27
|
+
results = results.concat(res);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
const extension = path_1.default.extname(filePath);
|
|
31
|
+
const fileName = path_1.default.basename(filePath).split('.')[0];
|
|
32
|
+
let fileNameKey = true;
|
|
33
|
+
// name filter
|
|
34
|
+
if (settings.name && settings.name !== fileName) {
|
|
35
|
+
fileNameKey = false;
|
|
36
|
+
}
|
|
37
|
+
// extension filter
|
|
38
|
+
if (settings.filter && fileNameKey) {
|
|
39
|
+
if (settings.filter.some(ext => ext.toLowerCase() === extension.toLowerCase())) {
|
|
40
|
+
results.push(filePath);
|
|
34
41
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
// file filter
|
|
47
|
-
const extension = path_1.default.extname(file);
|
|
48
|
-
const fileName = path_1.default.basename(file).split('.')[0];
|
|
49
|
-
let fileNameKey = true;
|
|
50
|
-
// name filter
|
|
51
|
-
if (settings.name && settings.name === fileName)
|
|
52
|
-
fileNameKey = true;
|
|
53
|
-
else
|
|
54
|
-
fileNameKey = false;
|
|
55
|
-
// extension filter
|
|
56
|
-
if (settings.filter && fileNameKey) {
|
|
57
|
-
settings.filter.forEach(function (element) {
|
|
58
|
-
if (element.toLowerCase() === extension.toLowerCase())
|
|
59
|
-
results.push(file);
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
// push any file if no option
|
|
63
|
-
else if (fileNameKey)
|
|
64
|
-
results.push(file);
|
|
65
|
-
if (!--pending)
|
|
66
|
-
done(null, results);
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
});
|
|
42
|
+
}
|
|
43
|
+
// push any file if no name filter and no extension filter
|
|
44
|
+
else if (fileNameKey && !settings.filter) {
|
|
45
|
+
results.push(filePath);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return results;
|
|
71
50
|
}
|
|
72
51
|
/**
|
|
73
52
|
* Find files in a directory with Promise API
|
|
@@ -75,13 +54,12 @@ function walk(dir, settings, done) {
|
|
|
75
54
|
* @param settings - Settings for filtering files
|
|
76
55
|
* @returns Promise resolving to an array of file paths
|
|
77
56
|
*/
|
|
78
|
-
function find(dir, settings) {
|
|
79
|
-
|
|
80
|
-
walk(dir, settings
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
});
|
|
57
|
+
async function find(dir, settings) {
|
|
58
|
+
try {
|
|
59
|
+
return await walk(dir, settings);
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
console.error(`Error in directory.find for ${dir}:`, err);
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
87
65
|
}
|
|
@@ -65,43 +65,110 @@ const userManager = __importStar(require("../services/user_manager/service"));
|
|
|
65
65
|
*/
|
|
66
66
|
async function createAdminUser({ email, password }) {
|
|
67
67
|
const authModel = DataProvider.getCollection('cms', 'auth');
|
|
68
|
+
// Wait for connection to be ready for queries
|
|
69
|
+
const connection = authModel.db;
|
|
70
|
+
if (connection) {
|
|
71
|
+
if (connection.readyState !== 1) {
|
|
72
|
+
await new Promise((resolve, reject) => {
|
|
73
|
+
const timeout = setTimeout(() => {
|
|
74
|
+
reject(new Error('Database connection timeout - connection not ready after 10s'));
|
|
75
|
+
}, 10000);
|
|
76
|
+
if (connection.readyState === 1) {
|
|
77
|
+
clearTimeout(timeout);
|
|
78
|
+
resolve();
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
connection.once('connected', () => {
|
|
82
|
+
clearTimeout(timeout);
|
|
83
|
+
resolve();
|
|
84
|
+
});
|
|
85
|
+
connection.once('error', err => {
|
|
86
|
+
clearTimeout(timeout);
|
|
87
|
+
reject(err);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
// Test if database is actually ready by doing a simple operation
|
|
93
|
+
// This ensures the connection is fully initialized
|
|
94
|
+
let retries = 5;
|
|
95
|
+
let lastError = null;
|
|
96
|
+
while (retries > 0) {
|
|
97
|
+
try {
|
|
98
|
+
await authModel.findOne({}).limit(1).maxTimeMS(3000).lean().exec();
|
|
99
|
+
// Additional small delay to ensure write operations will work
|
|
100
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
101
|
+
break; // Success, connection is ready
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
105
|
+
retries--;
|
|
106
|
+
if (retries > 0) {
|
|
107
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (retries === 0 && lastError) {
|
|
112
|
+
throw new Error(`Database not ready for queries: ${lastError.message}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
68
115
|
try {
|
|
69
|
-
|
|
116
|
+
// Execute queries with explicit timeout handling
|
|
117
|
+
const queryOptions = { maxTimeMS: 5000 }; // 5 second timeout
|
|
118
|
+
const isAnonymousExisted = await authModel
|
|
119
|
+
.countDocuments({ type: 'anonymous' })
|
|
120
|
+
.maxTimeMS(5000)
|
|
121
|
+
.exec();
|
|
70
122
|
const isAdministratorExisted = await authModel
|
|
71
123
|
.countDocuments({ type: 'user', email: email })
|
|
124
|
+
.maxTimeMS(5000)
|
|
72
125
|
.exec();
|
|
73
126
|
if (isAnonymousExisted === 0) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
127
|
+
// Wrap registerUser with timeout to prevent hanging
|
|
128
|
+
try {
|
|
129
|
+
let timeoutId;
|
|
130
|
+
await Promise.race([
|
|
131
|
+
userManager.main.registerUser({
|
|
132
|
+
permissionGroup: (0, permissionManager_1.getDefaultAnonymousPermissionGroup)().title,
|
|
133
|
+
email: '',
|
|
134
|
+
phone: '',
|
|
135
|
+
password: '',
|
|
136
|
+
type: 'anonymous',
|
|
137
|
+
}),
|
|
138
|
+
new Promise((_, reject) => {
|
|
139
|
+
timeoutId = setTimeout(() => reject(new Error('registerUser timeout for anonymous user after 15s')), 15000);
|
|
140
|
+
}),
|
|
141
|
+
]).finally(() => {
|
|
142
|
+
if (timeoutId)
|
|
143
|
+
clearTimeout(timeoutId);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
// If anonymous user creation fails, log but don't fail completely
|
|
148
|
+
// It might already exist from a previous attempt
|
|
149
|
+
console.warn('Failed to create anonymous user:', err instanceof Error ? err.message : String(err));
|
|
150
|
+
}
|
|
88
151
|
}
|
|
89
152
|
if (isAdministratorExisted === 0) {
|
|
90
153
|
if (!email || !password) {
|
|
91
154
|
return Promise.reject('Invalid email or password for admin user.');
|
|
92
155
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
156
|
+
// Wrap registerUser with timeout to prevent hanging
|
|
157
|
+
let timeoutId;
|
|
158
|
+
await Promise.race([
|
|
159
|
+
userManager.main.registerUser({
|
|
160
|
+
permissionGroup: (0, permissionManager_1.getDefaultAdministratorPermissionGroup)().title,
|
|
161
|
+
email: email,
|
|
162
|
+
password: password,
|
|
163
|
+
type: 'user',
|
|
164
|
+
}),
|
|
165
|
+
new Promise((_, reject) => {
|
|
166
|
+
timeoutId = setTimeout(() => reject(new Error('registerUser timeout for admin user after 15s')), 15000);
|
|
167
|
+
}),
|
|
168
|
+
]).finally(() => {
|
|
169
|
+
if (timeoutId)
|
|
170
|
+
clearTimeout(timeoutId);
|
|
98
171
|
});
|
|
99
|
-
// await new authModel({
|
|
100
|
-
// permission: getDefaultAdministratorPermissionGroup().title,
|
|
101
|
-
// email: email,
|
|
102
|
-
// password: password,
|
|
103
|
-
// type: "user",
|
|
104
|
-
// }).save();
|
|
105
172
|
}
|
|
106
173
|
}
|
|
107
174
|
catch (e) {
|
package/dist/index.d.ts
CHANGED
|
@@ -40,7 +40,7 @@ export { createRest };
|
|
|
40
40
|
* ],
|
|
41
41
|
* uploadDirectoryConfig: {
|
|
42
42
|
* directory: './uploads',
|
|
43
|
-
|
|
43
|
+
* urlPath: '/assets'
|
|
44
44
|
* }
|
|
45
45
|
* };
|
|
46
46
|
* ```
|
|
@@ -118,6 +118,17 @@ export { paginator };
|
|
|
118
118
|
* @description Database collection access utilities
|
|
119
119
|
*/
|
|
120
120
|
export { getCollection };
|
|
121
|
+
/**
|
|
122
|
+
* @description Model registry for managing mongoose models and connections
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* import { modelRegistry } from '@modular-rest/server';
|
|
126
|
+
*
|
|
127
|
+
* // Get a model directly from registry
|
|
128
|
+
* const userModel = modelRegistry.getModel('myapp', 'users');
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
export { modelRegistry } from './services/data_provider/model_registry';
|
|
121
132
|
/**
|
|
122
133
|
* @description File handling utilities
|
|
123
134
|
* @example
|
package/dist/index.js
CHANGED
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.userManager = exports.middleware = exports.fileService = exports.getCollection = exports.paginator = exports.reply = exports.validator = exports.TypeCasters = exports.defineFunction = exports.AccessTypes = exports.PermissionGroup = exports.PermissionTypes = exports.Permission = exports.AccessDefinition = exports.CmsTrigger = exports.DatabaseTrigger = exports.Schema = exports.schemas = exports.defineCollection = exports.CollectionDefinition = exports.createRest = void 0;
|
|
39
|
+
exports.userManager = exports.middleware = exports.fileService = exports.modelRegistry = exports.getCollection = exports.paginator = exports.reply = exports.validator = exports.TypeCasters = exports.defineFunction = exports.AccessTypes = exports.PermissionGroup = exports.PermissionTypes = exports.Permission = exports.AccessDefinition = exports.CmsTrigger = exports.DatabaseTrigger = exports.Schema = exports.schemas = exports.defineCollection = exports.CollectionDefinition = exports.createRest = void 0;
|
|
40
40
|
// Application
|
|
41
41
|
const application_1 = require("./application");
|
|
42
42
|
Object.defineProperty(exports, "createRest", { enumerable: true, get: function () { return application_1.createRest; } });
|
|
@@ -77,3 +77,15 @@ Object.defineProperty(exports, "PermissionGroup", { enumerable: true, get: funct
|
|
|
77
77
|
Object.defineProperty(exports, "AccessTypes", { enumerable: true, get: function () { return security_1.AccessTypes; } });
|
|
78
78
|
const middleware = __importStar(require("./middlewares"));
|
|
79
79
|
exports.middleware = middleware;
|
|
80
|
+
/**
|
|
81
|
+
* @description Model registry for managing mongoose models and connections
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* import { modelRegistry } from '@modular-rest/server';
|
|
85
|
+
*
|
|
86
|
+
* // Get a model directly from registry
|
|
87
|
+
* const userModel = modelRegistry.getModel('myapp', 'users');
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
var model_registry_1 = require("./services/data_provider/model_registry");
|
|
91
|
+
Object.defineProperty(exports, "modelRegistry", { enumerable: true, get: function () { return model_registry_1.modelRegistry; } });
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Connection, Model } from 'mongoose';
|
|
2
|
+
import { CollectionDefinition } from '../../class/collection_definition';
|
|
3
|
+
import { MongoOption } from './service';
|
|
4
|
+
/**
|
|
5
|
+
* ModelRegistry - Singleton class for managing mongoose models and connections
|
|
6
|
+
* Pre-creates all models before database connections are established
|
|
7
|
+
*/
|
|
8
|
+
declare class ModelRegistry {
|
|
9
|
+
private static instance;
|
|
10
|
+
private connections;
|
|
11
|
+
private models;
|
|
12
|
+
private collectionDefinitions;
|
|
13
|
+
private mongoOption;
|
|
14
|
+
private constructor();
|
|
15
|
+
/**
|
|
16
|
+
* Get the singleton instance of ModelRegistry
|
|
17
|
+
* @returns {ModelRegistry} The singleton instance
|
|
18
|
+
*/
|
|
19
|
+
static getInstance(): ModelRegistry;
|
|
20
|
+
/**
|
|
21
|
+
* Register a collection definition and create its model
|
|
22
|
+
* @param {CollectionDefinition} definition - The collection definition
|
|
23
|
+
* @param {MongoOption} mongoOption - MongoDB connection options
|
|
24
|
+
* @returns {Model<any>} The created mongoose model
|
|
25
|
+
*/
|
|
26
|
+
registerCollection(definition: CollectionDefinition, mongoOption: MongoOption): Model<any>;
|
|
27
|
+
/**
|
|
28
|
+
* Get a model by database and collection name
|
|
29
|
+
* @param {string} database - Database name
|
|
30
|
+
* @param {string} collection - Collection name
|
|
31
|
+
* @returns {Model<any> | null} The mongoose model or null if not found
|
|
32
|
+
*/
|
|
33
|
+
getModel(database: string, collection: string): Model<any> | null;
|
|
34
|
+
/**
|
|
35
|
+
* Get a connection for a database
|
|
36
|
+
* @param {string} database - Database name
|
|
37
|
+
* @returns {Connection | null} The mongoose connection or null if not found
|
|
38
|
+
*/
|
|
39
|
+
getConnection(database: string): Connection | null;
|
|
40
|
+
/**
|
|
41
|
+
* Initialize all connections and connect to databases
|
|
42
|
+
* This should be called during server startup
|
|
43
|
+
* @param {MongoOption} mongoOption - MongoDB connection options
|
|
44
|
+
* @returns {Promise<void>} A promise that resolves when all connections are established
|
|
45
|
+
*/
|
|
46
|
+
initializeConnections(mongoOption: MongoOption): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Get all registered collection definitions
|
|
49
|
+
* @returns {CollectionDefinition[]} Array of collection definitions
|
|
50
|
+
*/
|
|
51
|
+
getCollectionDefinitions(): CollectionDefinition[];
|
|
52
|
+
/**
|
|
53
|
+
* Get all models for a specific database
|
|
54
|
+
* @param {string} database - Database name
|
|
55
|
+
* @returns {Record<string, Model<any>> | null} Object mapping collection names to models, or null if database not found
|
|
56
|
+
*/
|
|
57
|
+
getModelsForDatabase(database: string): Record<string, Model<any>> | null;
|
|
58
|
+
/**
|
|
59
|
+
* Check if a model exists
|
|
60
|
+
* @param {string} database - Database name
|
|
61
|
+
* @param {string} collection - Collection name
|
|
62
|
+
* @returns {boolean} True if model exists, false otherwise
|
|
63
|
+
*/
|
|
64
|
+
hasModel(database: string, collection: string): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Clear all internal state, including connections and models.
|
|
67
|
+
* Useful for testing when different database prefixes are used.
|
|
68
|
+
*/
|
|
69
|
+
clear(): Promise<void>;
|
|
70
|
+
}
|
|
71
|
+
export declare const modelRegistry: ModelRegistry;
|
|
72
|
+
export default modelRegistry;
|