@technomoron/api-server-base 1.1.5 → 1.1.6
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.
|
@@ -325,7 +325,7 @@ function fillConfig(config) {
|
|
|
325
325
|
accessCookie: config.accessCookie ?? 'dat',
|
|
326
326
|
refreshCookie: config.refreshCookie ?? 'drt',
|
|
327
327
|
accessExpiry: config.accessExpiry ?? 60 * 15,
|
|
328
|
-
refreshExpiry: config.refreshExpiry ?? 30 * 24 * 60 * 60
|
|
328
|
+
refreshExpiry: config.refreshExpiry ?? 30 * 24 * 60 * 60,
|
|
329
329
|
authApi: config.authApi ?? false,
|
|
330
330
|
devMode: config.devMode ?? false,
|
|
331
331
|
hydrateGetBody: config.hydrateGetBody ?? true,
|
|
@@ -628,18 +628,18 @@ class ApiServer {
|
|
|
628
628
|
handle_request(handler, auth) {
|
|
629
629
|
return async (req, res, next) => {
|
|
630
630
|
void next;
|
|
631
|
+
const apiReq = {
|
|
632
|
+
server: this,
|
|
633
|
+
req,
|
|
634
|
+
res,
|
|
635
|
+
token: '',
|
|
636
|
+
tokenData: null,
|
|
637
|
+
getClientInfo: () => ensureClientInfo(apiReq),
|
|
638
|
+
getClientIp: () => ensureClientInfo(apiReq).ip,
|
|
639
|
+
getClientIpChain: () => ensureClientInfo(apiReq).ipchain
|
|
640
|
+
};
|
|
641
|
+
this.currReq = apiReq;
|
|
631
642
|
try {
|
|
632
|
-
const apiReq = {
|
|
633
|
-
server: this,
|
|
634
|
-
req,
|
|
635
|
-
res,
|
|
636
|
-
token: '',
|
|
637
|
-
tokenData: null,
|
|
638
|
-
getClientInfo: () => ensureClientInfo(apiReq),
|
|
639
|
-
getClientIp: () => ensureClientInfo(apiReq).ip,
|
|
640
|
-
getClientIpChain: () => ensureClientInfo(apiReq).ipchain
|
|
641
|
-
};
|
|
642
|
-
this.currReq = apiReq;
|
|
643
643
|
if (this.config.hydrateGetBody) {
|
|
644
644
|
hydrateGetBody(apiReq.req);
|
|
645
645
|
}
|
|
@@ -660,7 +660,11 @@ class ApiServer {
|
|
|
660
660
|
throw new ApiError({ code: 500, message: 'Handler result must start with a numeric status code' });
|
|
661
661
|
}
|
|
662
662
|
const message = typeof rawMessage === 'string' ? rawMessage : 'Success';
|
|
663
|
-
|
|
663
|
+
const responsePayload = { code, message, data };
|
|
664
|
+
if (this.config.debug) {
|
|
665
|
+
this.dumpResponse(apiReq, responsePayload, code);
|
|
666
|
+
}
|
|
667
|
+
res.status(code).json(responsePayload);
|
|
664
668
|
}
|
|
665
669
|
catch (error) {
|
|
666
670
|
if (error instanceof ApiError || isApiErrorLike(error)) {
|
|
@@ -668,20 +672,28 @@ class ApiServer {
|
|
|
668
672
|
const normalizedErrors = apiError.errors && typeof apiError.errors === 'object' && !Array.isArray(apiError.errors)
|
|
669
673
|
? apiError.errors
|
|
670
674
|
: {};
|
|
671
|
-
|
|
675
|
+
const errorPayload = {
|
|
672
676
|
code: apiError.code,
|
|
673
677
|
message: apiError.message,
|
|
674
678
|
data: apiError.data ?? null,
|
|
675
679
|
errors: normalizedErrors
|
|
676
|
-
}
|
|
680
|
+
};
|
|
681
|
+
if (this.config.debug) {
|
|
682
|
+
this.dumpResponse(apiReq, errorPayload, apiError.code);
|
|
683
|
+
}
|
|
684
|
+
res.status(apiError.code).json(errorPayload);
|
|
677
685
|
}
|
|
678
686
|
else {
|
|
679
|
-
|
|
687
|
+
const errorPayload = {
|
|
680
688
|
code: 500,
|
|
681
689
|
message: this.guessExceptionText(error),
|
|
682
690
|
data: null,
|
|
683
691
|
errors: {}
|
|
684
|
-
}
|
|
692
|
+
};
|
|
693
|
+
if (this.config.debug) {
|
|
694
|
+
this.dumpResponse(apiReq, errorPayload, 500);
|
|
695
|
+
}
|
|
696
|
+
res.status(500).json(errorPayload);
|
|
685
697
|
}
|
|
686
698
|
}
|
|
687
699
|
};
|
|
@@ -719,6 +731,59 @@ class ApiServer {
|
|
|
719
731
|
console.log('Headers:', req.headers);
|
|
720
732
|
console.log('------------------------');
|
|
721
733
|
}
|
|
734
|
+
formatDebugValue(value, maxLength = 50, seen = new WeakSet()) {
|
|
735
|
+
if (value === null || value === undefined) {
|
|
736
|
+
return value;
|
|
737
|
+
}
|
|
738
|
+
if (typeof value === 'string') {
|
|
739
|
+
return value.length <= maxLength
|
|
740
|
+
? value
|
|
741
|
+
: `${value.slice(0, maxLength)}… [truncated ${value.length - maxLength} chars]`;
|
|
742
|
+
}
|
|
743
|
+
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
|
|
744
|
+
return value;
|
|
745
|
+
}
|
|
746
|
+
if (typeof value === 'symbol') {
|
|
747
|
+
return value.toString();
|
|
748
|
+
}
|
|
749
|
+
if (value instanceof Date) {
|
|
750
|
+
return value.toISOString();
|
|
751
|
+
}
|
|
752
|
+
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
|
|
753
|
+
return `<Buffer length=${value.length}>`;
|
|
754
|
+
}
|
|
755
|
+
if (typeof value === 'function') {
|
|
756
|
+
return `[Function ${value.name || 'anonymous'}]`;
|
|
757
|
+
}
|
|
758
|
+
if (typeof value === 'object') {
|
|
759
|
+
const obj = value;
|
|
760
|
+
if (seen.has(obj)) {
|
|
761
|
+
return '[Circular]';
|
|
762
|
+
}
|
|
763
|
+
seen.add(obj);
|
|
764
|
+
if (Array.isArray(value)) {
|
|
765
|
+
const arr = value.map((item) => this.formatDebugValue(item, maxLength, seen));
|
|
766
|
+
seen.delete(obj);
|
|
767
|
+
return arr;
|
|
768
|
+
}
|
|
769
|
+
const recordValue = value;
|
|
770
|
+
const entries = Object.entries(recordValue).reduce((acc, [key, val]) => {
|
|
771
|
+
acc[key] = this.formatDebugValue(val, maxLength, seen);
|
|
772
|
+
return acc;
|
|
773
|
+
}, {});
|
|
774
|
+
seen.delete(obj);
|
|
775
|
+
return entries;
|
|
776
|
+
}
|
|
777
|
+
return value;
|
|
778
|
+
}
|
|
779
|
+
dumpResponse(apiReq, payload, status) {
|
|
780
|
+
const url = apiReq.req.originalUrl || apiReq.req.url;
|
|
781
|
+
console.log('--- Outgoing Response! ---');
|
|
782
|
+
console.log('URL:', url);
|
|
783
|
+
console.log('Status:', status);
|
|
784
|
+
console.log('Payload:', this.formatDebugValue(payload));
|
|
785
|
+
console.log('--------------------------');
|
|
786
|
+
}
|
|
722
787
|
}
|
|
723
788
|
exports.ApiServer = ApiServer;
|
|
724
789
|
exports.default = ApiServer;
|
|
@@ -9,7 +9,7 @@ import jwt, { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
|
|
|
9
9
|
import { ApiModule } from './api-module.js';
|
|
10
10
|
import type { ApiAuthClass, ApiKey } from './api-module.js';
|
|
11
11
|
import type { AuthProviderModule } from './auth-module.js';
|
|
12
|
-
import type { AuthStorage, AuthTokenData } from './auth-storage.js';
|
|
12
|
+
import type { AuthStorage, AuthTokenData, AuthTokenMetadata } from './auth-storage.js';
|
|
13
13
|
export type { Application, Request, Response, NextFunction, Router } from 'express';
|
|
14
14
|
export type { Multer } from 'multer';
|
|
15
15
|
export type { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
|
|
@@ -35,7 +35,7 @@ interface JwtDecodeResult<T> {
|
|
|
35
35
|
data?: T;
|
|
36
36
|
error?: string;
|
|
37
37
|
}
|
|
38
|
-
export interface ApiTokenData extends JwtPayload {
|
|
38
|
+
export interface ApiTokenData extends JwtPayload, AuthTokenMetadata {
|
|
39
39
|
uid: unknown;
|
|
40
40
|
iat?: number;
|
|
41
41
|
exp?: number;
|
|
@@ -144,5 +144,7 @@ export declare class ApiServer {
|
|
|
144
144
|
private handle_request;
|
|
145
145
|
api<T extends ApiModule<any>>(module: T): this;
|
|
146
146
|
dumpRequest(apiReq: ApiRequest): void;
|
|
147
|
+
private formatDebugValue;
|
|
148
|
+
dumpResponse(apiReq: ApiRequest, payload: unknown, status: number): void;
|
|
147
149
|
}
|
|
148
150
|
export default ApiServer;
|
|
@@ -9,7 +9,7 @@ import jwt, { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
|
|
|
9
9
|
import { ApiModule } from './api-module.js';
|
|
10
10
|
import type { ApiAuthClass, ApiKey } from './api-module.js';
|
|
11
11
|
import type { AuthProviderModule } from './auth-module.js';
|
|
12
|
-
import type { AuthStorage, AuthTokenData } from './auth-storage.js';
|
|
12
|
+
import type { AuthStorage, AuthTokenData, AuthTokenMetadata } from './auth-storage.js';
|
|
13
13
|
export type { Application, Request, Response, NextFunction, Router } from 'express';
|
|
14
14
|
export type { Multer } from 'multer';
|
|
15
15
|
export type { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
|
|
@@ -35,7 +35,7 @@ interface JwtDecodeResult<T> {
|
|
|
35
35
|
data?: T;
|
|
36
36
|
error?: string;
|
|
37
37
|
}
|
|
38
|
-
export interface ApiTokenData extends JwtPayload {
|
|
38
|
+
export interface ApiTokenData extends JwtPayload, AuthTokenMetadata {
|
|
39
39
|
uid: unknown;
|
|
40
40
|
iat?: number;
|
|
41
41
|
exp?: number;
|
|
@@ -144,5 +144,7 @@ export declare class ApiServer {
|
|
|
144
144
|
private handle_request;
|
|
145
145
|
api<T extends ApiModule<any>>(module: T): this;
|
|
146
146
|
dumpRequest(apiReq: ApiRequest): void;
|
|
147
|
+
private formatDebugValue;
|
|
148
|
+
dumpResponse(apiReq: ApiRequest, payload: unknown, status: number): void;
|
|
147
149
|
}
|
|
148
150
|
export default ApiServer;
|
|
@@ -317,7 +317,7 @@ function fillConfig(config) {
|
|
|
317
317
|
accessCookie: config.accessCookie ?? 'dat',
|
|
318
318
|
refreshCookie: config.refreshCookie ?? 'drt',
|
|
319
319
|
accessExpiry: config.accessExpiry ?? 60 * 15,
|
|
320
|
-
refreshExpiry: config.refreshExpiry ?? 30 * 24 * 60 * 60
|
|
320
|
+
refreshExpiry: config.refreshExpiry ?? 30 * 24 * 60 * 60,
|
|
321
321
|
authApi: config.authApi ?? false,
|
|
322
322
|
devMode: config.devMode ?? false,
|
|
323
323
|
hydrateGetBody: config.hydrateGetBody ?? true,
|
|
@@ -620,18 +620,18 @@ export class ApiServer {
|
|
|
620
620
|
handle_request(handler, auth) {
|
|
621
621
|
return async (req, res, next) => {
|
|
622
622
|
void next;
|
|
623
|
+
const apiReq = {
|
|
624
|
+
server: this,
|
|
625
|
+
req,
|
|
626
|
+
res,
|
|
627
|
+
token: '',
|
|
628
|
+
tokenData: null,
|
|
629
|
+
getClientInfo: () => ensureClientInfo(apiReq),
|
|
630
|
+
getClientIp: () => ensureClientInfo(apiReq).ip,
|
|
631
|
+
getClientIpChain: () => ensureClientInfo(apiReq).ipchain
|
|
632
|
+
};
|
|
633
|
+
this.currReq = apiReq;
|
|
623
634
|
try {
|
|
624
|
-
const apiReq = {
|
|
625
|
-
server: this,
|
|
626
|
-
req,
|
|
627
|
-
res,
|
|
628
|
-
token: '',
|
|
629
|
-
tokenData: null,
|
|
630
|
-
getClientInfo: () => ensureClientInfo(apiReq),
|
|
631
|
-
getClientIp: () => ensureClientInfo(apiReq).ip,
|
|
632
|
-
getClientIpChain: () => ensureClientInfo(apiReq).ipchain
|
|
633
|
-
};
|
|
634
|
-
this.currReq = apiReq;
|
|
635
635
|
if (this.config.hydrateGetBody) {
|
|
636
636
|
hydrateGetBody(apiReq.req);
|
|
637
637
|
}
|
|
@@ -652,7 +652,11 @@ export class ApiServer {
|
|
|
652
652
|
throw new ApiError({ code: 500, message: 'Handler result must start with a numeric status code' });
|
|
653
653
|
}
|
|
654
654
|
const message = typeof rawMessage === 'string' ? rawMessage : 'Success';
|
|
655
|
-
|
|
655
|
+
const responsePayload = { code, message, data };
|
|
656
|
+
if (this.config.debug) {
|
|
657
|
+
this.dumpResponse(apiReq, responsePayload, code);
|
|
658
|
+
}
|
|
659
|
+
res.status(code).json(responsePayload);
|
|
656
660
|
}
|
|
657
661
|
catch (error) {
|
|
658
662
|
if (error instanceof ApiError || isApiErrorLike(error)) {
|
|
@@ -660,20 +664,28 @@ export class ApiServer {
|
|
|
660
664
|
const normalizedErrors = apiError.errors && typeof apiError.errors === 'object' && !Array.isArray(apiError.errors)
|
|
661
665
|
? apiError.errors
|
|
662
666
|
: {};
|
|
663
|
-
|
|
667
|
+
const errorPayload = {
|
|
664
668
|
code: apiError.code,
|
|
665
669
|
message: apiError.message,
|
|
666
670
|
data: apiError.data ?? null,
|
|
667
671
|
errors: normalizedErrors
|
|
668
|
-
}
|
|
672
|
+
};
|
|
673
|
+
if (this.config.debug) {
|
|
674
|
+
this.dumpResponse(apiReq, errorPayload, apiError.code);
|
|
675
|
+
}
|
|
676
|
+
res.status(apiError.code).json(errorPayload);
|
|
669
677
|
}
|
|
670
678
|
else {
|
|
671
|
-
|
|
679
|
+
const errorPayload = {
|
|
672
680
|
code: 500,
|
|
673
681
|
message: this.guessExceptionText(error),
|
|
674
682
|
data: null,
|
|
675
683
|
errors: {}
|
|
676
|
-
}
|
|
684
|
+
};
|
|
685
|
+
if (this.config.debug) {
|
|
686
|
+
this.dumpResponse(apiReq, errorPayload, 500);
|
|
687
|
+
}
|
|
688
|
+
res.status(500).json(errorPayload);
|
|
677
689
|
}
|
|
678
690
|
}
|
|
679
691
|
};
|
|
@@ -711,5 +723,58 @@ export class ApiServer {
|
|
|
711
723
|
console.log('Headers:', req.headers);
|
|
712
724
|
console.log('------------------------');
|
|
713
725
|
}
|
|
726
|
+
formatDebugValue(value, maxLength = 50, seen = new WeakSet()) {
|
|
727
|
+
if (value === null || value === undefined) {
|
|
728
|
+
return value;
|
|
729
|
+
}
|
|
730
|
+
if (typeof value === 'string') {
|
|
731
|
+
return value.length <= maxLength
|
|
732
|
+
? value
|
|
733
|
+
: `${value.slice(0, maxLength)}… [truncated ${value.length - maxLength} chars]`;
|
|
734
|
+
}
|
|
735
|
+
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
|
|
736
|
+
return value;
|
|
737
|
+
}
|
|
738
|
+
if (typeof value === 'symbol') {
|
|
739
|
+
return value.toString();
|
|
740
|
+
}
|
|
741
|
+
if (value instanceof Date) {
|
|
742
|
+
return value.toISOString();
|
|
743
|
+
}
|
|
744
|
+
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
|
|
745
|
+
return `<Buffer length=${value.length}>`;
|
|
746
|
+
}
|
|
747
|
+
if (typeof value === 'function') {
|
|
748
|
+
return `[Function ${value.name || 'anonymous'}]`;
|
|
749
|
+
}
|
|
750
|
+
if (typeof value === 'object') {
|
|
751
|
+
const obj = value;
|
|
752
|
+
if (seen.has(obj)) {
|
|
753
|
+
return '[Circular]';
|
|
754
|
+
}
|
|
755
|
+
seen.add(obj);
|
|
756
|
+
if (Array.isArray(value)) {
|
|
757
|
+
const arr = value.map((item) => this.formatDebugValue(item, maxLength, seen));
|
|
758
|
+
seen.delete(obj);
|
|
759
|
+
return arr;
|
|
760
|
+
}
|
|
761
|
+
const recordValue = value;
|
|
762
|
+
const entries = Object.entries(recordValue).reduce((acc, [key, val]) => {
|
|
763
|
+
acc[key] = this.formatDebugValue(val, maxLength, seen);
|
|
764
|
+
return acc;
|
|
765
|
+
}, {});
|
|
766
|
+
seen.delete(obj);
|
|
767
|
+
return entries;
|
|
768
|
+
}
|
|
769
|
+
return value;
|
|
770
|
+
}
|
|
771
|
+
dumpResponse(apiReq, payload, status) {
|
|
772
|
+
const url = apiReq.req.originalUrl || apiReq.req.url;
|
|
773
|
+
console.log('--- Outgoing Response! ---');
|
|
774
|
+
console.log('URL:', url);
|
|
775
|
+
console.log('Status:', status);
|
|
776
|
+
console.log('Payload:', this.formatDebugValue(payload));
|
|
777
|
+
console.log('--------------------------');
|
|
778
|
+
}
|
|
714
779
|
}
|
|
715
780
|
export default ApiServer;
|