@modular-rest/server 1.16.2 → 1.17.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.
@@ -104,13 +104,41 @@ async function createRest(options) {
104
104
  /**
105
105
  * Plug In KoaStatic
106
106
  */
107
- if (config_1.config.staticPath) {
108
- const defaultStaticPath = config_1.config.staticPath.actualPath || '';
109
- const defaultStaticRootPath = config_1.config.staticPath.path || '/assets';
110
- const staticOptions = { ...config_1.config.staticPath, defer: true };
111
- delete staticOptions.actualPath;
112
- delete staticOptions.path;
113
- app.use((0, koa_mount_1.default)(defaultStaticRootPath, (0, koa_static_1.default)(defaultStaticPath, staticOptions)));
107
+ // Collect static paths from new property or fallback to old property
108
+ let staticPathsToMount = [];
109
+ if (config_1.config.staticPaths && config_1.config.staticPaths.length > 0) {
110
+ // Use new staticPaths property
111
+ staticPathsToMount = config_1.config.staticPaths;
112
+ }
113
+ else if (config_1.config.staticPath) {
114
+ // Backward compatibility: use old staticPath property with deprecation warning
115
+ console.warn('\x1b[33m%s\x1b[0m', "Warning: 'staticPath' is deprecated and will be removed in a future version. Please use 'staticPaths' (array) instead.");
116
+ staticPathsToMount = Array.isArray(config_1.config.staticPath) ? config_1.config.staticPath : [config_1.config.staticPath];
117
+ }
118
+ // Mount all static paths
119
+ for (const staticPathConfig of staticPathsToMount) {
120
+ const directory = staticPathConfig.directory || '';
121
+ const urlPath = staticPathConfig.urlPath || '/assets';
122
+ const staticOptions = { ...staticPathConfig, defer: true };
123
+ delete staticOptions.directory;
124
+ delete staticOptions.urlPath;
125
+ app.use((0, koa_mount_1.default)(urlPath, (0, koa_static_1.default)(directory, staticOptions)));
126
+ }
127
+ // Auto-mount uploadDirectoryConfig if staticPaths (new property) is configured
128
+ if (config_1.config.staticPaths && config_1.config.staticPaths.length > 0 && config_1.config.uploadDirectoryConfig) {
129
+ const uploadDirectory = config_1.config.uploadDirectoryConfig.directory || '';
130
+ const uploadUrlPath = config_1.config.uploadDirectoryConfig.urlPath || '/assets';
131
+ const uploadStaticOptions = {
132
+ ...config_1.config.uploadDirectoryConfig,
133
+ defer: true,
134
+ };
135
+ delete uploadStaticOptions.directory;
136
+ delete uploadStaticOptions.urlPath;
137
+ app.use((0, koa_mount_1.default)(uploadUrlPath, (0, koa_static_1.default)(uploadDirectory, uploadStaticOptions)));
138
+ }
139
+ // Show deprecation warning for old uploadDirectory property
140
+ if (config_1.config.uploadDirectory) {
141
+ console.warn('\x1b[33m%s\x1b[0m', "Warning: 'uploadDirectory' is deprecated and will be removed in a future version. Please use 'uploadDirectoryConfig' (StaticPathOptions) instead.");
114
142
  }
115
143
  /**
116
144
  * Run before hook
package/dist/config.d.ts CHANGED
@@ -8,16 +8,17 @@ import { Options as KoaStaticOptionsBase } from 'koa-static';
8
8
  /**
9
9
  * The options for the static file server, it's a combination of modular-rest and [koa-static options](https://github.com/koajs/static?tab=readme-ov-file#options)
10
10
  */
11
- export type StaticPathOptions = KoaStaticOptionsBase & {
11
+ export interface StaticPathOptions extends KoaStaticOptionsBase {
12
12
  /**
13
- * The actual path of the static files on your server
13
+ * The filesystem directory where static files are stored
14
14
  */
15
- actualPath: string;
15
+ directory: string;
16
16
  /**
17
- * The path you want to serve the static files from
17
+ * The URL path where static files will be served from
18
+ * @default '/assets'
18
19
  */
19
- path: string;
20
- };
20
+ urlPath?: string;
21
+ }
21
22
  /**
22
23
  * JWT keypair configuration
23
24
  * @interface KeyPair
@@ -53,9 +54,9 @@ interface AdminUser {
53
54
  * @interface RestOptions
54
55
  * @property {KoaCorsOptions} [cors] - CORS configuration [options](https://github.com/koajs/cors?tab=readme-ov-file#corsoptions)
55
56
  * @property {string} [modulesPath] - Path to custom modules directory
56
- * @property {string} [uploadDirectory] - Directory for file uploads
57
+ * @property {StaticPathOptions[]} [staticPaths] - Array of static file serving configurations
58
+ * @property {StaticPathOptions} [uploadDirectoryConfig] - Upload directory configuration with static path info
57
59
  * @property {any} [koaBodyOptions] - Options for koa-body middleware
58
- * @property {StaticPathOptions} [staticPath] - Static file serving options
59
60
  * @property {Function} [onBeforeInit] - Hook called before initialization
60
61
  * @property {Function} [onAfterInit] - Hook called after initialization
61
62
  * @property {number} [port] - Port to listen on
@@ -69,13 +70,15 @@ interface AdminUser {
69
70
  * @property {CmsTrigger[]} [authTriggers] - Authentication triggers
70
71
  * @property {CmsTrigger[]} [fileTriggers] - File handling triggers
71
72
  * @property {DefinedFunction[]} [functions] - Custom API functions
73
+ * @property {string} [uploadDirectory] - @deprecated Use uploadDirectoryConfig instead. Directory for file uploads
74
+ * @property {StaticPathOptions | StaticPathOptions[]} [staticPath] - @deprecated Use staticPaths instead. Static file serving options
72
75
  */
73
76
  export interface RestOptions {
74
77
  cors?: KoaCorsOptions;
75
78
  modulesPath?: string;
76
- uploadDirectory?: string;
79
+ staticPaths?: StaticPathOptions[];
80
+ uploadDirectoryConfig?: StaticPathOptions;
77
81
  koaBodyOptions?: any;
78
- staticPath?: StaticPathOptions;
79
82
  onBeforeInit?: (koaApp: Koa) => void;
80
83
  onAfterInit?: (koaApp: Koa) => void;
81
84
  port?: number;
@@ -89,6 +92,14 @@ export interface RestOptions {
89
92
  authTriggers?: CmsTrigger[];
90
93
  fileTriggers?: CmsTrigger[];
91
94
  functions?: DefinedFunction[];
95
+ /**
96
+ * @deprecated Use uploadDirectoryConfig instead. This property will be removed in a future version.
97
+ */
98
+ uploadDirectory?: string;
99
+ /**
100
+ * @deprecated Use staticPaths instead. This property will be removed in a future version.
101
+ */
102
+ staticPath?: StaticPathOptions | StaticPathOptions[];
92
103
  }
93
104
  /**
94
105
  * Global configuration object
@@ -1,3 +1,4 @@
1
+ import { StaticPathOptions } from '../config';
1
2
  /**
2
3
  * Setup options interface for initializing required services
3
4
  * @interface SetupOptions
@@ -7,7 +8,8 @@
7
8
  * @property {Object} adminUser - Admin user configuration
8
9
  * @property {string} adminUser.email - Admin user email address
9
10
  * @property {string} adminUser.password - Admin user password
10
- * @property {string} [uploadDirectory] - Directory for file uploads
11
+ * @property {StaticPathOptions} [uploadDirectoryConfig] - Upload directory configuration with static path info
12
+ * @property {string} [uploadDirectory] - @deprecated Use uploadDirectoryConfig instead. Directory for file uploads
11
13
  */
12
14
  interface SetupOptions {
13
15
  keypair?: {
@@ -18,6 +20,10 @@ interface SetupOptions {
18
20
  email: string;
19
21
  password: string;
20
22
  };
23
+ uploadDirectoryConfig?: StaticPathOptions;
24
+ /**
25
+ * @deprecated Use uploadDirectoryConfig instead. This property will be removed in a future version.
26
+ */
21
27
  uploadDirectory?: string;
22
28
  }
23
29
  /**
@@ -34,7 +40,7 @@ interface SetupOptions {
34
40
  *
35
41
  * @example
36
42
  * ```typescript
37
- * // Setup with custom keypair
43
+ * // Setup with custom keypair and uploadDirectoryConfig
38
44
  * await setup({
39
45
  * keypair: {
40
46
  * private: 'your-private-key',
@@ -44,7 +50,10 @@ interface SetupOptions {
44
50
  * email: 'admin@example.com',
45
51
  * password: 'secure-password'
46
52
  * },
47
- * uploadDirectory: './uploads'
53
+ * uploadDirectoryConfig: {
54
+ * directory: './uploads',
55
+ * urlPath: '/assets'
56
+ * }
48
57
  * });
49
58
  *
50
59
  * // Setup with auto-generated keypair
@@ -56,5 +65,5 @@ interface SetupOptions {
56
65
  * });
57
66
  * ```
58
67
  */
59
- export declare function setup({ keypair, adminUser, uploadDirectory }: SetupOptions): Promise<void>;
68
+ export declare function setup({ keypair, adminUser, uploadDirectory, uploadDirectoryConfig, }: SetupOptions): Promise<void>;
60
69
  export {};
@@ -40,6 +40,7 @@ exports.setup = setup;
40
40
  const DataInsertion = __importStar(require("./data_insertion"));
41
41
  const JWT = __importStar(require("../services/jwt/service"));
42
42
  const FileService = __importStar(require("../services/file/service"));
43
+ const config_1 = require("../config");
43
44
  const keypair_1 = __importDefault(require("keypair"));
44
45
  /**
45
46
  * Sets up required services for the application to run
@@ -55,7 +56,7 @@ const keypair_1 = __importDefault(require("keypair"));
55
56
  *
56
57
  * @example
57
58
  * ```typescript
58
- * // Setup with custom keypair
59
+ * // Setup with custom keypair and uploadDirectoryConfig
59
60
  * await setup({
60
61
  * keypair: {
61
62
  * private: 'your-private-key',
@@ -65,7 +66,10 @@ const keypair_1 = __importDefault(require("keypair"));
65
66
  * email: 'admin@example.com',
66
67
  * password: 'secure-password'
67
68
  * },
68
- * uploadDirectory: './uploads'
69
+ * uploadDirectoryConfig: {
70
+ * directory: './uploads',
71
+ * urlPath: '/assets'
72
+ * }
69
73
  * });
70
74
  *
71
75
  * // Setup with auto-generated keypair
@@ -77,7 +81,7 @@ const keypair_1 = __importDefault(require("keypair"));
77
81
  * });
78
82
  * ```
79
83
  */
80
- async function setup({ keypair, adminUser, uploadDirectory }) {
84
+ async function setup({ keypair, adminUser, uploadDirectory, uploadDirectoryConfig, }) {
81
85
  /**
82
86
  * Json web Token
83
87
  *
@@ -102,7 +106,14 @@ async function setup({ keypair, adminUser, uploadDirectory }) {
102
106
  /**
103
107
  * File Service
104
108
  */
105
- if (uploadDirectory) {
109
+ // Check for uploadDirectoryConfig from global config first, then from options
110
+ const uploadConfigToUse = config_1.config.uploadDirectoryConfig || uploadDirectoryConfig;
111
+ if (uploadConfigToUse) {
112
+ FileService.main.setUploadDirectory(uploadConfigToUse);
113
+ }
114
+ else if (uploadDirectory) {
115
+ // Backward compatibility: use old uploadDirectory property with deprecation warning
116
+ console.warn('\x1b[33m%s\x1b[0m', "Warning: 'uploadDirectory' is deprecated and will be removed in a future version. Please use 'uploadDirectoryConfig' (StaticPathOptions) instead.");
106
117
  FileService.main.setUploadDirectory(uploadDirectory);
107
118
  }
108
119
  }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createRest } from './application';
2
2
  import { Schema } from 'mongoose';
3
+ import { StaticPathOptions, RestOptions } from './config';
3
4
  import * as paginator from './class/paginator';
4
5
  import * as reply from './class/reply';
5
6
  import { validator } from './class/validator';
@@ -23,6 +24,28 @@ import * as middleware from './middlewares';
23
24
  * @returns A new REST API instance
24
25
  */
25
26
  export { createRest };
27
+ /**
28
+ * @description Configuration types for creating a REST API instance
29
+ * @example
30
+ * ```typescript
31
+ * import { RestOptions, StaticPathOptions } from '@modular-rest/server-ts';
32
+ *
33
+ * const config: RestOptions = {
34
+ * port: 3000,
35
+ * staticPaths: [
36
+ * {
37
+ * directory: './public',
38
+ * urlPath: '/static'
39
+ * }
40
+ * ],
41
+ * uploadDirectoryConfig: {
42
+ * directory: './uploads',
43
+ * urlPath: '/assets'
44
+ * }
45
+ * };
46
+ * ```
47
+ */
48
+ export type { RestOptions, StaticPathOptions };
26
49
  export { CollectionDefinition, defineCollection };
27
50
  /**
28
51
  * @description Provides predefined database schemas
@@ -1,3 +1,4 @@
1
+ import { StaticPathOptions } from '../../config';
1
2
  import { IFile } from '../../class/db_schemas';
2
3
  /**
3
4
  * Service name constant
@@ -51,6 +52,10 @@ declare class FileService {
51
52
  * @hidden
52
53
  */
53
54
  private directory;
55
+ /**
56
+ * @hidden
57
+ */
58
+ private urlPath;
54
59
  /**
55
60
  * @hidden
56
61
  */
@@ -63,16 +68,23 @@ declare class FileService {
63
68
  * @hidden
64
69
  *
65
70
  * Sets the upload directory for file storage
66
- * @param {string} directory - Directory path for file storage
71
+ * @param {string | StaticPathOptions} directoryOrConfig - Directory path for file storage or StaticPathOptions configuration
67
72
  * @throws {Error} If directory is invalid or not writable
68
73
  * @example
69
74
  * ```typescript
70
75
  * import { fileService } from '@modular-rest/server';
71
76
  *
77
+ * // New format with StaticPathOptions
78
+ * fileService.setUploadDirectory({
79
+ * directory: '/path/to/uploads',
80
+ * urlPath: '/assets'
81
+ * });
82
+ *
83
+ * // Legacy format (deprecated)
72
84
  * fileService.setUploadDirectory('/path/to/uploads');
73
85
  * ```
74
86
  */
75
- setUploadDirectory(directory: string): void;
87
+ setUploadDirectory(directoryOrConfig: string | StaticPathOptions): void;
76
88
  /**
77
89
  * @hidden
78
90
  *
@@ -170,13 +182,13 @@ declare class FileService {
170
182
  *
171
183
  * @param {string} fileId - File ID to get link for
172
184
  * @returns {Promise<string>} Promise resolving to file URL
173
- * @throws {Error} If static path root is not defined or file is not found
185
+ * @throws {Error} If URL path is not defined or file is not found
174
186
  * @example
175
187
  * ```typescript
176
188
  * import { fileService } from '@modular-rest/server';
177
189
  *
178
190
  * const link = await fileService.getFileLink('file123');
179
- * // Returns: '/static/jpeg/profile/1234567890.jpeg'
191
+ * // Returns: '/uploads/jpeg/profile/1234567890.jpeg'
180
192
  * ```
181
193
  */
182
194
  getFileLink(fileId: string): Promise<string>;
@@ -41,7 +41,6 @@ const fs_1 = __importDefault(require("fs"));
41
41
  const path_1 = __importDefault(require("path"));
42
42
  const DataProvider = __importStar(require("../data_provider/service"));
43
43
  const trigger_operator_1 = __importDefault(require("../../class/trigger_operator"));
44
- const config_1 = require("../../config");
45
44
  /**
46
45
  * Service name constant
47
46
  * @constant {string}
@@ -63,25 +62,54 @@ class FileService {
63
62
  * @hidden
64
63
  */
65
64
  this.directory = null;
65
+ /**
66
+ * @hidden
67
+ */
68
+ this.urlPath = null;
66
69
  }
67
70
  /**
68
71
  * @hidden
69
72
  *
70
73
  * Sets the upload directory for file storage
71
- * @param {string} directory - Directory path for file storage
74
+ * @param {string | StaticPathOptions} directoryOrConfig - Directory path for file storage or StaticPathOptions configuration
72
75
  * @throws {Error} If directory is invalid or not writable
73
76
  * @example
74
77
  * ```typescript
75
78
  * import { fileService } from '@modular-rest/server';
76
79
  *
80
+ * // New format with StaticPathOptions
81
+ * fileService.setUploadDirectory({
82
+ * directory: '/path/to/uploads',
83
+ * urlPath: '/assets'
84
+ * });
85
+ *
86
+ * // Legacy format (deprecated)
77
87
  * fileService.setUploadDirectory('/path/to/uploads');
78
88
  * ```
79
89
  */
80
- setUploadDirectory(directory) {
90
+ setUploadDirectory(directoryOrConfig) {
91
+ // Handle backward compatibility with string
92
+ if (typeof directoryOrConfig === 'string') {
93
+ console.warn('\x1b[33m%s\x1b[0m', "Warning: Passing a string to 'setUploadDirectory' is deprecated. Please use StaticPathOptions object instead.");
94
+ if (!fs_1.default.existsSync(directoryOrConfig)) {
95
+ fs_1.default.mkdirSync(directoryOrConfig, { recursive: true });
96
+ }
97
+ this.directory = directoryOrConfig;
98
+ this.urlPath = null; // No URL path available with legacy format
99
+ return;
100
+ }
101
+ // New format: Extract only necessary properties from StaticPathOptions
102
+ const directory = directoryOrConfig.directory || '';
103
+ const urlPath = directoryOrConfig.urlPath || '/assets';
104
+ if (!directory) {
105
+ throw new Error('directory is required in uploadDirectoryConfig');
106
+ }
81
107
  if (!fs_1.default.existsSync(directory)) {
82
108
  fs_1.default.mkdirSync(directory, { recursive: true });
83
109
  }
110
+ // Store only the necessary properties (ignore koa-static options)
84
111
  this.directory = directory;
112
+ this.urlPath = urlPath;
85
113
  }
86
114
  /**
87
115
  * @hidden
@@ -295,21 +323,21 @@ class FileService {
295
323
  *
296
324
  * @param {string} fileId - File ID to get link for
297
325
  * @returns {Promise<string>} Promise resolving to file URL
298
- * @throws {Error} If static path root is not defined or file is not found
326
+ * @throws {Error} If URL path is not defined or file is not found
299
327
  * @example
300
328
  * ```typescript
301
329
  * import { fileService } from '@modular-rest/server';
302
330
  *
303
331
  * const link = await fileService.getFileLink('file123');
304
- * // Returns: '/static/jpeg/profile/1234567890.jpeg'
332
+ * // Returns: '/uploads/jpeg/profile/1234567890.jpeg'
305
333
  * ```
306
334
  */
307
335
  async getFileLink(fileId) {
308
336
  const fileDoc = await FileService.instance.getFile(fileId);
309
- if (!config_1.config.staticPath?.actualPath) {
310
- throw new Error('Static path root is not defined');
337
+ if (!FileService.instance.urlPath) {
338
+ throw new Error('Upload directory URL path is not defined. Please configure uploadDirectoryConfig with a urlPath property.');
311
339
  }
312
- const link = config_1.config.staticPath.actualPath + `/${fileDoc.format}/${fileDoc.tag}/` + fileDoc.fileName;
340
+ const link = `${FileService.instance.urlPath}/${fileDoc.format}/${fileDoc.tag}/${fileDoc.fileName}`;
313
341
  return link;
314
342
  }
315
343
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modular-rest/server",
3
- "version": "1.16.2",
3
+ "version": "1.17.0",
4
4
  "description": "TypeScript version of a nodejs module based on KOAJS for developing Rest-APIs in a modular solution.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -74,16 +74,56 @@ export async function createRest(options: RestOptions): Promise<{ app: Koa; serv
74
74
  /**
75
75
  * Plug In KoaStatic
76
76
  */
77
- if (config.staticPath) {
78
- const defaultStaticPath = config.staticPath.actualPath || '';
79
- const defaultStaticRootPath = config.staticPath.path || '/assets';
77
+ // Collect static paths from new property or fallback to old property
78
+ let staticPathsToMount: StaticPathOptions[] = [];
79
+
80
+ if (config.staticPaths && config.staticPaths.length > 0) {
81
+ // Use new staticPaths property
82
+ staticPathsToMount = config.staticPaths;
83
+ } else if (config.staticPath) {
84
+ // Backward compatibility: use old staticPath property with deprecation warning
85
+ console.warn(
86
+ '\x1b[33m%s\x1b[0m',
87
+ "Warning: 'staticPath' is deprecated and will be removed in a future version. Please use 'staticPaths' (array) instead."
88
+ );
89
+ staticPathsToMount = Array.isArray(config.staticPath) ? config.staticPath : [config.staticPath];
90
+ }
91
+
92
+ // Mount all static paths
93
+ for (const staticPathConfig of staticPathsToMount) {
94
+ const directory = staticPathConfig.directory || '';
95
+ const urlPath = staticPathConfig.urlPath || '/assets';
96
+
97
+ const staticOptions: Partial<StaticPathOptions> = { ...staticPathConfig, defer: true };
80
98
 
81
- const staticOptions: Partial<StaticPathOptions> = { ...config.staticPath, defer: true };
99
+ delete staticOptions.directory;
100
+ delete staticOptions.urlPath;
101
+
102
+ app.use(mount(urlPath, koaStatic(directory, staticOptions)));
103
+ }
82
104
 
83
- delete staticOptions.actualPath;
84
- delete staticOptions.path;
105
+ // Auto-mount uploadDirectoryConfig if staticPaths (new property) is configured
106
+ if (config.staticPaths && config.staticPaths.length > 0 && config.uploadDirectoryConfig) {
107
+ const uploadDirectory = config.uploadDirectoryConfig.directory || '';
108
+ const uploadUrlPath = config.uploadDirectoryConfig.urlPath || '/assets';
109
+
110
+ const uploadStaticOptions: Partial<StaticPathOptions> = {
111
+ ...config.uploadDirectoryConfig,
112
+ defer: true,
113
+ };
114
+
115
+ delete uploadStaticOptions.directory;
116
+ delete uploadStaticOptions.urlPath;
117
+
118
+ app.use(mount(uploadUrlPath, koaStatic(uploadDirectory, uploadStaticOptions)));
119
+ }
85
120
 
86
- app.use(mount(defaultStaticRootPath, koaStatic(defaultStaticPath, staticOptions)));
121
+ // Show deprecation warning for old uploadDirectory property
122
+ if (config.uploadDirectory) {
123
+ console.warn(
124
+ '\x1b[33m%s\x1b[0m',
125
+ "Warning: 'uploadDirectory' is deprecated and will be removed in a future version. Please use 'uploadDirectoryConfig' (StaticPathOptions) instead."
126
+ );
87
127
  }
88
128
 
89
129
  /**
package/src/config.ts CHANGED
@@ -10,16 +10,17 @@ import { Options as KoaStaticOptionsBase } from 'koa-static';
10
10
  /**
11
11
  * The options for the static file server, it's a combination of modular-rest and [koa-static options](https://github.com/koajs/static?tab=readme-ov-file#options)
12
12
  */
13
- export type StaticPathOptions = KoaStaticOptionsBase & {
13
+ export interface StaticPathOptions extends KoaStaticOptionsBase {
14
14
  /**
15
- * The actual path of the static files on your server
15
+ * The filesystem directory where static files are stored
16
16
  */
17
- actualPath: string;
17
+ directory: string;
18
18
  /**
19
- * The path you want to serve the static files from
19
+ * The URL path where static files will be served from
20
+ * @default '/assets'
20
21
  */
21
- path: string;
22
- };
22
+ urlPath?: string;
23
+ }
23
24
 
24
25
  /**
25
26
  * JWT keypair configuration
@@ -59,9 +60,9 @@ interface AdminUser {
59
60
  * @interface RestOptions
60
61
  * @property {KoaCorsOptions} [cors] - CORS configuration [options](https://github.com/koajs/cors?tab=readme-ov-file#corsoptions)
61
62
  * @property {string} [modulesPath] - Path to custom modules directory
62
- * @property {string} [uploadDirectory] - Directory for file uploads
63
+ * @property {StaticPathOptions[]} [staticPaths] - Array of static file serving configurations
64
+ * @property {StaticPathOptions} [uploadDirectoryConfig] - Upload directory configuration with static path info
63
65
  * @property {any} [koaBodyOptions] - Options for koa-body middleware
64
- * @property {StaticPathOptions} [staticPath] - Static file serving options
65
66
  * @property {Function} [onBeforeInit] - Hook called before initialization
66
67
  * @property {Function} [onAfterInit] - Hook called after initialization
67
68
  * @property {number} [port] - Port to listen on
@@ -75,13 +76,15 @@ interface AdminUser {
75
76
  * @property {CmsTrigger[]} [authTriggers] - Authentication triggers
76
77
  * @property {CmsTrigger[]} [fileTriggers] - File handling triggers
77
78
  * @property {DefinedFunction[]} [functions] - Custom API functions
79
+ * @property {string} [uploadDirectory] - @deprecated Use uploadDirectoryConfig instead. Directory for file uploads
80
+ * @property {StaticPathOptions | StaticPathOptions[]} [staticPath] - @deprecated Use staticPaths instead. Static file serving options
78
81
  */
79
82
  export interface RestOptions {
80
83
  cors?: KoaCorsOptions;
81
84
  modulesPath?: string;
82
- uploadDirectory?: string;
85
+ staticPaths?: StaticPathOptions[];
86
+ uploadDirectoryConfig?: StaticPathOptions;
83
87
  koaBodyOptions?: any;
84
- staticPath?: StaticPathOptions;
85
88
  onBeforeInit?: (koaApp: Koa) => void;
86
89
  onAfterInit?: (koaApp: Koa) => void;
87
90
  port?: number;
@@ -95,6 +98,14 @@ export interface RestOptions {
95
98
  authTriggers?: CmsTrigger[];
96
99
  fileTriggers?: CmsTrigger[];
97
100
  functions?: DefinedFunction[];
101
+ /**
102
+ * @deprecated Use uploadDirectoryConfig instead. This property will be removed in a future version.
103
+ */
104
+ uploadDirectory?: string;
105
+ /**
106
+ * @deprecated Use staticPaths instead. This property will be removed in a future version.
107
+ */
108
+ staticPath?: StaticPathOptions | StaticPathOptions[];
98
109
  }
99
110
 
100
111
  /**
@@ -1,6 +1,7 @@
1
1
  import * as DataInsertion from './data_insertion';
2
2
  import * as JWT from '../services/jwt/service';
3
3
  import * as FileService from '../services/file/service';
4
+ import { config, StaticPathOptions } from '../config';
4
5
  import generateKeypair from 'keypair';
5
6
 
6
7
  /**
@@ -12,7 +13,8 @@ import generateKeypair from 'keypair';
12
13
  * @property {Object} adminUser - Admin user configuration
13
14
  * @property {string} adminUser.email - Admin user email address
14
15
  * @property {string} adminUser.password - Admin user password
15
- * @property {string} [uploadDirectory] - Directory for file uploads
16
+ * @property {StaticPathOptions} [uploadDirectoryConfig] - Upload directory configuration with static path info
17
+ * @property {string} [uploadDirectory] - @deprecated Use uploadDirectoryConfig instead. Directory for file uploads
16
18
  */
17
19
  interface SetupOptions {
18
20
  keypair?: {
@@ -23,6 +25,10 @@ interface SetupOptions {
23
25
  email: string;
24
26
  password: string;
25
27
  };
28
+ uploadDirectoryConfig?: StaticPathOptions;
29
+ /**
30
+ * @deprecated Use uploadDirectoryConfig instead. This property will be removed in a future version.
31
+ */
26
32
  uploadDirectory?: string;
27
33
  }
28
34
 
@@ -40,7 +46,7 @@ interface SetupOptions {
40
46
  *
41
47
  * @example
42
48
  * ```typescript
43
- * // Setup with custom keypair
49
+ * // Setup with custom keypair and uploadDirectoryConfig
44
50
  * await setup({
45
51
  * keypair: {
46
52
  * private: 'your-private-key',
@@ -50,7 +56,10 @@ interface SetupOptions {
50
56
  * email: 'admin@example.com',
51
57
  * password: 'secure-password'
52
58
  * },
53
- * uploadDirectory: './uploads'
59
+ * uploadDirectoryConfig: {
60
+ * directory: './uploads',
61
+ * urlPath: '/assets'
62
+ * }
54
63
  * });
55
64
  *
56
65
  * // Setup with auto-generated keypair
@@ -62,7 +71,12 @@ interface SetupOptions {
62
71
  * });
63
72
  * ```
64
73
  */
65
- export async function setup({ keypair, adminUser, uploadDirectory }: SetupOptions): Promise<void> {
74
+ export async function setup({
75
+ keypair,
76
+ adminUser,
77
+ uploadDirectory,
78
+ uploadDirectoryConfig,
79
+ }: SetupOptions): Promise<void> {
66
80
  /**
67
81
  * Json web Token
68
82
  *
@@ -90,7 +104,17 @@ export async function setup({ keypair, adminUser, uploadDirectory }: SetupOption
90
104
  /**
91
105
  * File Service
92
106
  */
93
- if (uploadDirectory) {
107
+ // Check for uploadDirectoryConfig from global config first, then from options
108
+ const uploadConfigToUse = config.uploadDirectoryConfig || uploadDirectoryConfig;
109
+
110
+ if (uploadConfigToUse) {
111
+ FileService.main.setUploadDirectory(uploadConfigToUse);
112
+ } else if (uploadDirectory) {
113
+ // Backward compatibility: use old uploadDirectory property with deprecation warning
114
+ console.warn(
115
+ '\x1b[33m%s\x1b[0m',
116
+ "Warning: 'uploadDirectory' is deprecated and will be removed in a future version. Please use 'uploadDirectoryConfig' (StaticPathOptions) instead."
117
+ );
94
118
  FileService.main.setUploadDirectory(uploadDirectory);
95
119
  }
96
120
  }
package/src/index.ts CHANGED
@@ -2,6 +2,9 @@
2
2
  import { createRest } from './application';
3
3
  import { Schema } from 'mongoose';
4
4
 
5
+ // Configuration types
6
+ import { StaticPathOptions, RestOptions } from './config';
7
+
5
8
  // Utilities
6
9
  import * as paginator from './class/paginator';
7
10
  import * as reply from './class/reply';
@@ -37,6 +40,29 @@ import * as middleware from './middlewares';
37
40
  */
38
41
  export { createRest };
39
42
 
43
+ /**
44
+ * @description Configuration types for creating a REST API instance
45
+ * @example
46
+ * ```typescript
47
+ * import { RestOptions, StaticPathOptions } from '@modular-rest/server-ts';
48
+ *
49
+ * const config: RestOptions = {
50
+ * port: 3000,
51
+ * staticPaths: [
52
+ * {
53
+ * directory: './public',
54
+ * urlPath: '/static'
55
+ * }
56
+ * ],
57
+ * uploadDirectoryConfig: {
58
+ * directory: './uploads',
59
+ * urlPath: '/assets'
60
+ * }
61
+ * };
62
+ * ```
63
+ */
64
+ export type { RestOptions, StaticPathOptions };
65
+
40
66
  export { CollectionDefinition, defineCollection };
41
67
 
42
68
  /**
@@ -2,7 +2,7 @@ import fs from 'fs';
2
2
  import pathModule from 'path';
3
3
  import * as DataProvider from '../data_provider/service';
4
4
  import triggerService from '../../class/trigger_operator';
5
- import { config } from '../../config';
5
+ import { config, StaticPathOptions } from '../../config';
6
6
  import { IFile } from '../../class/db_schemas';
7
7
 
8
8
  /**
@@ -61,6 +61,11 @@ class FileService {
61
61
  */
62
62
  private directory: string | null = null;
63
63
 
64
+ /**
65
+ * @hidden
66
+ */
67
+ private urlPath: string | null = null;
68
+
64
69
  /**
65
70
  * @hidden
66
71
  */
@@ -75,20 +80,52 @@ class FileService {
75
80
  * @hidden
76
81
  *
77
82
  * Sets the upload directory for file storage
78
- * @param {string} directory - Directory path for file storage
83
+ * @param {string | StaticPathOptions} directoryOrConfig - Directory path for file storage or StaticPathOptions configuration
79
84
  * @throws {Error} If directory is invalid or not writable
80
85
  * @example
81
86
  * ```typescript
82
87
  * import { fileService } from '@modular-rest/server';
83
88
  *
89
+ * // New format with StaticPathOptions
90
+ * fileService.setUploadDirectory({
91
+ * directory: '/path/to/uploads',
92
+ * urlPath: '/assets'
93
+ * });
94
+ *
95
+ * // Legacy format (deprecated)
84
96
  * fileService.setUploadDirectory('/path/to/uploads');
85
97
  * ```
86
98
  */
87
- setUploadDirectory(directory: string): void {
99
+ setUploadDirectory(directoryOrConfig: string | StaticPathOptions): void {
100
+ // Handle backward compatibility with string
101
+ if (typeof directoryOrConfig === 'string') {
102
+ console.warn(
103
+ '\x1b[33m%s\x1b[0m',
104
+ "Warning: Passing a string to 'setUploadDirectory' is deprecated. Please use StaticPathOptions object instead."
105
+ );
106
+ if (!fs.existsSync(directoryOrConfig)) {
107
+ fs.mkdirSync(directoryOrConfig, { recursive: true });
108
+ }
109
+ this.directory = directoryOrConfig;
110
+ this.urlPath = null; // No URL path available with legacy format
111
+ return;
112
+ }
113
+
114
+ // New format: Extract only necessary properties from StaticPathOptions
115
+ const directory = directoryOrConfig.directory || '';
116
+ const urlPath = directoryOrConfig.urlPath || '/assets';
117
+
118
+ if (!directory) {
119
+ throw new Error('directory is required in uploadDirectoryConfig');
120
+ }
121
+
88
122
  if (!fs.existsSync(directory)) {
89
123
  fs.mkdirSync(directory, { recursive: true });
90
124
  }
125
+
126
+ // Store only the necessary properties (ignore koa-static options)
91
127
  this.directory = directory;
128
+ this.urlPath = urlPath;
92
129
  }
93
130
 
94
131
  /**
@@ -333,24 +370,25 @@ class FileService {
333
370
  *
334
371
  * @param {string} fileId - File ID to get link for
335
372
  * @returns {Promise<string>} Promise resolving to file URL
336
- * @throws {Error} If static path root is not defined or file is not found
373
+ * @throws {Error} If URL path is not defined or file is not found
337
374
  * @example
338
375
  * ```typescript
339
376
  * import { fileService } from '@modular-rest/server';
340
377
  *
341
378
  * const link = await fileService.getFileLink('file123');
342
- * // Returns: '/static/jpeg/profile/1234567890.jpeg'
379
+ * // Returns: '/uploads/jpeg/profile/1234567890.jpeg'
343
380
  * ```
344
381
  */
345
382
  async getFileLink(fileId: string): Promise<string> {
346
383
  const fileDoc = await FileService.instance.getFile(fileId);
347
384
 
348
- if (!config.staticPath?.actualPath) {
349
- throw new Error('Static path root is not defined');
385
+ if (!FileService.instance.urlPath) {
386
+ throw new Error(
387
+ 'Upload directory URL path is not defined. Please configure uploadDirectoryConfig with a urlPath property.'
388
+ );
350
389
  }
351
390
 
352
- const link =
353
- config.staticPath.actualPath + `/${fileDoc.format}/${fileDoc.tag}/` + fileDoc.fileName;
391
+ const link = `${FileService.instance.urlPath}/${fileDoc.format}/${fileDoc.tag}/${fileDoc.fileName}`;
354
392
 
355
393
  return link;
356
394
  }