@technomoron/api-server-base 1.0.23 → 1.0.25

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.
@@ -49,13 +49,15 @@ export type ApiRoute = {
49
49
  req: ApiAuthClass;
50
50
  };
51
51
  };
52
- export interface IApiServer {
53
- addRoutes(routes: ApiRoute[]): void;
54
- }
55
- export declare class ApiModule<T extends IApiServer = ApiServer> {
56
- protected server: T;
57
- init(server: T): this;
58
- protected define_routes(): ApiRoute[];
52
+ export declare class ApiModule<T> {
53
+ server: T;
54
+ namespace: string;
55
+ mountpath: string;
56
+ static defaultNamespace: string;
57
+ constructor(opts?: {
58
+ namespace?: string;
59
+ });
60
+ defineRoutes(): ApiRoute[];
59
61
  }
60
62
  export interface ApiErrorParams {
61
63
  code?: number;
@@ -70,21 +72,28 @@ export declare class ApiError extends Error {
70
72
  constructor({ code, message, data, errors }: ApiErrorParams);
71
73
  }
72
74
  export interface ApiServerConf {
73
- jwtSecret?: string;
74
- apiPort?: number;
75
- apiHost?: string;
76
- uploadPath?: string;
77
- uploadMax?: number;
78
- origins?: string[];
79
- debug?: boolean;
80
- accessExpire?: number;
81
- refreshExpire?: number;
75
+ apiPort: number;
76
+ apiHost: string;
77
+ uploadPath: string;
78
+ uploadMax: number;
79
+ origins: string[];
80
+ debug: boolean;
81
+ apiBasePath: string;
82
+ accessSecret: string;
83
+ refreshSecret: string;
84
+ cookieDomain: string;
85
+ accessCookie: string;
86
+ refreshCookie: string;
87
+ accessExpiry: number;
88
+ refreshExpiry: number;
89
+ authApi: boolean;
90
+ devMode: boolean;
82
91
  }
83
92
  export declare class ApiServer {
84
93
  app: Application;
85
94
  currReq: ApiRequest | null;
86
- private routerV1;
87
95
  readonly config: ApiServerConf;
96
+ constructor(config?: Partial<ApiServerConf>);
88
97
  jwtSign(payload: any, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
89
98
  jwtVerify<T>(token: string, secret: string, options?: VerifyOptions): JwtVerifyResult<T>;
90
99
  jwtDecode<T>(token: string, options?: jwt.DecodeOptions): JwtDecodeResult<T>;
@@ -95,6 +104,9 @@ export declare class ApiServer {
95
104
  access: string;
96
105
  refresh: string;
97
106
  userId: unknown;
107
+ domain?: string;
108
+ fingerprint?: string;
109
+ label?: string;
98
110
  }): Promise<void>;
99
111
  getToken(params: {
100
112
  accessToken?: string;
@@ -110,8 +122,12 @@ export declare class ApiServer {
110
122
  refreshToken?: string;
111
123
  accessToken?: string;
112
124
  userId?: unknown;
125
+ domain?: string;
126
+ fingerprint?: string;
127
+ label?: string;
113
128
  }): Promise<number>;
114
- constructor(config: ApiServerConf);
129
+ verifyPassword(password: string, hash: string): Promise<boolean>;
130
+ filterUser<T = any, U = any>(fullUser: T): U;
115
131
  guessExceptionText(error: any, defMsg?: string): string;
116
132
  protected authorize(apiReq: ApiRequest, requiredClass: ApiAuthClass): Promise<void>;
117
133
  private middlewares;
@@ -119,8 +135,7 @@ export declare class ApiServer {
119
135
  private verifyJWT;
120
136
  private authenticate;
121
137
  private handle_request;
122
- addRoutes(routes: ApiRoute[]): void;
123
- api<T extends ApiModule<IApiServer>>(ApiModuleClass: new () => T): this;
138
+ api<T extends ApiModule<any>>(module: T): this;
124
139
  dumpRequest(apiReq: ApiRequest): void;
125
140
  }
126
141
  export default ApiServer;
@@ -16,16 +16,16 @@ const express_1 = __importDefault(require("express"));
16
16
  const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
17
17
  const multer_1 = __importDefault(require("multer"));
18
18
  class ApiModule {
19
- init(server) {
20
- this.server = server;
21
- server.addRoutes(this.define_routes());
22
- return this;
19
+ constructor(opts = {}) {
20
+ this.mountpath = '';
21
+ this.namespace = opts.namespace ?? this.constructor.defaultNamespace ?? '';
23
22
  }
24
- define_routes() {
23
+ defineRoutes() {
25
24
  return [];
26
25
  }
27
26
  }
28
27
  exports.ApiModule = ApiModule;
28
+ ApiModule.defaultNamespace = '';
29
29
  function guess_exception_text(error, defMsg = 'Unknown Error') {
30
30
  const msg = [];
31
31
  if (typeof error === 'string' && error.trim() !== '') {
@@ -52,7 +52,38 @@ class ApiError extends Error {
52
52
  }
53
53
  }
54
54
  exports.ApiError = ApiError;
55
+ function fillConfig(config) {
56
+ return {
57
+ apiPort: config.apiPort ?? 3101,
58
+ apiHost: config.apiHost ?? 'localhost',
59
+ uploadPath: config.uploadPath ?? '',
60
+ uploadMax: config.uploadMax ?? 30 * 1024 * 1024,
61
+ origins: config.origins ?? [],
62
+ debug: config.debug ?? false,
63
+ apiBasePath: config.apiBasePath ?? '/api',
64
+ accessSecret: config.accessSecret ?? '',
65
+ refreshSecret: config.refreshSecret ?? '',
66
+ cookieDomain: config.cookieDomain ?? '.somewhere-over-the-rainbow.com',
67
+ accessCookie: config.accessCookie ?? 'dat',
68
+ refreshCookie: config.refreshCookie ?? 'drt',
69
+ accessExpiry: config.accessExpiry ?? 60 * 15,
70
+ refreshExpiry: config.refreshExpiry ?? 30 * 24 * 60 * 60 * 1000,
71
+ authApi: config.authApi ?? false,
72
+ devMode: config.devMode ?? false
73
+ };
74
+ }
55
75
  class ApiServer {
76
+ constructor(config = {}) {
77
+ this.currReq = null;
78
+ this.config = fillConfig(config);
79
+ this.app = (0, express_1.default)();
80
+ if (config.uploadPath) {
81
+ const upload = (0, multer_1.default)({ dest: config.uploadPath });
82
+ this.app.use(upload.any());
83
+ }
84
+ this.middlewares();
85
+ // addSwaggerUi(this.app);
86
+ }
56
87
  jwtSign(payload, secret, expiresInSeconds, options) {
57
88
  options || (options = {});
58
89
  const opts = { ...options, expiresIn: expiresInSeconds };
@@ -141,27 +172,11 @@ class ApiServer {
141
172
  async deleteToken(params) {
142
173
  throw new Error('deleteToken() not implemented');
143
174
  }
144
- constructor(config) {
145
- this.currReq = null;
146
- config.jwtSecret || (config.jwtSecret = '');
147
- config.uploadMax || (config.uploadMax = 30 * 1024 * 1024);
148
- config.uploadPath || (config.uploadPath = '');
149
- config.origins || (config.origins = []);
150
- config.debug || (config.debug = false);
151
- config.apiHost || (config.apiHost = 'localhost');
152
- config.apiPort || (config.apiPort = 3101);
153
- config.accessExpire || (config.accessExpire = 60 * 15); // 15 minutes default
154
- config.refreshExpire || (config.refreshExpire = 30 * 24 * 60 * 60); // 30 days
155
- this.config = config;
156
- this.routerV1 = express_1.default.Router();
157
- this.app = (0, express_1.default)();
158
- if (config.uploadPath) {
159
- const upload = (0, multer_1.default)({ dest: config.uploadPath });
160
- this.app.use(upload.any());
161
- }
162
- this.middlewares();
163
- this.app.use('/api/v1', this.routerV1);
164
- // addSwaggerUi(this.app);
175
+ async verifyPassword(password, hash) {
176
+ throw new Error('verifyPassword() not implemented');
177
+ }
178
+ filterUser(fullUser) {
179
+ return fullUser;
165
180
  }
166
181
  guessExceptionText(error, defMsg = 'Unkown Error') {
167
182
  return guess_exception_text(error, defMsg);
@@ -214,10 +229,10 @@ class ApiServer {
214
229
  return this;
215
230
  }
216
231
  async verifyJWT(token) {
217
- if (!this.config.jwtSecret) {
232
+ if (!this.config.accessSecret) {
218
233
  return { tokenData: undefined, error: 'JWT authentication disabled; no jwtSecret set' };
219
234
  }
220
- const result = this.jwtVerify(token, this.config.jwtSecret);
235
+ const result = this.jwtVerify(token, this.config.accessSecret);
221
236
  if (!result.success) {
222
237
  return { tokenData: undefined, error: result.error };
223
238
  }
@@ -319,30 +334,21 @@ class ApiServer {
319
334
  }
320
335
  };
321
336
  }
322
- addRoutes(routes) {
323
- routes.forEach((route) => {
324
- const handler = this.handle_request(route.handler, route.auth);
325
- switch (route.method) {
326
- case 'get':
327
- this.routerV1.get(route.path, handler);
328
- break;
329
- case 'post':
330
- this.routerV1.post(route.path, handler);
331
- break;
332
- case 'put':
333
- this.routerV1.put(route.path, handler);
334
- break;
335
- case 'delete':
336
- this.routerV1.delete(route.path, handler);
337
- break;
338
- default:
339
- throw new Error(`Unsupported method: ${route.method}`);
337
+ api(module) {
338
+ const router = express_1.default.Router();
339
+ module.server = this;
340
+ const base = this.config.apiBasePath ?? '/api';
341
+ const ns = module.namespace;
342
+ const mountPath = `${base}${ns}`;
343
+ module.mountpath = mountPath;
344
+ module.defineRoutes().forEach((r) => {
345
+ const handler = this.handle_request(r.handler, r.auth);
346
+ router[r.method](r.path, handler);
347
+ if (this.config.debug) {
348
+ console.log(`Adding ${mountPath}${r.path} (${r.method.toUpperCase()})`);
340
349
  }
341
350
  });
342
- }
343
- api(ApiModuleClass) {
344
- const moduleInstance = new ApiModuleClass();
345
- moduleInstance.init(this); // docServer should be a valid IApiServer.
351
+ this.app.use(mountPath, router);
346
352
  return this;
347
353
  }
348
354
  dumpRequest(apiReq) {
@@ -49,13 +49,15 @@ export type ApiRoute = {
49
49
  req: ApiAuthClass;
50
50
  };
51
51
  };
52
- export interface IApiServer {
53
- addRoutes(routes: ApiRoute[]): void;
54
- }
55
- export declare class ApiModule<T extends IApiServer = ApiServer> {
56
- protected server: T;
57
- init(server: T): this;
58
- protected define_routes(): ApiRoute[];
52
+ export declare class ApiModule<T> {
53
+ server: T;
54
+ namespace: string;
55
+ mountpath: string;
56
+ static defaultNamespace: string;
57
+ constructor(opts?: {
58
+ namespace?: string;
59
+ });
60
+ defineRoutes(): ApiRoute[];
59
61
  }
60
62
  export interface ApiErrorParams {
61
63
  code?: number;
@@ -70,21 +72,28 @@ export declare class ApiError extends Error {
70
72
  constructor({ code, message, data, errors }: ApiErrorParams);
71
73
  }
72
74
  export interface ApiServerConf {
73
- jwtSecret?: string;
74
- apiPort?: number;
75
- apiHost?: string;
76
- uploadPath?: string;
77
- uploadMax?: number;
78
- origins?: string[];
79
- debug?: boolean;
80
- accessExpire?: number;
81
- refreshExpire?: number;
75
+ apiPort: number;
76
+ apiHost: string;
77
+ uploadPath: string;
78
+ uploadMax: number;
79
+ origins: string[];
80
+ debug: boolean;
81
+ apiBasePath: string;
82
+ accessSecret: string;
83
+ refreshSecret: string;
84
+ cookieDomain: string;
85
+ accessCookie: string;
86
+ refreshCookie: string;
87
+ accessExpiry: number;
88
+ refreshExpiry: number;
89
+ authApi: boolean;
90
+ devMode: boolean;
82
91
  }
83
92
  export declare class ApiServer {
84
93
  app: Application;
85
94
  currReq: ApiRequest | null;
86
- private routerV1;
87
95
  readonly config: ApiServerConf;
96
+ constructor(config?: Partial<ApiServerConf>);
88
97
  jwtSign(payload: any, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
89
98
  jwtVerify<T>(token: string, secret: string, options?: VerifyOptions): JwtVerifyResult<T>;
90
99
  jwtDecode<T>(token: string, options?: jwt.DecodeOptions): JwtDecodeResult<T>;
@@ -95,6 +104,9 @@ export declare class ApiServer {
95
104
  access: string;
96
105
  refresh: string;
97
106
  userId: unknown;
107
+ domain?: string;
108
+ fingerprint?: string;
109
+ label?: string;
98
110
  }): Promise<void>;
99
111
  getToken(params: {
100
112
  accessToken?: string;
@@ -110,8 +122,12 @@ export declare class ApiServer {
110
122
  refreshToken?: string;
111
123
  accessToken?: string;
112
124
  userId?: unknown;
125
+ domain?: string;
126
+ fingerprint?: string;
127
+ label?: string;
113
128
  }): Promise<number>;
114
- constructor(config: ApiServerConf);
129
+ verifyPassword(password: string, hash: string): Promise<boolean>;
130
+ filterUser<T = any, U = any>(fullUser: T): U;
115
131
  guessExceptionText(error: any, defMsg?: string): string;
116
132
  protected authorize(apiReq: ApiRequest, requiredClass: ApiAuthClass): Promise<void>;
117
133
  private middlewares;
@@ -119,8 +135,7 @@ export declare class ApiServer {
119
135
  private verifyJWT;
120
136
  private authenticate;
121
137
  private handle_request;
122
- addRoutes(routes: ApiRoute[]): void;
123
- api<T extends ApiModule<IApiServer>>(ApiModuleClass: new () => T): this;
138
+ api<T extends ApiModule<any>>(module: T): this;
124
139
  dumpRequest(apiReq: ApiRequest): void;
125
140
  }
126
141
  export default ApiServer;
@@ -10,15 +10,15 @@ import express from 'express';
10
10
  import jwt from 'jsonwebtoken';
11
11
  import multer from 'multer';
12
12
  export class ApiModule {
13
- init(server) {
14
- this.server = server;
15
- server.addRoutes(this.define_routes());
16
- return this;
13
+ constructor(opts = {}) {
14
+ this.mountpath = '';
15
+ this.namespace = opts.namespace ?? this.constructor.defaultNamespace ?? '';
17
16
  }
18
- define_routes() {
17
+ defineRoutes() {
19
18
  return [];
20
19
  }
21
20
  }
21
+ ApiModule.defaultNamespace = '';
22
22
  function guess_exception_text(error, defMsg = 'Unknown Error') {
23
23
  const msg = [];
24
24
  if (typeof error === 'string' && error.trim() !== '') {
@@ -44,7 +44,38 @@ export class ApiError extends Error {
44
44
  this.errors = errors !== undefined ? errors : {};
45
45
  }
46
46
  }
47
+ function fillConfig(config) {
48
+ return {
49
+ apiPort: config.apiPort ?? 3101,
50
+ apiHost: config.apiHost ?? 'localhost',
51
+ uploadPath: config.uploadPath ?? '',
52
+ uploadMax: config.uploadMax ?? 30 * 1024 * 1024,
53
+ origins: config.origins ?? [],
54
+ debug: config.debug ?? false,
55
+ apiBasePath: config.apiBasePath ?? '/api',
56
+ accessSecret: config.accessSecret ?? '',
57
+ refreshSecret: config.refreshSecret ?? '',
58
+ cookieDomain: config.cookieDomain ?? '.somewhere-over-the-rainbow.com',
59
+ accessCookie: config.accessCookie ?? 'dat',
60
+ refreshCookie: config.refreshCookie ?? 'drt',
61
+ accessExpiry: config.accessExpiry ?? 60 * 15,
62
+ refreshExpiry: config.refreshExpiry ?? 30 * 24 * 60 * 60 * 1000,
63
+ authApi: config.authApi ?? false,
64
+ devMode: config.devMode ?? false
65
+ };
66
+ }
47
67
  export class ApiServer {
68
+ constructor(config = {}) {
69
+ this.currReq = null;
70
+ this.config = fillConfig(config);
71
+ this.app = express();
72
+ if (config.uploadPath) {
73
+ const upload = multer({ dest: config.uploadPath });
74
+ this.app.use(upload.any());
75
+ }
76
+ this.middlewares();
77
+ // addSwaggerUi(this.app);
78
+ }
48
79
  jwtSign(payload, secret, expiresInSeconds, options) {
49
80
  options || (options = {});
50
81
  const opts = { ...options, expiresIn: expiresInSeconds };
@@ -133,27 +164,11 @@ export class ApiServer {
133
164
  async deleteToken(params) {
134
165
  throw new Error('deleteToken() not implemented');
135
166
  }
136
- constructor(config) {
137
- this.currReq = null;
138
- config.jwtSecret || (config.jwtSecret = '');
139
- config.uploadMax || (config.uploadMax = 30 * 1024 * 1024);
140
- config.uploadPath || (config.uploadPath = '');
141
- config.origins || (config.origins = []);
142
- config.debug || (config.debug = false);
143
- config.apiHost || (config.apiHost = 'localhost');
144
- config.apiPort || (config.apiPort = 3101);
145
- config.accessExpire || (config.accessExpire = 60 * 15); // 15 minutes default
146
- config.refreshExpire || (config.refreshExpire = 30 * 24 * 60 * 60); // 30 days
147
- this.config = config;
148
- this.routerV1 = express.Router();
149
- this.app = express();
150
- if (config.uploadPath) {
151
- const upload = multer({ dest: config.uploadPath });
152
- this.app.use(upload.any());
153
- }
154
- this.middlewares();
155
- this.app.use('/api/v1', this.routerV1);
156
- // addSwaggerUi(this.app);
167
+ async verifyPassword(password, hash) {
168
+ throw new Error('verifyPassword() not implemented');
169
+ }
170
+ filterUser(fullUser) {
171
+ return fullUser;
157
172
  }
158
173
  guessExceptionText(error, defMsg = 'Unkown Error') {
159
174
  return guess_exception_text(error, defMsg);
@@ -206,10 +221,10 @@ export class ApiServer {
206
221
  return this;
207
222
  }
208
223
  async verifyJWT(token) {
209
- if (!this.config.jwtSecret) {
224
+ if (!this.config.accessSecret) {
210
225
  return { tokenData: undefined, error: 'JWT authentication disabled; no jwtSecret set' };
211
226
  }
212
- const result = this.jwtVerify(token, this.config.jwtSecret);
227
+ const result = this.jwtVerify(token, this.config.accessSecret);
213
228
  if (!result.success) {
214
229
  return { tokenData: undefined, error: result.error };
215
230
  }
@@ -311,30 +326,21 @@ export class ApiServer {
311
326
  }
312
327
  };
313
328
  }
314
- addRoutes(routes) {
315
- routes.forEach((route) => {
316
- const handler = this.handle_request(route.handler, route.auth);
317
- switch (route.method) {
318
- case 'get':
319
- this.routerV1.get(route.path, handler);
320
- break;
321
- case 'post':
322
- this.routerV1.post(route.path, handler);
323
- break;
324
- case 'put':
325
- this.routerV1.put(route.path, handler);
326
- break;
327
- case 'delete':
328
- this.routerV1.delete(route.path, handler);
329
- break;
330
- default:
331
- throw new Error(`Unsupported method: ${route.method}`);
329
+ api(module) {
330
+ const router = express.Router();
331
+ module.server = this;
332
+ const base = this.config.apiBasePath ?? '/api';
333
+ const ns = module.namespace;
334
+ const mountPath = `${base}${ns}`;
335
+ module.mountpath = mountPath;
336
+ module.defineRoutes().forEach((r) => {
337
+ const handler = this.handle_request(r.handler, r.auth);
338
+ router[r.method](r.path, handler);
339
+ if (this.config.debug) {
340
+ console.log(`Adding ${mountPath}${r.path} (${r.method.toUpperCase()})`);
332
341
  }
333
342
  });
334
- }
335
- api(ApiModuleClass) {
336
- const moduleInstance = new ApiModuleClass();
337
- moduleInstance.init(this); // docServer should be a valid IApiServer.
343
+ this.app.use(mountPath, router);
338
344
  return this;
339
345
  }
340
346
  dumpRequest(apiReq) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@technomoron/api-server-base",
3
- "version": "1.0.23",
3
+ "version": "1.0.25",
4
4
  "description": "Api Server Skeleton / Base Class",
5
5
  "main": "dist/cjs/api-server-base.js",
6
6
  "module": "dist/esm/api-server-base.js",