@naturalcycles/backend-lib 9.81.0 → 9.83.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/adminMiddleware.d.ts +2 -2
- package/dist/admin/base.admin.service.d.ts +6 -6
- package/dist/auth/auth.service.d.ts +3 -3
- package/dist/db/httpDB.d.ts +3 -3
- package/dist/db/httpDB.js +2 -0
- package/dist/express/getDefaultRouter.d.ts +1 -1
- package/dist/server/serverStatsMiddleware.js +3 -2
- package/dist/upload/getUploadedFiles.d.ts +1 -1
- package/package.json +2 -2
- package/src/admin/adminMiddleware.ts +2 -2
- package/src/admin/base.admin.service.ts +6 -6
- package/src/admin/secureHeaderMiddleware.ts +1 -1
- package/src/auth/auth.service.ts +10 -10
- package/src/db/httpDB.ts +9 -3
- package/src/express/createDefaultApp.ts +4 -1
- package/src/express/getDefaultRouter.ts +3 -1
- package/src/server/compressionMiddleware.ts +1 -1
- package/src/server/serverStatsMiddleware.ts +3 -2
- package/src/upload/getUploadedFiles.ts +1 -1
- package/src/validation/validateRequest.util.ts +1 -1
|
@@ -31,7 +31,7 @@ export interface RequireAdminCfg {
|
|
|
31
31
|
/**
|
|
32
32
|
* @deprecated Use `AuthMiddleware`.
|
|
33
33
|
*/
|
|
34
|
-
export type AdminMiddleware = (reqPermissions?: string[], cfg?: RequireAdminCfg) => BackendRequestHandler;
|
|
34
|
+
export type AdminMiddleware = (reqPermissions?: readonly string[], cfg?: RequireAdminCfg) => BackendRequestHandler;
|
|
35
35
|
/**
|
|
36
36
|
* @deprecated Use `AuthService.getLoginRedirectMiddleware`.
|
|
37
37
|
*/
|
|
@@ -44,7 +44,7 @@ export declare function createAdminMiddleware(adminService: BaseAdminService, cf
|
|
|
44
44
|
*
|
|
45
45
|
* @deprecated Use `AuthService.getLoginRedirectMiddleware`.
|
|
46
46
|
*/
|
|
47
|
-
export declare function requireAdminPermissions(adminService: BaseAdminService, reqPermissions?: string[], cfg?: RequireAdminCfg): BackendRequestHandler;
|
|
47
|
+
export declare function requireAdminPermissions(adminService: BaseAdminService, reqPermissions?: readonly string[], cfg?: RequireAdminCfg): BackendRequestHandler;
|
|
48
48
|
/**
|
|
49
49
|
* @deprecated Use `loginHtmlHandler`.
|
|
50
50
|
*/
|
|
@@ -19,8 +19,8 @@ export declare class BaseAdminService {
|
|
|
19
19
|
* Returns AdminInfo if it has all required permissions.
|
|
20
20
|
* Otherwise returns undefined
|
|
21
21
|
*/
|
|
22
|
-
hasPermissions(req: BackendRequest, reqPermissions?: string[], meta?: AnyObject): Promise<AdminInfo | undefined>;
|
|
23
|
-
requirePermissions(req: BackendRequest, reqPermissions?: string[], meta?: AnyObject, andComparison?: boolean): Promise<AdminInfo>;
|
|
22
|
+
hasPermissions(req: BackendRequest, reqPermissions?: readonly string[], meta?: AnyObject): Promise<AdminInfo | undefined>;
|
|
23
|
+
requirePermissions(req: BackendRequest, reqPermissions?: readonly string[], meta?: AnyObject, andComparison?: boolean): Promise<AdminInfo>;
|
|
24
24
|
/**
|
|
25
25
|
* Doesn't check permissions, nor `authEnabled`. See `checkPermissions`.
|
|
26
26
|
*/
|
|
@@ -28,7 +28,7 @@ export declare class BaseAdminService {
|
|
|
28
28
|
/**
|
|
29
29
|
* Doesn't log or throw. See `requireGranted`.
|
|
30
30
|
*/
|
|
31
|
-
checkPermissions(admin: ResolvedAdmin, reqPermissions?: string[], opt?: CheckPermissionsOptions): CheckPermissionsResult;
|
|
31
|
+
checkPermissions(admin: ResolvedAdmin, reqPermissions?: readonly string[], opt?: CheckPermissionsOptions): CheckPermissionsResult;
|
|
32
32
|
/**
|
|
33
33
|
* Throws 401 if there's no email, 403 if not granted.
|
|
34
34
|
*/
|
|
@@ -51,7 +51,7 @@ export declare class BaseAdminService {
|
|
|
51
51
|
/**
|
|
52
52
|
* To be extended.
|
|
53
53
|
*/
|
|
54
|
-
protected onPermissionCheck(req: BackendRequest, email: string, reqPermissions: string[], required: boolean, granted: boolean, meta?: AnyObject): Promise<void>;
|
|
54
|
+
protected onPermissionCheck(req: BackendRequest, email: string, reqPermissions: readonly string[], required: boolean, granted: boolean, meta?: AnyObject): Promise<void>;
|
|
55
55
|
/**
|
|
56
56
|
* Install it on POST /admin/login url
|
|
57
57
|
*
|
|
@@ -113,7 +113,7 @@ export interface CheckPermissionsResult {
|
|
|
113
113
|
email?: string;
|
|
114
114
|
isAdmin: boolean;
|
|
115
115
|
granted: boolean;
|
|
116
|
-
reqPermissions: string[];
|
|
117
|
-
grantedPermissions: string[];
|
|
116
|
+
reqPermissions: readonly string[];
|
|
117
|
+
grantedPermissions: readonly string[];
|
|
118
118
|
authDisabled: boolean;
|
|
119
119
|
}
|
|
@@ -27,7 +27,7 @@ export declare class AuthService<CTX = AnyObject> {
|
|
|
27
27
|
* Returns the granted permissions (all of the subject's if alwaysAuthorized),
|
|
28
28
|
* or `{ subject: 'authDisabled', permissions: [] }` if auth is disabled.
|
|
29
29
|
*/
|
|
30
|
-
require(resolved: ResolvedAuth | undefined, reqPermissions?: string[], opt?: AuthCheckOptions<CTX>): PermissionInfo;
|
|
30
|
+
require(resolved: ResolvedAuth | undefined, reqPermissions?: readonly string[], opt?: AuthCheckOptions<CTX>): PermissionInfo;
|
|
31
31
|
private checkSubject;
|
|
32
32
|
private assertAuthenticated;
|
|
33
33
|
private authRequiredData;
|
|
@@ -67,9 +67,9 @@ export interface AuthCheck {
|
|
|
67
67
|
subject: string;
|
|
68
68
|
granted: boolean;
|
|
69
69
|
alwaysAuthorized: boolean;
|
|
70
|
-
reqPermissions: string[];
|
|
70
|
+
reqPermissions: readonly string[];
|
|
71
71
|
/** The granted permissions, or reqPermissions if denied */
|
|
72
|
-
checkedPermissions: string[];
|
|
72
|
+
checkedPermissions: readonly string[];
|
|
73
73
|
}
|
|
74
74
|
export interface AuthLoginRedirectMiddlewareCfg<CTX> extends AuthMiddlewareCfg<CTX> {
|
|
75
75
|
loginHtmlPath: string;
|
package/dist/db/httpDB.d.ts
CHANGED
|
@@ -19,10 +19,10 @@ export declare class HttpDB extends BaseCommonDB implements CommonDB {
|
|
|
19
19
|
getTables(): Promise<string[]>;
|
|
20
20
|
getTableSchema<ROW extends ObjectWithId>(table: string): Promise<JsonSchema<ROW>>;
|
|
21
21
|
resetCache(table?: string): Promise<void>;
|
|
22
|
-
getByIds<ROW extends ObjectWithId>(table: string, ids: ROW['id'][], opt?: CommonDBOptions): Promise<ROW[]>;
|
|
22
|
+
getByIds<ROW extends ObjectWithId>(table: string, ids: readonly ROW['id'][], opt?: CommonDBOptions): Promise<ROW[]>;
|
|
23
23
|
runQuery<ROW extends ObjectWithId>(query: DBQuery<ROW>, opt?: CommonDBOptions): Promise<RunQueryResult<ROW>>;
|
|
24
24
|
runQueryCount<ROW extends ObjectWithId>(query: DBQuery<ROW>, opt?: CommonDBOptions): Promise<number>;
|
|
25
|
-
saveBatch<ROW extends ObjectWithId>(table: string, rows: ROW[], opt?: CommonDBSaveOptions<ROW>): Promise<void>;
|
|
26
|
-
deleteByIds(table: string, ids: string[], opt?: CommonDBOptions): Promise<number>;
|
|
25
|
+
saveBatch<ROW extends ObjectWithId>(table: string, rows: readonly ROW[], opt?: CommonDBSaveOptions<ROW>): Promise<void>;
|
|
26
|
+
deleteByIds(table: string, ids: readonly string[], opt?: CommonDBOptions): Promise<number>;
|
|
27
27
|
deleteByQuery<ROW extends ObjectWithId>(query: DBQuery<ROW>, opt?: CommonDBOptions): Promise<number>;
|
|
28
28
|
}
|
package/dist/db/httpDB.js
CHANGED
|
@@ -2,4 +2,4 @@ import type { BackendRequestHandler, BackendRouter } from '../server/server.mode
|
|
|
2
2
|
/**
|
|
3
3
|
* Convenience method.
|
|
4
4
|
*/
|
|
5
|
-
export declare function getDefaultRouter(defaultHandlers?: BackendRequestHandler[]): BackendRouter;
|
|
5
|
+
export declare function getDefaultRouter(defaultHandlers?: readonly BackendRequestHandler[]): BackendRouter;
|
|
@@ -2,7 +2,7 @@ import { _mb } from '@naturalcycles/js-lib';
|
|
|
2
2
|
import { _sum } from '@naturalcycles/js-lib/array/array.util.js';
|
|
3
3
|
import { _sortBy } from '@naturalcycles/js-lib/array/sort.js';
|
|
4
4
|
import { _ms } from '@naturalcycles/js-lib/datetime/time.util.js';
|
|
5
|
-
import {
|
|
5
|
+
import { _percentiles } from '@naturalcycles/js-lib/math/math.util.js';
|
|
6
6
|
import { NumberStack } from '@naturalcycles/js-lib/math/stack.util.js';
|
|
7
7
|
import { _get, _mapValues } from '@naturalcycles/js-lib/object/object.util.js';
|
|
8
8
|
import { htmlEscape } from '@naturalcycles/js-lib/string/escape.js';
|
|
@@ -31,6 +31,7 @@ export const serverStatsHTMLHandler = (req, res) => {
|
|
|
31
31
|
s.pc = _mapValues(s.stack.percentiles(percentiles), (_k, v) => Math.round(v), { mutate: true });
|
|
32
32
|
});
|
|
33
33
|
const allLatencies = _stringMapValues(serverStatsMap).flatMap(s => s.stack.items);
|
|
34
|
+
const allPercentiles = _percentiles(allLatencies, percentiles);
|
|
34
35
|
const all2xx = _sum(_stringMapValues(serverStatsMap).flatMap(s => s['2xx']));
|
|
35
36
|
const all4xx = _sum(_stringMapValues(serverStatsMap).flatMap(s => s['4xx']));
|
|
36
37
|
const all5xx = _sum(_stringMapValues(serverStatsMap).flatMap(s => s['5xx']));
|
|
@@ -55,7 +56,7 @@ export const serverStatsHTMLHandler = (req, res) => {
|
|
|
55
56
|
`<td align="right"><pre>${all2xx}</pre></td>`,
|
|
56
57
|
`<td align="right"><pre>${all4xx}</pre></td>`,
|
|
57
58
|
`<td align="right"><pre>${all5xx}</pre></td>`,
|
|
58
|
-
...percentiles.map(pc => `<td align="right"><pre>${Math.round(
|
|
59
|
+
...percentiles.map(pc => `<td align="right"><pre>${Math.round(allPercentiles[pc])}</pre></td>`),
|
|
59
60
|
`</tr>`,
|
|
60
61
|
..._sortBy(_stringMapEntries(serverStatsMap), ([_, stat]) => _get(stat, sortBy), {
|
|
61
62
|
dir: asc ? 'asc' : 'desc',
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/backend-lib",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "9.
|
|
4
|
+
"version": "9.83.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@naturalcycles/db-lib": "^10",
|
|
7
7
|
"@naturalcycles/js-lib": "^15",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"directory": "packages/backend-lib"
|
|
77
77
|
},
|
|
78
78
|
"engines": {
|
|
79
|
-
"node": ">=
|
|
79
|
+
"node": ">=26.12.1"
|
|
80
80
|
},
|
|
81
81
|
"description": "Standard library for making Express.js based backend services",
|
|
82
82
|
"author": "Natural Cycles Team",
|
|
@@ -42,7 +42,7 @@ export interface RequireAdminCfg {
|
|
|
42
42
|
* @deprecated Use `AuthMiddleware`.
|
|
43
43
|
*/
|
|
44
44
|
export type AdminMiddleware = (
|
|
45
|
-
reqPermissions?: string[],
|
|
45
|
+
reqPermissions?: readonly string[],
|
|
46
46
|
cfg?: RequireAdminCfg,
|
|
47
47
|
) => BackendRequestHandler
|
|
48
48
|
|
|
@@ -70,7 +70,7 @@ export function createAdminMiddleware(
|
|
|
70
70
|
*/
|
|
71
71
|
export function requireAdminPermissions(
|
|
72
72
|
adminService: BaseAdminService,
|
|
73
|
-
reqPermissions: string[] = [],
|
|
73
|
+
reqPermissions: readonly string[] = [],
|
|
74
74
|
cfg: RequireAdminCfg = {},
|
|
75
75
|
): BackendRequestHandler {
|
|
76
76
|
const {
|
|
@@ -70,7 +70,7 @@ export class BaseAdminService {
|
|
|
70
70
|
*/
|
|
71
71
|
async hasPermissions(
|
|
72
72
|
req: BackendRequest,
|
|
73
|
-
reqPermissions: string[] = [],
|
|
73
|
+
reqPermissions: readonly string[] = [],
|
|
74
74
|
meta: AnyObject = {},
|
|
75
75
|
): Promise<AdminInfo | undefined> {
|
|
76
76
|
if (!this.cfg.authEnabled) return adminInfoDisabled()
|
|
@@ -93,7 +93,7 @@ export class BaseAdminService {
|
|
|
93
93
|
|
|
94
94
|
async requirePermissions(
|
|
95
95
|
req: BackendRequest,
|
|
96
|
-
reqPermissions: string[] = [],
|
|
96
|
+
reqPermissions: readonly string[] = [],
|
|
97
97
|
meta: AnyObject = {},
|
|
98
98
|
andComparison = true,
|
|
99
99
|
): Promise<AdminInfo> {
|
|
@@ -128,7 +128,7 @@ export class BaseAdminService {
|
|
|
128
128
|
*/
|
|
129
129
|
checkPermissions(
|
|
130
130
|
admin: ResolvedAdmin,
|
|
131
|
-
reqPermissions: string[] = [],
|
|
131
|
+
reqPermissions: readonly string[] = [],
|
|
132
132
|
opt: CheckPermissionsOptions = {},
|
|
133
133
|
): CheckPermissionsResult {
|
|
134
134
|
const { andComparison = true } = opt
|
|
@@ -254,7 +254,7 @@ export class BaseAdminService {
|
|
|
254
254
|
protected async onPermissionCheck(
|
|
255
255
|
req: BackendRequest,
|
|
256
256
|
email: string,
|
|
257
|
-
reqPermissions: string[],
|
|
257
|
+
reqPermissions: readonly string[],
|
|
258
258
|
required: boolean,
|
|
259
259
|
granted: boolean,
|
|
260
260
|
meta: AnyObject = {},
|
|
@@ -366,7 +366,7 @@ export interface CheckPermissionsResult {
|
|
|
366
366
|
email?: string
|
|
367
367
|
isAdmin: boolean
|
|
368
368
|
granted: boolean
|
|
369
|
-
reqPermissions: string[]
|
|
370
|
-
grantedPermissions: string[]
|
|
369
|
+
reqPermissions: readonly string[]
|
|
370
|
+
grantedPermissions: readonly string[]
|
|
371
371
|
authDisabled: boolean
|
|
372
372
|
}
|
|
@@ -34,7 +34,7 @@ export function createSecureHeaderMiddleware(cfg: SecureHeaderMiddlewareCfg): Ad
|
|
|
34
34
|
|
|
35
35
|
function requireSecureHeaderOrAdmin(
|
|
36
36
|
cfg: SecureHeaderMiddlewareCfg,
|
|
37
|
-
reqPermissions?: string[],
|
|
37
|
+
reqPermissions?: readonly string[],
|
|
38
38
|
): BackendRequestHandler {
|
|
39
39
|
const { secureHeaderKey = 'Authorization', secureHeaderValue } = cfg
|
|
40
40
|
|
package/src/auth/auth.service.ts
CHANGED
|
@@ -34,7 +34,7 @@ export class AuthService<CTX = AnyObject> {
|
|
|
34
34
|
|
|
35
35
|
private createHandler(
|
|
36
36
|
cfg: AuthMiddlewareCfg<CTX>,
|
|
37
|
-
reqPermissions: string[] = [],
|
|
37
|
+
reqPermissions: readonly string[] = [],
|
|
38
38
|
loginHref?: string,
|
|
39
39
|
): BackendRequestHandler {
|
|
40
40
|
const {
|
|
@@ -100,7 +100,7 @@ export class AuthService<CTX = AnyObject> {
|
|
|
100
100
|
*/
|
|
101
101
|
require(
|
|
102
102
|
resolved: ResolvedAuth | undefined,
|
|
103
|
-
reqPermissions: string[] = [],
|
|
103
|
+
reqPermissions: readonly string[] = [],
|
|
104
104
|
opt: AuthCheckOptions<CTX> = {},
|
|
105
105
|
): PermissionInfo {
|
|
106
106
|
if (!this.cfg.authEnabled) return { subject: 'authDisabled', permissions: [] }
|
|
@@ -120,9 +120,9 @@ export class AuthService<CTX = AnyObject> {
|
|
|
120
120
|
|
|
121
121
|
private checkSubject(
|
|
122
122
|
resolved: AuthenticatedAuth,
|
|
123
|
-
reqPermissions: string[],
|
|
123
|
+
reqPermissions: readonly string[],
|
|
124
124
|
opt: AuthCheckOptions<CTX>,
|
|
125
|
-
): string[] | undefined {
|
|
125
|
+
): readonly string[] | undefined {
|
|
126
126
|
const { subject, permissions, alwaysAuthorized = false } = resolved
|
|
127
127
|
const { andComparison = true, ctx } = opt
|
|
128
128
|
|
|
@@ -160,8 +160,8 @@ export class AuthService<CTX = AnyObject> {
|
|
|
160
160
|
|
|
161
161
|
private assertGranted(
|
|
162
162
|
subject: string,
|
|
163
|
-
grantedPermissions: string[] | undefined,
|
|
164
|
-
reqPermissions: string[],
|
|
163
|
+
grantedPermissions: readonly string[] | undefined,
|
|
164
|
+
reqPermissions: readonly string[],
|
|
165
165
|
): asserts grantedPermissions is string[] {
|
|
166
166
|
_assert(grantedPermissions, `Permissions required: [${reqPermissions.join(', ')}]`, {
|
|
167
167
|
permissionsRequired: reqPermissions,
|
|
@@ -175,8 +175,8 @@ export class AuthService<CTX = AnyObject> {
|
|
|
175
175
|
* undefined - not granted.
|
|
176
176
|
*/
|
|
177
177
|
private getGrantedPermissions(
|
|
178
|
-
permissions: string[] | undefined,
|
|
179
|
-
reqPermissions: string[],
|
|
178
|
+
permissions: readonly string[] | undefined,
|
|
179
|
+
reqPermissions: readonly string[],
|
|
180
180
|
andComparison: boolean,
|
|
181
181
|
): string[] | undefined {
|
|
182
182
|
if (!permissions) return
|
|
@@ -242,9 +242,9 @@ export interface AuthCheck {
|
|
|
242
242
|
subject: string
|
|
243
243
|
granted: boolean
|
|
244
244
|
alwaysAuthorized: boolean
|
|
245
|
-
reqPermissions: string[]
|
|
245
|
+
reqPermissions: readonly string[]
|
|
246
246
|
/** The granted permissions, or reqPermissions if denied */
|
|
247
|
-
checkedPermissions: string[]
|
|
247
|
+
checkedPermissions: readonly string[]
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
export interface AuthLoginRedirectMiddlewareCfg<CTX> extends AuthMiddlewareCfg<CTX> {
|
package/src/db/httpDB.ts
CHANGED
|
@@ -30,6 +30,8 @@ export class HttpDB extends BaseCommonDB implements CommonDB {
|
|
|
30
30
|
transactions: false,
|
|
31
31
|
patchByQuery: false,
|
|
32
32
|
increment: false,
|
|
33
|
+
// httpDBRequestHandler's options schema strips readAt
|
|
34
|
+
timeMachine: false,
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
constructor(public cfg: HttpDBCfg) {
|
|
@@ -61,7 +63,7 @@ export class HttpDB extends BaseCommonDB implements CommonDB {
|
|
|
61
63
|
|
|
62
64
|
override async getByIds<ROW extends ObjectWithId>(
|
|
63
65
|
table: string,
|
|
64
|
-
ids: ROW['id'][],
|
|
66
|
+
ids: readonly ROW['id'][],
|
|
65
67
|
opt?: CommonDBOptions,
|
|
66
68
|
): Promise<ROW[]> {
|
|
67
69
|
return await this.fetcher.put(`getByIds`, {
|
|
@@ -99,7 +101,7 @@ export class HttpDB extends BaseCommonDB implements CommonDB {
|
|
|
99
101
|
|
|
100
102
|
override async saveBatch<ROW extends ObjectWithId>(
|
|
101
103
|
table: string,
|
|
102
|
-
rows: ROW[],
|
|
104
|
+
rows: readonly ROW[],
|
|
103
105
|
opt?: CommonDBSaveOptions<ROW>,
|
|
104
106
|
): Promise<void> {
|
|
105
107
|
await this.fetcher.putVoid(`saveBatch`, {
|
|
@@ -111,7 +113,11 @@ export class HttpDB extends BaseCommonDB implements CommonDB {
|
|
|
111
113
|
})
|
|
112
114
|
}
|
|
113
115
|
|
|
114
|
-
override async deleteByIds(
|
|
116
|
+
override async deleteByIds(
|
|
117
|
+
table: string,
|
|
118
|
+
ids: readonly string[],
|
|
119
|
+
opt?: CommonDBOptions,
|
|
120
|
+
): Promise<number> {
|
|
115
121
|
return await this.fetcher.put(`deleteByIds`, {
|
|
116
122
|
json: {
|
|
117
123
|
table,
|
|
@@ -147,7 +147,10 @@ export async function createDefaultApp(cfg: DefaultAppCfg): Promise<BackendAppli
|
|
|
147
147
|
return app
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
function useHandlers(
|
|
150
|
+
function useHandlers(
|
|
151
|
+
app: BackendApplication,
|
|
152
|
+
handlers: readonly BackendRequestHandlerCfg[] = [],
|
|
153
|
+
): void {
|
|
151
154
|
handlers
|
|
152
155
|
.map<BackendRequestHandlerWithPath>(cfg => {
|
|
153
156
|
if (typeof cfg === 'function') {
|
|
@@ -4,7 +4,9 @@ import type { BackendRequestHandler, BackendRouter } from '../server/server.mode
|
|
|
4
4
|
/**
|
|
5
5
|
* Convenience method.
|
|
6
6
|
*/
|
|
7
|
-
export function getDefaultRouter(
|
|
7
|
+
export function getDefaultRouter(
|
|
8
|
+
defaultHandlers: readonly BackendRequestHandler[] = [],
|
|
9
|
+
): BackendRouter {
|
|
8
10
|
const router = Router()
|
|
9
11
|
|
|
10
12
|
// Use default handlers
|
|
@@ -413,7 +413,7 @@ function compressSync(method: string, body: Buffer, opts: CodecOptions): Buffer
|
|
|
413
413
|
function addListeners(
|
|
414
414
|
stream: any,
|
|
415
415
|
on: (type: string, listener: (...args: any[]) => void) => void,
|
|
416
|
-
listeners: [string, (...args: any[]) => void][],
|
|
416
|
+
listeners: readonly [string, (...args: any[]) => void][],
|
|
417
417
|
): void {
|
|
418
418
|
for (const [type, listener] of listeners) {
|
|
419
419
|
on.call(stream, type, listener)
|
|
@@ -2,7 +2,7 @@ import { _mb } from '@naturalcycles/js-lib'
|
|
|
2
2
|
import { _sum } from '@naturalcycles/js-lib/array/array.util.js'
|
|
3
3
|
import { _sortBy } from '@naturalcycles/js-lib/array/sort.js'
|
|
4
4
|
import { _ms } from '@naturalcycles/js-lib/datetime/time.util.js'
|
|
5
|
-
import {
|
|
5
|
+
import { _percentiles } from '@naturalcycles/js-lib/math/math.util.js'
|
|
6
6
|
import { NumberStack } from '@naturalcycles/js-lib/math/stack.util.js'
|
|
7
7
|
import { _get, _mapValues } from '@naturalcycles/js-lib/object/object.util.js'
|
|
8
8
|
import { htmlEscape } from '@naturalcycles/js-lib/string/escape.js'
|
|
@@ -51,6 +51,7 @@ export const serverStatsHTMLHandler: BackendRequestHandler = (req, res) => {
|
|
|
51
51
|
s.pc = _mapValues(s.stack.percentiles(percentiles), (_k, v) => Math.round(v), { mutate: true })
|
|
52
52
|
})
|
|
53
53
|
const allLatencies = _stringMapValues(serverStatsMap).flatMap(s => s.stack.items)
|
|
54
|
+
const allPercentiles = _percentiles(allLatencies, percentiles)
|
|
54
55
|
const all2xx = _sum(_stringMapValues(serverStatsMap).flatMap(s => s['2xx']))
|
|
55
56
|
const all4xx = _sum(_stringMapValues(serverStatsMap).flatMap(s => s['4xx']))
|
|
56
57
|
const all5xx = _sum(_stringMapValues(serverStatsMap).flatMap(s => s['5xx']))
|
|
@@ -79,7 +80,7 @@ export const serverStatsHTMLHandler: BackendRequestHandler = (req, res) => {
|
|
|
79
80
|
`<td align="right"><pre>${all4xx}</pre></td>`,
|
|
80
81
|
`<td align="right"><pre>${all5xx}</pre></td>`,
|
|
81
82
|
...percentiles.map(
|
|
82
|
-
pc => `<td align="right"><pre>${Math.round(
|
|
83
|
+
pc => `<td align="right"><pre>${Math.round(allPercentiles[pc]!)}</pre></td>`,
|
|
83
84
|
),
|
|
84
85
|
`</tr>`,
|
|
85
86
|
..._sortBy(_stringMapEntries(serverStatsMap), ([_, stat]) => _get(stat, sortBy) as string, {
|
|
@@ -31,7 +31,7 @@ const REDACTED = 'REDACTED'
|
|
|
31
31
|
/**
|
|
32
32
|
* Mutates error
|
|
33
33
|
*/
|
|
34
|
-
function redact(redactPaths: string[], obj: any, error: Error): void {
|
|
34
|
+
function redact(redactPaths: readonly string[], obj: any, error: Error): void {
|
|
35
35
|
redactPaths
|
|
36
36
|
.map(path => _get(obj, path) as string)
|
|
37
37
|
.filter(Boolean)
|