@modular-rest/server 1.11.13 → 1.11.14

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.
Files changed (112) hide show
  1. package/.nvmrc +1 -0
  2. package/.prettierrc.json +9 -0
  3. package/.releaserc.json +24 -0
  4. package/README.md +79 -94
  5. package/dist/index.js +79 -0
  6. package/docs/.keep +0 -0
  7. package/docs/system-access-type.md +26 -0
  8. package/package.json +58 -45
  9. package/src/application.ts +206 -0
  10. package/src/class/cms_trigger.ts +68 -0
  11. package/src/class/collection_definition.ts +134 -0
  12. package/src/class/combinator.ts +176 -0
  13. package/src/class/database_trigger.ts +99 -0
  14. package/src/class/db_schemas.ts +44 -0
  15. package/src/class/{directory.js → directory.ts} +40 -18
  16. package/src/class/paginator.ts +51 -0
  17. package/src/class/reply.ts +59 -0
  18. package/src/class/security.ts +250 -0
  19. package/src/class/trigger_operator.ts +142 -0
  20. package/src/class/user.ts +199 -0
  21. package/src/class/validator.ts +123 -0
  22. package/src/config.ts +122 -0
  23. package/src/defult-permissions.ts +31 -0
  24. package/src/events.ts +59 -0
  25. package/src/helper/data_insertion.ts +94 -0
  26. package/src/helper/presetup_services.ts +96 -0
  27. package/src/index.ts +146 -0
  28. package/src/middlewares.ts +75 -0
  29. package/src/play-test.ts +8 -0
  30. package/src/services/data_provider/router.ts +191 -0
  31. package/src/services/data_provider/service.ts +305 -0
  32. package/src/services/data_provider/typeCasters.ts +15 -0
  33. package/src/services/file/db.ts +29 -0
  34. package/src/services/file/router.ts +88 -0
  35. package/src/services/file/service.ts +387 -0
  36. package/src/services/functions/router.ts +34 -0
  37. package/src/services/functions/service.ts +203 -0
  38. package/src/services/jwt/router.ts +73 -0
  39. package/src/services/jwt/service.ts +139 -0
  40. package/src/services/user_manager/db.ts +87 -0
  41. package/src/services/user_manager/permissionManager.ts +49 -0
  42. package/src/services/user_manager/router.ts +193 -0
  43. package/src/services/user_manager/service.ts +698 -0
  44. package/tsconfig.json +16 -9
  45. package/typedoc.mjs +41 -0
  46. package/LICENSE +0 -21
  47. package/package-lock.json +0 -1373
  48. package/src/application.js +0 -239
  49. package/src/class/cms_trigger.js +0 -20
  50. package/src/class/collection_definition.js +0 -33
  51. package/src/class/combinator.js +0 -133
  52. package/src/class/database_trigger.js +0 -20
  53. package/src/class/db_schemas.js +0 -18
  54. package/src/class/paginator.js +0 -31
  55. package/src/class/reply.js +0 -37
  56. package/src/class/security.js +0 -141
  57. package/src/class/trigger_operator.js +0 -39
  58. package/src/class/user.js +0 -112
  59. package/src/class/validator.js +0 -91
  60. package/src/config.js +0 -67
  61. package/src/events.js +0 -15
  62. package/src/helper/data_insertion.js +0 -64
  63. package/src/helper/presetup_services.js +0 -31
  64. package/src/index.js +0 -66
  65. package/src/middlewares.js +0 -44
  66. package/src/services/data_provider/router.js +0 -552
  67. package/src/services/data_provider/service.js +0 -262
  68. package/src/services/data_provider/typeCasters.js +0 -10
  69. package/src/services/file/db.js +0 -29
  70. package/src/services/file/router.js +0 -92
  71. package/src/services/file/service.js +0 -231
  72. package/src/services/functions/router.js +0 -37
  73. package/src/services/functions/service.js +0 -74
  74. package/src/services/jwt/router.js +0 -82
  75. package/src/services/jwt/service.js +0 -37
  76. package/src/services/user_manager/db.js +0 -83
  77. package/src/services/user_manager/permissionManager.js +0 -43
  78. package/src/services/user_manager/router.js +0 -176
  79. package/src/services/user_manager/service.js +0 -377
  80. package/types/application.d.ts +0 -97
  81. package/types/class/cms_trigger.d.ts +0 -24
  82. package/types/class/collection_definition.d.ts +0 -36
  83. package/types/class/combinator.d.ts +0 -30
  84. package/types/class/database_trigger.d.ts +0 -28
  85. package/types/class/db_schemas.d.ts +0 -2
  86. package/types/class/directory.d.ts +0 -2
  87. package/types/class/paginator.d.ts +0 -8
  88. package/types/class/reply.d.ts +0 -8
  89. package/types/class/security.d.ts +0 -109
  90. package/types/class/trigger_operator.d.ts +0 -19
  91. package/types/class/user.d.ts +0 -24
  92. package/types/class/validator.d.ts +0 -9
  93. package/types/config.d.ts +0 -101
  94. package/types/events.d.ts +0 -7
  95. package/types/helper/data_insertion.d.ts +0 -4
  96. package/types/helper/presetup_services.d.ts +0 -5
  97. package/types/index.d.ts +0 -72
  98. package/types/middlewares.d.ts +0 -10
  99. package/types/services/data_provider/router.d.ts +0 -3
  100. package/types/services/data_provider/service.d.ts +0 -40
  101. package/types/services/data_provider/typeCasters.d.ts +0 -3
  102. package/types/services/file/db.d.ts +0 -3
  103. package/types/services/file/router.d.ts +0 -3
  104. package/types/services/file/service.d.ts +0 -81
  105. package/types/services/functions/router.d.ts +0 -3
  106. package/types/services/functions/service.d.ts +0 -23
  107. package/types/services/jwt/router.d.ts +0 -3
  108. package/types/services/jwt/service.d.ts +0 -10
  109. package/types/services/user_manager/db.d.ts +0 -3
  110. package/types/services/user_manager/permissionManager.d.ts +0 -3
  111. package/types/services/user_manager/router.d.ts +0 -3
  112. package/types/services/user_manager/service.d.ts +0 -131
@@ -0,0 +1,75 @@
1
+ import { Context, Next } from 'koa';
2
+ import { validator as validateObject } from './class/validator';
3
+ import * as userManager from './services/user_manager/service';
4
+ import User from './class/user';
5
+
6
+ /**
7
+ * Authentication middleware that secures routes by validating user tokens and managing access control.
8
+ *
9
+ * This middleware performs several key functions:
10
+ * 1. Validates that the incoming request contains an authorization token in the header
11
+ * 2. Verifies the token is valid by checking against the user management service
12
+ * 3. Retrieves the associated user object if the token is valid
13
+ * 4. Attaches the authenticated {@link User} object on ctx.state.user for use in subsequent middleware/routes
14
+ * 5. Throws appropriate HTTP errors (401, 412) if authentication fails
15
+ *
16
+ * The middleware integrates with the permission system to enable role-based access control.
17
+ * The attached user object provides methods like hasPermission() to check specific permissions.
18
+ *
19
+ * Common usage patterns:
20
+ * - Protecting sensitive API endpoints
21
+ * - Implementing role-based access control
22
+ * - Getting the current authenticated user
23
+ * - Validating user permissions before allowing actions
24
+ *
25
+ * @throws {Error} 401 - If no authorization header is present
26
+ * @throws {Error} 412 - If token validation fails
27
+ * @param ctx - Koa Context object containing request/response data
28
+ * @param next - Function to invoke next middleware
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * // Inside the router.ts file
33
+ * import { auth } from '@modular-rest/server';
34
+ * import { Router } from 'koa-router';
35
+ *
36
+ * const name = 'flowers';
37
+ *
38
+ * const flowerRouter = new Router();
39
+ *
40
+ * flowerRouter.get('/list', auth, (ctx) => {
41
+ * // Get the authenticated user
42
+ * const user = ctx.state.user;
43
+ *
44
+ * // Then you can check the user's role and permission
45
+ * if(user.hasPermission('get_flower')) {
46
+ * ctx.body = 'This is a list of flowers: Rose, Lily, Tulip';
47
+ * } else {
48
+ * ctx.status = 403;
49
+ * ctx.body = 'You are not authorized to access this resource';
50
+ * }
51
+ * });
52
+ *
53
+ * module.exports.name = name;
54
+ * module.exports.main = flowerRouter;
55
+ * ```
56
+ */
57
+ export async function auth(ctx: Context, next: Next): Promise<void> {
58
+ const headers = ctx.header;
59
+ const headersValidated = validateObject(headers, 'authorization');
60
+
61
+ if (!headersValidated.isValid) ctx.throw(401, 'authentication is required');
62
+
63
+ const token = headers.authorization as string;
64
+
65
+ await userManager.main
66
+ .getUserByToken(token)
67
+ .then(async (user: User) => {
68
+ ctx.state.user = user;
69
+ await next();
70
+ })
71
+ .catch(err => {
72
+ console.log(err);
73
+ ctx.throw(err.status || 412, err.message);
74
+ });
75
+ }
@@ -0,0 +1,8 @@
1
+ import { createRest } from './application';
2
+
3
+ const app = createRest({
4
+ adminUser: {
5
+ email: 'admin@example.com',
6
+ password: 'password',
7
+ },
8
+ });
@@ -0,0 +1,191 @@
1
+ import { AccessTypes } from '../../class/security';
2
+ import Router from 'koa-router';
3
+ import { validateObject } from '../../class/validator';
4
+ import { create as reply } from '../../class/reply';
5
+ import nestedProperty from 'nested-property';
6
+ import * as service from './service';
7
+ import * as middleware from '../../middlewares';
8
+ import { Context, Next } from 'koa';
9
+
10
+ const name = 'data-provider';
11
+
12
+ const dataProvider = new Router();
13
+
14
+ dataProvider.use('/', middleware.auth, async (ctx: Context, next: Next) => {
15
+ const body = ctx.request.body;
16
+ const bodyValidated = validateObject(body, 'database collection');
17
+
18
+ // fields validation
19
+ if (!bodyValidated.isValid) {
20
+ ctx.throw(412, JSON.stringify(reply('e', { error: bodyValidated.requires })));
21
+ }
22
+
23
+ // type caster
24
+ if (body.types && body.hasOwnProperty(body.bodyKey || '.')) {
25
+ const bodyKey = body.bodyKey;
26
+ for (const key in body.types) {
27
+ if (body.types.hasOwnProperty(key) && typeof body.types[key] == 'object') {
28
+ const typeDetail = body.types[key];
29
+
30
+ try {
31
+ const value = nestedProperty.get(body[bodyKey], typeDetail.path);
32
+ const newProperty =
33
+ service.TypeCasters[typeDetail.type as keyof typeof service.TypeCasters](value);
34
+ nestedProperty.set(body[bodyKey], typeDetail.path, newProperty);
35
+ console.log('newProperty', newProperty, JSON.stringify(body[bodyKey]));
36
+ } catch (e) {
37
+ console.log('type caster error', e);
38
+ }
39
+ }
40
+ }
41
+ }
42
+
43
+ await next();
44
+ });
45
+
46
+ dataProvider.post('/find', async (ctx: Context) => {
47
+ const body = ctx.request.body;
48
+ const bodyValidate = validateObject(body, 'database collection query');
49
+
50
+ // fields validation
51
+ if (!bodyValidate.isValid) {
52
+ ctx.throw(412, JSON.stringify(reply('e', { error: bodyValidate.requires })));
53
+ }
54
+
55
+ // access validation
56
+ const hasAccess = service.checkAccess(
57
+ body.database,
58
+ body.collection,
59
+ AccessTypes.read,
60
+ body.query,
61
+ ctx.state.user
62
+ );
63
+ if (!hasAccess) {
64
+ console.log(body);
65
+ console.log(ctx.state.user.permission);
66
+ ctx.throw(403, 'access denied');
67
+ }
68
+
69
+ // collection validation
70
+ const collection = service.getCollection(body.database, body.collection);
71
+ if (collection == null) {
72
+ ctx.throw(412, JSON.stringify(reply('e', { error: 'wrong database or collection' })));
73
+ }
74
+
75
+ // operate on db
76
+ let queryRequest = collection.find(body.query, body.projection);
77
+
78
+ if (body.options) {
79
+ queryRequest = service.performAdditionalOptionsToQueryObject(queryRequest, body.options);
80
+ }
81
+
82
+ if (body.populates) {
83
+ try {
84
+ queryRequest = service.performPopulateToQueryObject(queryRequest, body.populates);
85
+ } catch (err) {
86
+ ctx.status = 412;
87
+ ctx.body = err;
88
+ }
89
+ }
90
+
91
+ await queryRequest
92
+ .exec()
93
+ .then(async docs => {
94
+ ctx.body = { data: docs };
95
+ })
96
+ .catch(err => {
97
+ ctx.status = err.status || 500;
98
+ ctx.body = err.message;
99
+ });
100
+ });
101
+
102
+ dataProvider.post('/find-one', async (ctx: Context) => {
103
+ const body = ctx.request.body;
104
+ const bodyValidate = validateObject(body, 'database collection query');
105
+
106
+ // fields validation
107
+ if (!bodyValidate.isValid) {
108
+ ctx.throw(412, JSON.stringify(reply('e', { error: bodyValidate.requires })));
109
+ }
110
+
111
+ // access validation
112
+ const hasAccess = service.checkAccess(
113
+ body.database,
114
+ body.collection,
115
+ AccessTypes.read,
116
+ body.query,
117
+ ctx.state.user
118
+ );
119
+ if (!hasAccess) ctx.throw(403, 'access denied');
120
+
121
+ // collection validation
122
+ const collection = service.getCollection(body.database, body.collection);
123
+ if (collection == null) {
124
+ ctx.throw(412, JSON.stringify(reply('e', { error: 'wrong database or collection' })));
125
+ }
126
+
127
+ // operate on db
128
+ let queryRequest = collection.findOne(body.query, body.projection, body.options);
129
+
130
+ if (body.options) {
131
+ queryRequest = service.performAdditionalOptionsToQueryObject(queryRequest, body.options);
132
+ }
133
+
134
+ if (body.populates) {
135
+ try {
136
+ queryRequest = service.performPopulateToQueryObject(queryRequest, body.populates);
137
+ } catch (err) {
138
+ ctx.status = 412;
139
+ ctx.body = err;
140
+ }
141
+ }
142
+
143
+ // operate on db
144
+ await queryRequest
145
+ .exec()
146
+ .then(async doc => {
147
+ ctx.body = { data: doc };
148
+ })
149
+ .catch(err => {
150
+ ctx.status = err.status || 500;
151
+ ctx.body = err.message;
152
+ });
153
+ });
154
+
155
+ dataProvider.post('/count', async (ctx: Context) => {
156
+ const body = ctx.request.body;
157
+ const bodyValidate = validateObject(body, 'database collection query');
158
+
159
+ // fields validation
160
+ if (!bodyValidate.isValid) {
161
+ ctx.throw(412, JSON.stringify(reply('e', { error: bodyValidate.requires })));
162
+ }
163
+
164
+ // access validation
165
+ const hasAccess = service.checkAccess(
166
+ body.database,
167
+ body.collection,
168
+ AccessTypes.read,
169
+ body.query,
170
+ ctx.state.user
171
+ );
172
+ if (!hasAccess) ctx.throw(403, 'access denied');
173
+
174
+ // collection validation
175
+ const collection = service.getCollection(body.database, body.collection);
176
+ if (collection == null) {
177
+ ctx.throw(412, JSON.stringify(reply('e', { error: 'wrong database or collection' })));
178
+ }
179
+
180
+ await collection
181
+ .countDocuments(body.query)
182
+ .then(count => {
183
+ ctx.body = { data: count };
184
+ })
185
+ .catch(err => {
186
+ ctx.status = err.status || 500;
187
+ ctx.body = err.message;
188
+ });
189
+ });
190
+
191
+ export { name, dataProvider as main };
@@ -0,0 +1,305 @@
1
+ import mongoose, { Connection, Model, PopulateOptions, Query } from 'mongoose';
2
+ import { AccessTypes, AccessDefinition, Permission } from '../../class/security';
3
+ import triggerOperator from '../../class/trigger_operator';
4
+ import TypeCasters from './typeCasters';
5
+ import { config } from '../../config';
6
+ import { CollectionDefinition } from '../../class/collection_definition';
7
+ import { User } from '../../class/user';
8
+
9
+ /**
10
+ * Service name constant
11
+ * @constant {string}
12
+ */
13
+ export const name = 'dataProvider';
14
+
15
+ // Set mongoose options
16
+ mongoose.set('useCreateIndex', true);
17
+
18
+ // Database connections and collections storage
19
+ const connections: Record<string, Connection> = {};
20
+ const collections: Record<string, Record<string, Model<any>>> = {};
21
+ const permissionDefinitions: Record<string, Record<string, AccessDefinition>> = {};
22
+
23
+ /**
24
+ * MongoDB connection options
25
+ * @interface MongoOption
26
+ * @property {string} [dbPrefix] - Prefix for database names
27
+ * @property {string} mongoBaseAddress - MongoDB connection URL
28
+ */
29
+ export interface MongoOption {
30
+ dbPrefix?: string;
31
+ mongoBaseAddress: string;
32
+ }
33
+
34
+ /**
35
+ * Collection definition options
36
+ * @interface CollectionDefinitionOption
37
+ * @property {CollectionDefinition[]} list - List of collection definitions
38
+ * @property {MongoOption} mongoOption - MongoDB connection options
39
+ */
40
+ interface CollectionDefinitionListOption {
41
+ list: CollectionDefinition[];
42
+ mongoOption: MongoOption;
43
+ }
44
+
45
+ /**
46
+ * Connects to a database and sets up collections based on collection definitions
47
+ * @function connectToDatabaseByCollectionDefinitionList
48
+ * @param {string} dbName - Name of the database to connect to
49
+ * @param {CollectionDefinition[]} [collectionDefinitionList=[]] - List of collection definitions
50
+ * @param {MongoOption} mongoOption - MongoDB connection options
51
+ * @returns {Promise<void>} A promise that resolves when the connection is established
52
+ * @throws {Error} If triggers are not properly configured
53
+ * @private
54
+ */
55
+ function connectToDatabaseByCollectionDefinitionList(
56
+ dbName: string,
57
+ collectionDefinitionList: CollectionDefinition[] = [],
58
+ mongoOption: MongoOption
59
+ ): Promise<void> {
60
+ return new Promise((done, reject) => {
61
+ // Create db connection
62
+ const fullDbName = (mongoOption.dbPrefix || '') + dbName;
63
+ const connectionString = mongoOption.mongoBaseAddress;
64
+
65
+ console.info(`- Connecting to database: ${fullDbName}`);
66
+
67
+ const connection = mongoose.createConnection(connectionString, {
68
+ useUnifiedTopology: true,
69
+ useNewUrlParser: true,
70
+ dbName: fullDbName,
71
+ });
72
+
73
+ // Store connection
74
+ connections[dbName] = connection;
75
+
76
+ // add db models from schemas
77
+ collectionDefinitionList.forEach(collectionDefinition => {
78
+ const collection = collectionDefinition.collection;
79
+ const schema = collectionDefinition.schema;
80
+
81
+ if (collections[dbName] == undefined) collections[dbName] = {};
82
+
83
+ if (permissionDefinitions[dbName] == undefined) permissionDefinitions[dbName] = {};
84
+
85
+ // create model from schema
86
+ // and store in on global collection object
87
+ const model = connection.model(collection, schema);
88
+ collections[dbName][collection] = model;
89
+
90
+ // define Access Definition from component permissions
91
+ // and store it on global access definition object
92
+ permissionDefinitions[dbName][collection] = new AccessDefinition({
93
+ database: dbName,
94
+ collection: collection,
95
+ permissionList: collectionDefinition.permissions,
96
+ });
97
+
98
+ // add trigger
99
+ if (collectionDefinition.triggers != undefined) {
100
+ if (!Array.isArray(collectionDefinition.triggers)) {
101
+ throw new Error('Triggers must be an array');
102
+ }
103
+
104
+ collectionDefinition.triggers.forEach(trigger => {
105
+ triggerOperator.addTrigger({
106
+ ...trigger,
107
+ database: collectionDefinition.database,
108
+ collection: collectionDefinition.collection,
109
+ });
110
+ });
111
+ }
112
+ });
113
+
114
+ connection.on('connected', () => {
115
+ console.info(`- ${fullDbName} database has been connected`);
116
+ done();
117
+ });
118
+ });
119
+ }
120
+
121
+ /**
122
+ * Adds collection definitions and connects to their respective databases
123
+ * @function addCollectionDefinitionByList
124
+ * @param {CollectionDefinitionListOption} options - Collection definition options
125
+ * @returns {Promise<void>} A promise that resolves when all collections are set up
126
+ * @example
127
+ * ```typescript
128
+ * await addCollectionDefinitionByList({
129
+ * list: [
130
+ * new CollectionDefinition({
131
+ * database: 'myapp',
132
+ * collection: 'users',
133
+ * schema: userSchema,
134
+ * permissions: [new Permission({ type: 'user_access', read: true })]
135
+ * })
136
+ * ],
137
+ * mongoOption: {
138
+ * mongoBaseAddress: 'mongodb://localhost:27017',
139
+ * dbPrefix: 'myapp_'
140
+ * }
141
+ * });
142
+ * ```
143
+ */
144
+ export async function addCollectionDefinitionByList({
145
+ list,
146
+ mongoOption,
147
+ }: CollectionDefinitionListOption): Promise<void> {
148
+ // Group collection definitions by database
149
+ const dbGroups: Record<string, CollectionDefinition[]> = {};
150
+ list.forEach(collectionDefinition => {
151
+ if (!dbGroups[collectionDefinition.database]) {
152
+ dbGroups[collectionDefinition.database] = [];
153
+ }
154
+ dbGroups[collectionDefinition.database].push(collectionDefinition);
155
+ });
156
+
157
+ // Connect to each database
158
+ const connectionPromises = Object.entries(dbGroups).map(([dbName, collectionDefinitionList]) =>
159
+ connectToDatabaseByCollectionDefinitionList(dbName, collectionDefinitionList, mongoOption)
160
+ );
161
+
162
+ await Promise.all(connectionPromises);
163
+ }
164
+
165
+ /**
166
+ * Gets a Mongoose model for a specific collection
167
+ * @function getCollection
168
+ * @param {string} db - Database name
169
+ * @param {string} collection - Collection name
170
+ * @returns {Model<T>} Mongoose model for the collection
171
+ * @throws {Error} If the collection doesn't exist
172
+ * @example
173
+ * ```typescript
174
+ * const userModel = getCollection('myapp', 'users');
175
+ * const users = await userModel.find();
176
+ * ```
177
+ */
178
+ export function getCollection<T>(db: string, collection: string): Model<T> {
179
+ if (!collections[db] || !collections[db][collection]) {
180
+ throw new Error(`Collection ${collection} not found in database ${db}`);
181
+ }
182
+ return collections[db][collection];
183
+ }
184
+
185
+ /**
186
+ * Gets the permission list for a specific operation on a collection
187
+ * @function _getPermissionList
188
+ * @param {string} db - Database name
189
+ * @param {string} collection - Collection name
190
+ * @param {string} operationType - Type of operation (read/write)
191
+ * @returns {any[]} List of permissions
192
+ * @private
193
+ */
194
+ function _getPermissionList(db: string, collection: string, operationType: string): Permission[] {
195
+ if (!permissionDefinitions[db] || !permissionDefinitions[db][collection]) {
196
+ return [];
197
+ }
198
+ return permissionDefinitions[db][collection].permissionList;
199
+ }
200
+
201
+ /**
202
+ * Checks if a user has access to perform an operation on a collection
203
+ * @function checkAccess
204
+ * @param {string} db - Database name
205
+ * @param {string} collection - Collection name
206
+ * @param {string} operationType - Type of operation (read/write)
207
+ * @param {Record<string, any>} queryOrDoc - Query or document being accessed
208
+ * @param {User} user - User performing the operation
209
+ * @returns {boolean} Whether the user has access
210
+ * @example
211
+ * ```typescript
212
+ * const hasAccess = checkAccess('myapp', 'users', 'read', {}, currentUser);
213
+ * if (hasAccess) {
214
+ * const users = await getCollection('myapp', 'users').find();
215
+ * }
216
+ * ```
217
+ */
218
+ export function checkAccess(
219
+ db: string,
220
+ collection: string,
221
+ operationType: string,
222
+ queryOrDoc: Record<string, any>,
223
+ user: User
224
+ ): boolean {
225
+ const permissionList = _getPermissionList(db, collection, operationType);
226
+ return permissionList.some(permission => {
227
+ if (permission.accessType === 'god_access') return true;
228
+ if (permission.accessType === 'anonymous_access' && user.type === 'anonymous') return true;
229
+ if (permission.accessType === 'user_access' && user.type === 'user') return true;
230
+ return false;
231
+ });
232
+ }
233
+
234
+ /**
235
+ * Converts a string ID to a MongoDB ObjectId
236
+ * @function getAsID
237
+ * @param {string} strId - String ID to convert
238
+ * @returns {mongoose.Types.ObjectId | undefined} MongoDB ObjectId or undefined if invalid
239
+ * @example
240
+ * ```typescript
241
+ * const id = getAsID('507f1f77bcf86cd799439011');
242
+ * if (id) {
243
+ * const doc = await collection.findById(id);
244
+ * }
245
+ * ```
246
+ */
247
+ export function getAsID(strId: string): mongoose.Types.ObjectId | undefined {
248
+ try {
249
+ return mongoose.Types.ObjectId(strId);
250
+ } catch (e) {
251
+ return undefined;
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Applies populate options to a Mongoose query
257
+ * @function performPopulateToQueryObject
258
+ * @param {Query<T, any>} queryObj - Mongoose query object
259
+ * @param {PopulateOptions[]} [popArr=[]] - Array of populate options
260
+ * @returns {Query<T, any>} Query with populate options applied
261
+ * @example
262
+ * ```typescript
263
+ * const query = collection.find();
264
+ * const populatedQuery = performPopulateToQueryObject(query, [
265
+ * { path: 'author', select: 'name email' }
266
+ * ]);
267
+ * ```
268
+ */
269
+ export function performPopulateToQueryObject<T = any>(
270
+ queryObj: Query<T, any>,
271
+ popArr: PopulateOptions[] = []
272
+ ): Query<T, any> {
273
+ popArr.forEach(pop => {
274
+ queryObj.populate(pop);
275
+ });
276
+ return queryObj;
277
+ }
278
+
279
+ /**
280
+ * Applies additional options to a Mongoose query
281
+ * @function performAdditionalOptionsToQueryObject
282
+ * @param {Query<T, any>} queryObj - Mongoose query object
283
+ * @param {Record<string, any>} options - Additional query options
284
+ * @returns {Query<T, any>} Query with additional options applied
285
+ * @example
286
+ * ```typescript
287
+ * const query = collection.find();
288
+ * const queryWithOptions = performAdditionalOptionsToQueryObject(query, {
289
+ * sort: { createdAt: -1 },
290
+ * limit: 10
291
+ * });
292
+ * ```
293
+ */
294
+ export function performAdditionalOptionsToQueryObject<T = any>(
295
+ queryObj: Query<T, any>,
296
+ options: Record<string, any>
297
+ ): Query<T, any> {
298
+ Object.entries(options).forEach(([key, value]) => {
299
+ // @ts-ignore
300
+ queryObj[key](value);
301
+ });
302
+ return queryObj;
303
+ }
304
+
305
+ export { TypeCasters };
@@ -0,0 +1,15 @@
1
+ import mongoose from 'mongoose';
2
+
3
+ /**
4
+ * Type casting functions for MongoDB types
5
+ */
6
+ const TypeCasters = {
7
+ ObjectId: mongoose.Types.ObjectId,
8
+ Date: (dateValue: string | Date): Date => {
9
+ const strDate = dateValue.toString();
10
+ const mongoDateFormateInString = new Date(strDate).toISOString().split('T')[0];
11
+ return new Date(mongoDateFormateInString);
12
+ },
13
+ };
14
+
15
+ export default TypeCasters;
@@ -0,0 +1,29 @@
1
+ import mongoose from 'mongoose';
2
+ import schemas from '../../class/db_schemas';
3
+ import { CollectionDefinition } from '../../class/collection_definition';
4
+ import { Permission, PermissionTypes } from '../../class/security';
5
+ import { config } from '../../config';
6
+ import { Schema } from 'mongoose';
7
+
8
+ module.exports = [
9
+ new CollectionDefinition({
10
+ database: 'cms',
11
+ collection: 'file',
12
+ schema: schemas.file as unknown as Schema,
13
+ permissions: [
14
+ new Permission({
15
+ accessType: PermissionTypes.upload_file_access,
16
+ read: true,
17
+ write: true,
18
+ onlyOwnData: false,
19
+ }),
20
+ new Permission({
21
+ accessType: PermissionTypes.remove_file_access,
22
+ read: true,
23
+ write: true,
24
+ onlyOwnData: false,
25
+ }),
26
+ ],
27
+ triggers: config.fileTriggers || [],
28
+ }),
29
+ ];