@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
|
@@ -1,6 +1,9 @@
|
|
|
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 modelRegistry from '../services/data_provider/model_registry';
|
|
5
|
+
import { MongoOption } from '../services/data_provider/service';
|
|
6
|
+
import { config } from '../config';
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
9
|
* Configuration options for creating a collection definition.
|
|
@@ -26,6 +29,12 @@ interface CollectionDefinitionOptions {
|
|
|
26
29
|
* @see https://mongoosejs.com/docs/5.x/docs/guide.html
|
|
27
30
|
*/
|
|
28
31
|
schema: Schema<any>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Optional MongoDB connection options. If not provided, will use config.mongo if available.
|
|
35
|
+
* This is used to pre-create the model before server startup.
|
|
36
|
+
*/
|
|
37
|
+
mongoOption?: MongoOption;
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
/**
|
|
@@ -34,7 +43,7 @@ interface CollectionDefinitionOptions {
|
|
|
34
43
|
* @param {CollectionDefinitionOptions} options - The options for the collection
|
|
35
44
|
* @expandType CollectionDefinitionOptions
|
|
36
45
|
*
|
|
37
|
-
* @returns A
|
|
46
|
+
* @returns A CollectionDefinition instance with a model property that returns the mongoose model
|
|
38
47
|
*
|
|
39
48
|
* @public
|
|
40
49
|
*
|
|
@@ -51,10 +60,72 @@ interface CollectionDefinitionOptions {
|
|
|
51
60
|
* // trigger: DatabaseTrigger[]
|
|
52
61
|
* })
|
|
53
62
|
* ]
|
|
63
|
+
*
|
|
64
|
+
* // Access the model directly:
|
|
65
|
+
* const userCollection = defineCollection({...});
|
|
66
|
+
* const UserModel = userCollection.model;
|
|
67
|
+
* const users = await UserModel.find();
|
|
54
68
|
* ```
|
|
55
69
|
*/
|
|
56
|
-
export function defineCollection(
|
|
57
|
-
|
|
70
|
+
export function defineCollection(
|
|
71
|
+
options: CollectionDefinitionOptions
|
|
72
|
+
): CollectionDefinition & { model: Model<any> } {
|
|
73
|
+
const definition = new CollectionDefinition(options);
|
|
74
|
+
|
|
75
|
+
// Try to get mongoOption from options or config
|
|
76
|
+
let mongoOption: MongoOption | undefined = options.mongoOption;
|
|
77
|
+
if (!mongoOption && config.mongo) {
|
|
78
|
+
mongoOption = config.mongo as MongoOption;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// If mongoOption is available, register the collection and create the model
|
|
82
|
+
if (mongoOption) {
|
|
83
|
+
const model = modelRegistry.registerCollection(definition, mongoOption);
|
|
84
|
+
definition.setModel(model);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Create a proxy object that includes both CollectionDefinition properties and model
|
|
88
|
+
const result = definition as CollectionDefinition & { model: Model<any> };
|
|
89
|
+
|
|
90
|
+
// Add model property getter
|
|
91
|
+
Object.defineProperty(result, 'model', {
|
|
92
|
+
get() {
|
|
93
|
+
// If model was already set, return it
|
|
94
|
+
const existingModel = definition.getModel();
|
|
95
|
+
if (existingModel) {
|
|
96
|
+
return existingModel;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Otherwise, try to get from registry
|
|
100
|
+
const registryModel = modelRegistry.getModel(definition.database, definition.collection);
|
|
101
|
+
if (registryModel) {
|
|
102
|
+
definition.setModel(registryModel);
|
|
103
|
+
return registryModel;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// If still not found, try to get mongoOption from config and register now
|
|
107
|
+
let currentMongoOption: MongoOption | undefined = mongoOption;
|
|
108
|
+
if (!currentMongoOption && config.mongo) {
|
|
109
|
+
currentMongoOption = config.mongo as MongoOption;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (currentMongoOption) {
|
|
113
|
+
const newModel = modelRegistry.registerCollection(definition, currentMongoOption);
|
|
114
|
+
definition.setModel(newModel);
|
|
115
|
+
return newModel;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
throw new Error(
|
|
119
|
+
`Model for ${definition.database}.${definition.collection} is not available. ` +
|
|
120
|
+
`Ensure mongoOption is provided in defineCollection options, config.mongo is set, ` +
|
|
121
|
+
`or the collection is registered via addCollectionDefinitionByList before accessing the model.`
|
|
122
|
+
);
|
|
123
|
+
},
|
|
124
|
+
enumerable: true,
|
|
125
|
+
configurable: true,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return result;
|
|
58
129
|
}
|
|
59
130
|
|
|
60
131
|
/**
|
|
@@ -110,6 +181,9 @@ export class CollectionDefinition {
|
|
|
110
181
|
/** @readonly Optional database triggers */
|
|
111
182
|
triggers?: DatabaseTrigger[];
|
|
112
183
|
|
|
184
|
+
/** Optional mongoose model for this collection */
|
|
185
|
+
private _model: Model<any> | null = null;
|
|
186
|
+
|
|
113
187
|
/**
|
|
114
188
|
* Creates a new CollectionDefinition instance
|
|
115
189
|
*
|
|
@@ -131,4 +205,20 @@ export class CollectionDefinition {
|
|
|
131
205
|
this.permissions = permissions;
|
|
132
206
|
this.triggers = triggers;
|
|
133
207
|
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get the mongoose model for this collection
|
|
211
|
+
* @returns {Model<any> | null} The mongoose model or null if not set
|
|
212
|
+
*/
|
|
213
|
+
getModel(): Model<any> | null {
|
|
214
|
+
return this._model;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Set the mongoose model for this collection
|
|
219
|
+
* @param {Model<any>} model - The mongoose model
|
|
220
|
+
*/
|
|
221
|
+
setModel(model: Model<any>): void {
|
|
222
|
+
this._model = model;
|
|
223
|
+
}
|
|
134
224
|
}
|
package/src/class/combinator.ts
CHANGED
|
@@ -25,7 +25,7 @@ class Combinator {
|
|
|
25
25
|
// find route paths
|
|
26
26
|
const option = {
|
|
27
27
|
name: 'router',
|
|
28
|
-
filter: ['.js'],
|
|
28
|
+
filter: ['.js', '.ts'],
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
let routerPaths: string[] = [];
|
|
@@ -35,6 +35,9 @@ class Combinator {
|
|
|
35
35
|
console.log(e);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
// ignore type declarations
|
|
39
|
+
routerPaths = routerPaths.filter(routePath => !routePath.endsWith('.d.ts'));
|
|
40
|
+
|
|
38
41
|
// create and combine routes into the app
|
|
39
42
|
for (let i = 0; i < routerPaths.length; i++) {
|
|
40
43
|
const service = require(routerPaths[i]);
|
|
@@ -62,7 +65,7 @@ class Combinator {
|
|
|
62
65
|
|
|
63
66
|
const option = {
|
|
64
67
|
name: filename.name,
|
|
65
|
-
filter: [filename.extension],
|
|
68
|
+
filter: [filename.extension, '.ts'],
|
|
66
69
|
};
|
|
67
70
|
|
|
68
71
|
let modulesPath: string[] = [];
|
|
@@ -72,6 +75,8 @@ class Combinator {
|
|
|
72
75
|
console.log(e);
|
|
73
76
|
}
|
|
74
77
|
|
|
78
|
+
modulesPath = modulesPath.filter(modulePath => !modulePath.endsWith('.d.ts'));
|
|
79
|
+
|
|
75
80
|
// create and combine routes into the app
|
|
76
81
|
for (let i = 0; i < modulesPath.length; i++) {
|
|
77
82
|
const moduleObject = require(modulesPath[i]);
|
|
@@ -113,7 +118,7 @@ class Combinator {
|
|
|
113
118
|
// find route paths
|
|
114
119
|
const option = {
|
|
115
120
|
name: filename.name,
|
|
116
|
-
filter: [filename.extension],
|
|
121
|
+
filter: [filename.extension, '.ts'],
|
|
117
122
|
};
|
|
118
123
|
|
|
119
124
|
let functionsPaths: string[] = [];
|
|
@@ -123,6 +128,8 @@ class Combinator {
|
|
|
123
128
|
console.log(e);
|
|
124
129
|
}
|
|
125
130
|
|
|
131
|
+
functionsPaths = functionsPaths.filter(functionPath => !functionPath.endsWith('.d.ts'));
|
|
132
|
+
|
|
126
133
|
// create and combine routes into the app
|
|
127
134
|
for (let i = 0; i < functionsPaths.length; i++) {
|
|
128
135
|
const modularFunctions = require(functionsPaths[i]);
|
package/src/class/directory.ts
CHANGED
|
@@ -6,67 +6,49 @@ interface DirectorySettings {
|
|
|
6
6
|
filter?: string[];
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
type WalkCallback = (err: Error | null, results: string[]) => void;
|
|
10
|
-
|
|
11
9
|
/**
|
|
12
|
-
* Walk through a directory and its subdirectories
|
|
10
|
+
* Walk through a directory and its subdirectories efficiently
|
|
13
11
|
* @param dir - Directory to walk
|
|
14
12
|
* @param settings - Settings for filtering files
|
|
15
|
-
* @param done - Callback function
|
|
16
13
|
*/
|
|
17
|
-
function walk(dir: string, settings: DirectorySettings
|
|
14
|
+
async function walk(dir: string, settings: DirectorySettings): Promise<string[]> {
|
|
18
15
|
let results: string[] = [];
|
|
16
|
+
const list = await fs.promises.readdir(dir);
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
let pending = list.length;
|
|
25
|
-
if (!pending) return done(null, results);
|
|
26
|
-
|
|
27
|
-
list.forEach(function (file) {
|
|
28
|
-
file = path.join(dir, file);
|
|
29
|
-
fs.stat(file, function (err, stat) {
|
|
30
|
-
if (err) {
|
|
31
|
-
// Handle file stat error but continue with other files
|
|
32
|
-
console.error(`Error reading file stats for ${file}:`, err);
|
|
33
|
-
if (!--pending) done(null, results);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// If directory, execute a recursive call
|
|
38
|
-
if (stat && stat.isDirectory()) {
|
|
39
|
-
// Add directory to array [comment if you need to remove the directories from the array]
|
|
40
|
-
// results.push(file);
|
|
41
|
-
walk(file, settings, function (err, res) {
|
|
42
|
-
results = results.concat(res);
|
|
43
|
-
if (!--pending) done(null, results);
|
|
44
|
-
});
|
|
45
|
-
} else {
|
|
46
|
-
// file filter
|
|
47
|
-
const extension = path.extname(file);
|
|
48
|
-
const fileName = path.basename(file).split('.')[0];
|
|
49
|
-
let fileNameKey = true;
|
|
50
|
-
|
|
51
|
-
// name filter
|
|
52
|
-
if (settings.name && settings.name === fileName) fileNameKey = true;
|
|
53
|
-
else fileNameKey = false;
|
|
18
|
+
for (const file of list) {
|
|
19
|
+
const filePath = path.join(dir, file);
|
|
20
|
+
const stat = await fs.promises.stat(filePath);
|
|
54
21
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
22
|
+
if (stat && stat.isDirectory()) {
|
|
23
|
+
// Ignore common directories that should not be scanned
|
|
24
|
+
if (file === 'node_modules' || file === '.git' || file === 'dist') {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const res = await walk(filePath, settings);
|
|
28
|
+
results = results.concat(res);
|
|
29
|
+
} else {
|
|
30
|
+
const extension = path.extname(filePath);
|
|
31
|
+
const fileName = path.basename(filePath).split('.')[0];
|
|
32
|
+
let fileNameKey = true;
|
|
61
33
|
|
|
62
|
-
|
|
63
|
-
|
|
34
|
+
// name filter
|
|
35
|
+
if (settings.name && settings.name !== fileName) {
|
|
36
|
+
fileNameKey = false;
|
|
37
|
+
}
|
|
64
38
|
|
|
65
|
-
|
|
39
|
+
// extension filter
|
|
40
|
+
if (settings.filter && fileNameKey) {
|
|
41
|
+
if (settings.filter.some(ext => ext.toLowerCase() === extension.toLowerCase())) {
|
|
42
|
+
results.push(filePath);
|
|
66
43
|
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
44
|
+
}
|
|
45
|
+
// push any file if no name filter and no extension filter
|
|
46
|
+
else if (fileNameKey && !settings.filter) {
|
|
47
|
+
results.push(filePath);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return results;
|
|
70
52
|
}
|
|
71
53
|
|
|
72
54
|
/**
|
|
@@ -75,13 +57,13 @@ function walk(dir: string, settings: DirectorySettings, done: WalkCallback): voi
|
|
|
75
57
|
* @param settings - Settings for filtering files
|
|
76
58
|
* @returns Promise resolving to an array of file paths
|
|
77
59
|
*/
|
|
78
|
-
function find(dir: string, settings: DirectorySettings): Promise<string[]> {
|
|
79
|
-
|
|
80
|
-
walk(dir, settings
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
60
|
+
async function find(dir: string, settings: DirectorySettings): Promise<string[]> {
|
|
61
|
+
try {
|
|
62
|
+
return await walk(dir, settings);
|
|
63
|
+
} catch (err) {
|
|
64
|
+
console.error(`Error in directory.find for ${dir}:`, err);
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
85
67
|
}
|
|
86
68
|
|
|
87
69
|
export { walk, find };
|
|
@@ -45,28 +45,97 @@ interface AdminCredentials {
|
|
|
45
45
|
export async function createAdminUser({ email, password }: AdminCredentials): Promise<void> {
|
|
46
46
|
const authModel = DataProvider.getCollection('cms', 'auth');
|
|
47
47
|
|
|
48
|
+
// Wait for connection to be ready for queries
|
|
49
|
+
const connection = authModel.db;
|
|
50
|
+
if (connection) {
|
|
51
|
+
if (connection.readyState !== 1) {
|
|
52
|
+
await new Promise<void>((resolve, reject) => {
|
|
53
|
+
const timeout = setTimeout(() => {
|
|
54
|
+
reject(new Error('Database connection timeout - connection not ready after 10s'));
|
|
55
|
+
}, 10000);
|
|
56
|
+
|
|
57
|
+
if (connection.readyState === 1) {
|
|
58
|
+
clearTimeout(timeout);
|
|
59
|
+
resolve();
|
|
60
|
+
} else {
|
|
61
|
+
connection.once('connected', () => {
|
|
62
|
+
clearTimeout(timeout);
|
|
63
|
+
resolve();
|
|
64
|
+
});
|
|
65
|
+
connection.once('error', err => {
|
|
66
|
+
clearTimeout(timeout);
|
|
67
|
+
reject(err);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Test if database is actually ready by doing a simple operation
|
|
74
|
+
// This ensures the connection is fully initialized
|
|
75
|
+
let retries = 5;
|
|
76
|
+
let lastError: Error | null = null;
|
|
77
|
+
while (retries > 0) {
|
|
78
|
+
try {
|
|
79
|
+
await authModel.findOne({}).limit(1).maxTimeMS(3000).lean().exec();
|
|
80
|
+
// Additional small delay to ensure write operations will work
|
|
81
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
82
|
+
break; // Success, connection is ready
|
|
83
|
+
} catch (err) {
|
|
84
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
85
|
+
retries--;
|
|
86
|
+
if (retries > 0) {
|
|
87
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (retries === 0 && lastError) {
|
|
92
|
+
throw new Error(`Database not ready for queries: ${lastError.message}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
48
96
|
try {
|
|
49
|
-
|
|
97
|
+
// Execute queries with explicit timeout handling
|
|
98
|
+
const queryOptions = { maxTimeMS: 5000 }; // 5 second timeout
|
|
99
|
+
|
|
100
|
+
const isAnonymousExisted = await authModel
|
|
101
|
+
.countDocuments({ type: 'anonymous' })
|
|
102
|
+
.maxTimeMS(5000)
|
|
103
|
+
.exec();
|
|
50
104
|
|
|
51
105
|
const isAdministratorExisted = await authModel
|
|
52
106
|
.countDocuments({ type: 'user', email: email })
|
|
107
|
+
.maxTimeMS(5000)
|
|
53
108
|
.exec();
|
|
54
109
|
|
|
55
110
|
if (isAnonymousExisted === 0) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
111
|
+
// Wrap registerUser with timeout to prevent hanging
|
|
112
|
+
try {
|
|
113
|
+
let timeoutId: NodeJS.Timeout;
|
|
114
|
+
await Promise.race([
|
|
115
|
+
userManager.main.registerUser({
|
|
116
|
+
permissionGroup: getDefaultAnonymousPermissionGroup().title,
|
|
117
|
+
email: '',
|
|
118
|
+
phone: '',
|
|
119
|
+
password: '',
|
|
120
|
+
type: 'anonymous',
|
|
121
|
+
}),
|
|
122
|
+
new Promise<never>((_, reject) => {
|
|
123
|
+
timeoutId = setTimeout(
|
|
124
|
+
() => reject(new Error('registerUser timeout for anonymous user after 15s')),
|
|
125
|
+
15000
|
|
126
|
+
);
|
|
127
|
+
}),
|
|
128
|
+
]).finally(() => {
|
|
129
|
+
if (timeoutId!) clearTimeout(timeoutId);
|
|
130
|
+
});
|
|
131
|
+
} catch (err) {
|
|
132
|
+
// If anonymous user creation fails, log but don't fail completely
|
|
133
|
+
// It might already exist from a previous attempt
|
|
134
|
+
console.warn(
|
|
135
|
+
'Failed to create anonymous user:',
|
|
136
|
+
err instanceof Error ? err.message : String(err)
|
|
137
|
+
);
|
|
138
|
+
}
|
|
70
139
|
}
|
|
71
140
|
|
|
72
141
|
if (isAdministratorExisted === 0) {
|
|
@@ -74,19 +143,24 @@ export async function createAdminUser({ email, password }: AdminCredentials): Pr
|
|
|
74
143
|
return Promise.reject('Invalid email or password for admin user.');
|
|
75
144
|
}
|
|
76
145
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
146
|
+
// Wrap registerUser with timeout to prevent hanging
|
|
147
|
+
let timeoutId: NodeJS.Timeout;
|
|
148
|
+
await Promise.race([
|
|
149
|
+
userManager.main.registerUser({
|
|
150
|
+
permissionGroup: getDefaultAdministratorPermissionGroup().title,
|
|
151
|
+
email: email,
|
|
152
|
+
password: password,
|
|
153
|
+
type: 'user',
|
|
154
|
+
}),
|
|
155
|
+
new Promise<never>((_, reject) => {
|
|
156
|
+
timeoutId = setTimeout(
|
|
157
|
+
() => reject(new Error('registerUser timeout for admin user after 15s')),
|
|
158
|
+
15000
|
|
159
|
+
);
|
|
160
|
+
}),
|
|
161
|
+
]).finally(() => {
|
|
162
|
+
if (timeoutId!) clearTimeout(timeoutId);
|
|
82
163
|
});
|
|
83
|
-
|
|
84
|
-
// await new authModel({
|
|
85
|
-
// permission: getDefaultAdministratorPermissionGroup().title,
|
|
86
|
-
// email: email,
|
|
87
|
-
// password: password,
|
|
88
|
-
// type: "user",
|
|
89
|
-
// }).save();
|
|
90
164
|
}
|
|
91
165
|
} catch (e) {
|
|
92
166
|
return Promise.reject(e);
|
package/src/index.ts
CHANGED
|
@@ -56,7 +56,7 @@ export { createRest };
|
|
|
56
56
|
* ],
|
|
57
57
|
* uploadDirectoryConfig: {
|
|
58
58
|
* directory: './uploads',
|
|
59
|
-
|
|
59
|
+
* urlPath: '/assets'
|
|
60
60
|
* }
|
|
61
61
|
* };
|
|
62
62
|
* ```
|
|
@@ -147,6 +147,18 @@ export { paginator };
|
|
|
147
147
|
*/
|
|
148
148
|
export { getCollection };
|
|
149
149
|
|
|
150
|
+
/**
|
|
151
|
+
* @description Model registry for managing mongoose models and connections
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* import { modelRegistry } from '@modular-rest/server';
|
|
155
|
+
*
|
|
156
|
+
* // Get a model directly from registry
|
|
157
|
+
* const userModel = modelRegistry.getModel('myapp', 'users');
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
export { modelRegistry } from './services/data_provider/model_registry';
|
|
161
|
+
|
|
150
162
|
/**
|
|
151
163
|
* @description File handling utilities
|
|
152
164
|
* @example
|