@tramvai/module-server 2.21.0 → 2.24.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/lib/modules/papi/api/index.d.ts +3 -2
- package/lib/modules/papi/server/executor.d.ts +11 -0
- package/lib/modules/papi/server/fileApi.d.ts +2 -0
- package/lib/server/express-compatibility.d.ts +1 -2
- package/lib/server/server.d.ts +1 -1
- package/lib/server/webApp.d.ts +1 -1
- package/lib/server.es.js +142 -59
- package/lib/server.js +134 -51
- package/package.json +12 -12
- package/lib/modules/papi/fileApi.d.ts +0 -8
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
1
2
|
import type { Papi } from '@tramvai/papi';
|
|
2
|
-
import type express from 'express';
|
|
3
3
|
import type { DI_TOKEN } from '@tramvai/core';
|
|
4
4
|
import type { LOGGER_TOKEN } from '@tramvai/tokens-common';
|
|
5
5
|
export interface CreateOptions {
|
|
6
|
+
baseUrl: string;
|
|
6
7
|
di: typeof DI_TOKEN;
|
|
7
8
|
logger: typeof LOGGER_TOKEN;
|
|
8
9
|
}
|
|
9
|
-
export declare function createApi(papiList: Papi[], { di, logger }: CreateOptions):
|
|
10
|
+
export declare function createApi(rootApp: FastifyInstance, papiList: Papi[], { baseUrl, di, logger }: CreateOptions): void;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
export declare const papiExecutorProvider: import("@tinkoff/dippy").Provider<{
|
|
3
|
+
di: import("@tinkoff/dippy").BaseTokenInterface<import("@tinkoff/dippy").Container>;
|
|
4
|
+
logger: import("@tinkoff/dippy").BaseTokenInterface<import("@tramvai/tokens-common").LoggerFactory>;
|
|
5
|
+
fastifyRequest: import("@tinkoff/dippy").BaseTokenInterface<import("fastify").FastifyRequest<import("fastify/types/route").RouteGenericInterface, import("fastify").RawServerDefault, import("http").IncomingMessage, unknown, import("fastify").FastifyLoggerInstance> & {
|
|
6
|
+
cookies: Record<string, string>;
|
|
7
|
+
query: Record<string, string>;
|
|
8
|
+
}>;
|
|
9
|
+
requestManager: import("@tinkoff/dippy").BaseTokenInterface<import("@tramvai/tokens-common").RequestManager>;
|
|
10
|
+
responseManager: import("@tinkoff/dippy").BaseTokenInterface<import("@tramvai/tokens-common").ResponseManager>;
|
|
11
|
+
}, import("@tinkoff/dippy").BaseTokenInterface<import("@tramvai/tokens-server-private").PapiExecutor>>;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Fork of https://github.com/fastify/fastify-express
|
|
3
3
|
*/
|
|
4
|
-
/// <reference types="node" />
|
|
5
4
|
import express from 'express';
|
|
6
5
|
import type { FastifyPluginCallback } from 'fastify';
|
|
7
6
|
declare module 'fastify' {
|
|
@@ -21,5 +20,5 @@ interface Options {
|
|
|
21
20
|
instance: express.Application;
|
|
22
21
|
};
|
|
23
22
|
}
|
|
24
|
-
export declare const fastifyExpressCompatibility: FastifyPluginCallback<Options, import("
|
|
23
|
+
export declare const fastifyExpressCompatibility: FastifyPluginCallback<Options, import("fastify").RawServerDefault>;
|
|
25
24
|
export {};
|
package/lib/server/server.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import http from 'http';
|
|
|
3
3
|
import type { SERVER_TOKEN } from '@tramvai/tokens-server';
|
|
4
4
|
import type { LOGGER_TOKEN } from '@tramvai/tokens-common';
|
|
5
5
|
import type { ENV_MANAGER_TOKEN } from '@tramvai/module-environment';
|
|
6
|
-
export declare const serverFactory: () => http.Server
|
|
6
|
+
export declare const serverFactory: () => http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
|
|
7
7
|
export declare const serverListenCommand: ({ server, logger, envManager, }: {
|
|
8
8
|
server: typeof SERVER_TOKEN;
|
|
9
9
|
logger: typeof LOGGER_TOKEN;
|
package/lib/server/webApp.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { WEB_FASTIFY_APP_TOKEN, WEB_FASTIFY_APP_AFTER_INIT_TOKEN, WEB_FASTI
|
|
|
6
6
|
import type { ExtractDependencyType } from '@tinkoff/dippy';
|
|
7
7
|
export declare const webAppFactory: ({ server }: {
|
|
8
8
|
server: typeof SERVER_TOKEN;
|
|
9
|
-
}) => import("fastify").FastifyInstance<import("http").Server
|
|
9
|
+
}) => import("fastify").FastifyInstance<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, import("fastify").FastifyLoggerInstance> & PromiseLike<import("fastify").FastifyInstance<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, import("fastify").FastifyLoggerInstance>>;
|
|
10
10
|
export declare const webAppExpressFactory: ({ webApp }: {
|
|
11
11
|
webApp: typeof WEB_FASTIFY_APP_TOKEN;
|
|
12
12
|
}) => import("express-serve-static-core").Express;
|
package/lib/server.es.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { __decorate } from 'tslib';
|
|
2
2
|
import { setDefaultResultOrder } from 'dns';
|
|
3
3
|
import EventEmitter$1, { EventEmitter } from 'events';
|
|
4
|
-
import { Scope, APP_INFO_TOKEN, Module, provide as provide$1, DI_TOKEN, COMMAND_LINE_RUNNER_TOKEN, commandLineListTokens, createToken } from '@tramvai/core';
|
|
5
|
-
import { SERVER_MODULE_PAPI_PUBLIC_ROUTE, SERVER_MODULE_PAPI_PUBLIC_URL, SERVER_MODULE_PAPI_PRIVATE_URL,
|
|
4
|
+
import { Scope, APP_INFO_TOKEN, Module, provide as provide$1, DI_TOKEN as DI_TOKEN$1, COMMAND_LINE_RUNNER_TOKEN, commandLineListTokens, createToken } from '@tramvai/core';
|
|
5
|
+
import { SERVER_MODULE_PAPI_PUBLIC_ROUTE, SERVER_MODULE_PAPI_PUBLIC_URL, SERVER_MODULE_PAPI_PRIVATE_URL, SERVER_MODULE_PAPI_PRIVATE_ROUTE, SERVER_MODULE_STATICS_OPTIONS, SERVER_TOKEN, READINESS_PROBE_TOKEN, LIVENESS_PROBE_TOKEN, UTILITY_SERVER_PATHS, WEB_APP_BEFORE_INIT_TOKEN, WEB_APP_TOKEN, PROXY_CONFIG_TOKEN, DEPENDENCIES_VERSION_FILTER_TOKEN, UTILITY_SERVER_PORT_TOKEN, WEB_APP_INIT_TOKEN, WEB_APP_AFTER_INIT_TOKEN, WEB_APP_LIMITER_TOKEN } from '@tramvai/tokens-server';
|
|
6
6
|
export * from '@tramvai/tokens-server';
|
|
7
|
-
import { WEB_FASTIFY_APP_BEFORE_INIT_TOKEN, UTILITY_WEB_FASTIFY_APP_TOKEN, UTILITY_SERVER_TOKEN, SERVER_FACTORY_TOKEN, WEB_FASTIFY_APP_TOKEN, WEB_FASTIFY_APP_FACTORY_TOKEN, WEB_FASTIFY_APP_INIT_TOKEN, WEB_FASTIFY_APP_AFTER_INIT_TOKEN, WEB_FASTIFY_APP_METRICS_TOKEN, WEB_FASTIFY_APP_LIMITER_TOKEN, WEB_FASTIFY_APP_BEFORE_ERROR_TOKEN, WEB_FASTIFY_APP_PROCESS_ERROR_TOKEN, WEB_FASTIFY_APP_AFTER_ERROR_TOKEN } from '@tramvai/tokens-server-private';
|
|
8
|
-
import { ROOT_EXECUTION_CONTEXT_TOKEN, REQUEST, RESPONSE, FASTIFY_REQUEST, FASTIFY_RESPONSE, RESPONSE_MANAGER_TOKEN, LOGGER_TOKEN, ENV_MANAGER_TOKEN, ENV_USED_TOKEN, EXECUTION_CONTEXT_MANAGER_TOKEN } from '@tramvai/tokens-common';
|
|
7
|
+
import { PAPI_EXECUTOR, WEB_FASTIFY_APP_BEFORE_INIT_TOKEN, UTILITY_WEB_FASTIFY_APP_TOKEN, UTILITY_SERVER_TOKEN, SERVER_FACTORY_TOKEN, WEB_FASTIFY_APP_TOKEN, WEB_FASTIFY_APP_FACTORY_TOKEN, WEB_FASTIFY_APP_INIT_TOKEN, WEB_FASTIFY_APP_AFTER_INIT_TOKEN, WEB_FASTIFY_APP_METRICS_TOKEN, WEB_FASTIFY_APP_LIMITER_TOKEN, WEB_FASTIFY_APP_BEFORE_ERROR_TOKEN, WEB_FASTIFY_APP_PROCESS_ERROR_TOKEN, WEB_FASTIFY_APP_AFTER_ERROR_TOKEN } from '@tramvai/tokens-server-private';
|
|
8
|
+
import { ROOT_EXECUTION_CONTEXT_TOKEN, REQUEST, RESPONSE, FASTIFY_REQUEST, FASTIFY_RESPONSE, RESPONSE_MANAGER_TOKEN, LOGGER_TOKEN, REQUEST_MANAGER_TOKEN, ENV_MANAGER_TOKEN, ENV_USED_TOKEN, EXECUTION_CONTEXT_MANAGER_TOKEN } from '@tramvai/tokens-common';
|
|
9
9
|
import { MetricsModule } from '@tramvai/module-metrics';
|
|
10
10
|
import { CacheWarmupModule } from '@tramvai/module-cache-warmup';
|
|
11
11
|
import http from 'http';
|
|
@@ -13,18 +13,18 @@ import fastify from 'fastify';
|
|
|
13
13
|
import express from 'express';
|
|
14
14
|
import { fastifyCookie } from '@fastify/cookie';
|
|
15
15
|
import fastifyFormBody from '@fastify/formbody';
|
|
16
|
-
import { provide, createChildContainer } from '@tinkoff/dippy';
|
|
16
|
+
import { provide, createChildContainer, Scope as Scope$1, DI_TOKEN } from '@tinkoff/dippy';
|
|
17
17
|
import fp from 'fastify-plugin';
|
|
18
18
|
import symbols from 'fastify/lib/symbols';
|
|
19
19
|
import isNil from '@tinkoff/utils/is/nil';
|
|
20
|
-
import { isRedirectFoundError, isNotFoundError, isHttpError } from '@tinkoff/errors';
|
|
20
|
+
import { isRedirectFoundError, isNotFoundError, isHttpError, HttpError } from '@tinkoff/errors';
|
|
21
21
|
import zlib from 'zlib';
|
|
22
22
|
import compression from 'compression';
|
|
23
23
|
import os from 'os';
|
|
24
24
|
import filterObj from '@tinkoff/utils/object/filter';
|
|
25
25
|
import flatten from '@tinkoff/utils/array/flatten';
|
|
26
26
|
import toArray from '@tinkoff/utils/array/toArray';
|
|
27
|
-
import {
|
|
27
|
+
import { getPapiParameters, isPapiMethod, createPapiMethod } from '@tramvai/papi';
|
|
28
28
|
import eachObj from '@tinkoff/utils/object/each';
|
|
29
29
|
import { resolve } from 'path';
|
|
30
30
|
import FastifyStatic from '@fastify/static';
|
|
@@ -188,6 +188,7 @@ const errorHandler = (app, { log, beforeError, processError, afterError, }) => {
|
|
|
188
188
|
|
|
189
189
|
const webAppFactory = ({ server }) => {
|
|
190
190
|
const app = fastify({
|
|
191
|
+
ignoreTrailingSlash: true,
|
|
191
192
|
serverFactory: (handler) => {
|
|
192
193
|
server.on('request', handler);
|
|
193
194
|
return server;
|
|
@@ -347,51 +348,102 @@ const xHeadersFactory = ({ app, envManager, appInfo, }) => {
|
|
|
347
348
|
};
|
|
348
349
|
};
|
|
349
350
|
|
|
350
|
-
function createApi(papiList, { di, logger }) {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
351
|
+
function createApi(rootApp, papiList, { baseUrl, di, logger }) {
|
|
352
|
+
const paths = new Set();
|
|
353
|
+
const papiLog = logger('papi');
|
|
354
|
+
rootApp.register(async (app) => {
|
|
355
|
+
await app.register(fastifyCookie);
|
|
356
|
+
await app.register(fastifyFormBody, { bodyLimit: 2097152 }); // 2mb
|
|
357
|
+
for (const papi of papiList) {
|
|
358
|
+
const papiParams = getPapiParameters(papi);
|
|
359
|
+
if (!papiParams) {
|
|
360
|
+
throw new Error(`papi should be created using createPapiMethod from @tramvai/papi,
|
|
361
|
+
got: ${JSON.stringify(papi)}`);
|
|
362
|
+
}
|
|
363
|
+
const { method, path, options } = papiParams;
|
|
364
|
+
const { timeout, schema } = options;
|
|
365
|
+
if (!path) {
|
|
366
|
+
throw new Error(`No path in papi handler, got: ${JSON.stringify(papi)}`);
|
|
367
|
+
}
|
|
368
|
+
const key = `${method} ${path}`;
|
|
369
|
+
if (paths.has(key)) {
|
|
370
|
+
throw new Error(`papi: route '${key}' already registered`);
|
|
371
|
+
}
|
|
372
|
+
paths.add(key);
|
|
373
|
+
const childLog = papiLog.child(`${papiParams.method}_${papiParams.path}`);
|
|
374
|
+
app[method](path, {
|
|
375
|
+
schema,
|
|
376
|
+
errorHandler: async (error, req, res) => {
|
|
363
377
|
var _a;
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
378
|
+
res.status(error.validation ? 400 : 503);
|
|
379
|
+
childLog.error(error);
|
|
380
|
+
return {
|
|
381
|
+
resultCode: 'INTERNAL_ERROR',
|
|
382
|
+
errorMessage: (_a = error.message) !== null && _a !== void 0 ? _a : 'internal error',
|
|
383
|
+
};
|
|
384
|
+
},
|
|
385
|
+
}, async (req, res) => {
|
|
386
|
+
const childDi = createChildContainer(di, [
|
|
387
|
+
{
|
|
388
|
+
provide: FASTIFY_REQUEST,
|
|
389
|
+
scope: Scope$1.REQUEST,
|
|
367
390
|
useValue: req,
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
provide:
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
provide: FASTIFY_RESPONSE,
|
|
394
|
+
scope: Scope$1.REQUEST,
|
|
371
395
|
useValue: res,
|
|
372
|
-
}
|
|
373
|
-
|
|
396
|
+
},
|
|
397
|
+
// TODO: remove deprecated after removing express compatibility layer
|
|
398
|
+
{
|
|
399
|
+
provide: REQUEST,
|
|
400
|
+
scope: Scope$1.REQUEST,
|
|
401
|
+
useValue: req.raw,
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
provide: RESPONSE,
|
|
405
|
+
scope: Scope$1.REQUEST,
|
|
406
|
+
useValue: res.raw,
|
|
407
|
+
},
|
|
408
|
+
]);
|
|
409
|
+
const papiExecutor = childDi.get(PAPI_EXECUTOR);
|
|
410
|
+
// TODO: use abortSignal
|
|
411
|
+
const payload = await Promise.race([
|
|
412
|
+
papiExecutor(papi),
|
|
413
|
+
new Promise((resolve, reject) => setTimeout(() => reject(new HttpError({ httpStatus: 503, message: 'Execution timeout' })), timeout)),
|
|
414
|
+
]);
|
|
415
|
+
const responseManager = childDi.get(RESPONSE_MANAGER_TOKEN);
|
|
416
|
+
res.headers(responseManager.getHeaders()).status(responseManager.getStatus());
|
|
417
|
+
if (res.sent) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
if (!payload && responseManager.getBody()) {
|
|
421
|
+
res.send(responseManager.getBody());
|
|
422
|
+
return res;
|
|
423
|
+
}
|
|
424
|
+
return {
|
|
425
|
+
resultCode: 'OK',
|
|
426
|
+
payload,
|
|
374
427
|
};
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
middlewares.error({ logger }),
|
|
379
|
-
]);
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
}, { prefix: baseUrl });
|
|
380
431
|
}
|
|
381
432
|
|
|
382
|
-
let
|
|
433
|
+
let papis;
|
|
383
434
|
try {
|
|
384
435
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
385
|
-
|
|
436
|
+
papis = require('@tramvai/cli/lib/external/api').default; // eslint-disable-line import/no-unresolved
|
|
386
437
|
}
|
|
387
438
|
catch (e) { }
|
|
388
439
|
const getFileApi = ({ logger }) => {
|
|
389
440
|
const log = logger('papi:fileApi');
|
|
390
441
|
const result = [];
|
|
391
|
-
eachObj((
|
|
392
|
-
|
|
393
|
-
if (!handler) {
|
|
442
|
+
eachObj(({ default: entry }, path) => {
|
|
443
|
+
if (!isPapiMethod(entry)) {
|
|
394
444
|
log.error({
|
|
445
|
+
path,
|
|
446
|
+
entry,
|
|
395
447
|
message: `Cannot resolve a papi handler.
|
|
396
448
|
Check that you are using file based papi right way by docs https://tramvai.dev/docs/how-to/how-create-papi#automatic-handler-creation
|
|
397
449
|
In case you have not added any file papi handler, consider renaming directory ./src/api (by default) to the other name to resolve conflicts with papi, or
|
|
@@ -399,14 +451,12 @@ change settings application.commands.build.options.serverApiDir in tramvai.json`
|
|
|
399
451
|
});
|
|
400
452
|
throw new Error('Not a papi');
|
|
401
453
|
}
|
|
402
|
-
const papiParameters = getPapiParameters(
|
|
454
|
+
const papiParameters = getPapiParameters(entry);
|
|
403
455
|
result.push(createPapiMethod({
|
|
404
|
-
...
|
|
405
|
-
|
|
406
|
-
path: `/${k}`,
|
|
407
|
-
handler,
|
|
456
|
+
...papiParameters,
|
|
457
|
+
path: `/${path}`,
|
|
408
458
|
}));
|
|
409
|
-
},
|
|
459
|
+
}, papis);
|
|
410
460
|
return result;
|
|
411
461
|
};
|
|
412
462
|
const fileApiProvider = {
|
|
@@ -439,16 +489,50 @@ const sharedProviders = [
|
|
|
439
489
|
},
|
|
440
490
|
];
|
|
441
491
|
|
|
492
|
+
const papiExecutorProvider = provide({
|
|
493
|
+
provide: PAPI_EXECUTOR,
|
|
494
|
+
scope: Scope$1.REQUEST,
|
|
495
|
+
useFactory: ({ di, logger, fastifyRequest, requestManager, responseManager }) => {
|
|
496
|
+
var _a;
|
|
497
|
+
const papiLog = logger('papi');
|
|
498
|
+
const papiOptions = {
|
|
499
|
+
requestManager,
|
|
500
|
+
responseManager,
|
|
501
|
+
params: (_a = fastifyRequest.params) !== null && _a !== void 0 ? _a : {},
|
|
502
|
+
cookies: requestManager.getCookies(),
|
|
503
|
+
headers: requestManager.getHeaders(),
|
|
504
|
+
body: requestManager.getBody(),
|
|
505
|
+
parsedUrl: requestManager.getParsedUrl(),
|
|
506
|
+
};
|
|
507
|
+
return (papi) => {
|
|
508
|
+
const { handler, deps, method, path } = getPapiParameters(papi);
|
|
509
|
+
const papiContext = {
|
|
510
|
+
deps: di.getOfDeps(deps),
|
|
511
|
+
log: papiLog.child(`${method}_${path}`),
|
|
512
|
+
};
|
|
513
|
+
return handler.call(papiContext, papiOptions);
|
|
514
|
+
};
|
|
515
|
+
},
|
|
516
|
+
deps: {
|
|
517
|
+
di: DI_TOKEN,
|
|
518
|
+
logger: LOGGER_TOKEN,
|
|
519
|
+
fastifyRequest: FASTIFY_REQUEST,
|
|
520
|
+
requestManager: REQUEST_MANAGER_TOKEN,
|
|
521
|
+
responseManager: RESPONSE_MANAGER_TOKEN,
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
|
|
442
525
|
let ServerPapiModule = class ServerPapiModule {
|
|
443
526
|
};
|
|
444
527
|
ServerPapiModule = __decorate([
|
|
445
528
|
Module({
|
|
446
529
|
providers: [
|
|
530
|
+
papiExecutorProvider,
|
|
447
531
|
fileApiProvider,
|
|
448
532
|
...sharedProviders,
|
|
449
533
|
provide$1({
|
|
450
|
-
provide:
|
|
451
|
-
useFactory: ({ di,
|
|
534
|
+
provide: WEB_FASTIFY_APP_BEFORE_INIT_TOKEN,
|
|
535
|
+
useFactory: ({ di, logger, privateRoutes, publicRoutes, publicBaseUrl, privateBaseUrl }) => (app) => {
|
|
452
536
|
if (process.env.NODE_ENV === 'development') {
|
|
453
537
|
const papiListRoute = createPapiMethod({
|
|
454
538
|
method: 'get',
|
|
@@ -482,23 +566,22 @@ ServerPapiModule = __decorate([
|
|
|
482
566
|
: [papiListRoute];
|
|
483
567
|
}
|
|
484
568
|
if (privateRoutes) {
|
|
485
|
-
|
|
569
|
+
createApi(app, flatten(privateRoutes), {
|
|
570
|
+
baseUrl: privateBaseUrl,
|
|
486
571
|
di,
|
|
487
572
|
logger,
|
|
488
573
|
});
|
|
489
|
-
app.use(privateBaseUrl, apiPrivate);
|
|
490
574
|
}
|
|
491
575
|
if (publicRoutes) {
|
|
492
|
-
|
|
576
|
+
createApi(app, flatten(publicRoutes), {
|
|
577
|
+
baseUrl: publicBaseUrl,
|
|
493
578
|
di,
|
|
494
579
|
logger,
|
|
495
580
|
});
|
|
496
|
-
app.use(publicBaseUrl, apiPublic);
|
|
497
581
|
}
|
|
498
582
|
},
|
|
499
583
|
deps: {
|
|
500
|
-
|
|
501
|
-
di: DI_TOKEN,
|
|
584
|
+
di: DI_TOKEN$1,
|
|
502
585
|
logger: LOGGER_TOKEN,
|
|
503
586
|
privateRoutes: {
|
|
504
587
|
token: SERVER_MODULE_PAPI_PRIVATE_ROUTE,
|
|
@@ -522,14 +605,14 @@ ServerPapiModule = __decorate([
|
|
|
522
605
|
useValue: createPapiMethod({
|
|
523
606
|
method: 'get',
|
|
524
607
|
path: '/version',
|
|
525
|
-
|
|
526
|
-
envManager: ENV_MANAGER_TOKEN,
|
|
527
|
-
},
|
|
528
|
-
async handler(req, res, { envManager }) {
|
|
608
|
+
async handler() {
|
|
529
609
|
return {
|
|
530
|
-
version: envManager.get('APP_VERSION'),
|
|
610
|
+
version: this.deps.envManager.get('APP_VERSION'),
|
|
531
611
|
};
|
|
532
612
|
},
|
|
613
|
+
deps: {
|
|
614
|
+
envManager: ENV_MANAGER_TOKEN,
|
|
615
|
+
},
|
|
533
616
|
}),
|
|
534
617
|
}),
|
|
535
618
|
],
|
|
@@ -751,8 +834,8 @@ ${parsed.href || `${parsed.protocol}//${parsed.hostname}${parsed.path}`}
|
|
|
751
834
|
return createPapiMethod({
|
|
752
835
|
method: 'post',
|
|
753
836
|
path: '/debug-http-request',
|
|
754
|
-
async handler({
|
|
755
|
-
const { delay = 10000 } =
|
|
837
|
+
async handler({ body }) {
|
|
838
|
+
const { delay = 10000 } = body;
|
|
756
839
|
if (delay) {
|
|
757
840
|
interceptor.setDelay(delay);
|
|
758
841
|
}
|
package/lib/server.js
CHANGED
|
@@ -214,6 +214,7 @@ const errorHandler = (app, { log, beforeError, processError, afterError, }) => {
|
|
|
214
214
|
|
|
215
215
|
const webAppFactory = ({ server }) => {
|
|
216
216
|
const app = fastify__default["default"]({
|
|
217
|
+
ignoreTrailingSlash: true,
|
|
217
218
|
serverFactory: (handler) => {
|
|
218
219
|
server.on('request', handler);
|
|
219
220
|
return server;
|
|
@@ -373,51 +374,102 @@ const xHeadersFactory = ({ app, envManager, appInfo, }) => {
|
|
|
373
374
|
};
|
|
374
375
|
};
|
|
375
376
|
|
|
376
|
-
function createApi(papiList, { di, logger }) {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
377
|
+
function createApi(rootApp, papiList, { baseUrl, di, logger }) {
|
|
378
|
+
const paths = new Set();
|
|
379
|
+
const papiLog = logger('papi');
|
|
380
|
+
rootApp.register(async (app) => {
|
|
381
|
+
await app.register(cookie.fastifyCookie);
|
|
382
|
+
await app.register(fastifyFormBody__default["default"], { bodyLimit: 2097152 }); // 2mb
|
|
383
|
+
for (const papi$1 of papiList) {
|
|
384
|
+
const papiParams = papi.getPapiParameters(papi$1);
|
|
385
|
+
if (!papiParams) {
|
|
386
|
+
throw new Error(`papi should be created using createPapiMethod from @tramvai/papi,
|
|
387
|
+
got: ${JSON.stringify(papi$1)}`);
|
|
388
|
+
}
|
|
389
|
+
const { method, path, options } = papiParams;
|
|
390
|
+
const { timeout, schema } = options;
|
|
391
|
+
if (!path) {
|
|
392
|
+
throw new Error(`No path in papi handler, got: ${JSON.stringify(papi$1)}`);
|
|
393
|
+
}
|
|
394
|
+
const key = `${method} ${path}`;
|
|
395
|
+
if (paths.has(key)) {
|
|
396
|
+
throw new Error(`papi: route '${key}' already registered`);
|
|
397
|
+
}
|
|
398
|
+
paths.add(key);
|
|
399
|
+
const childLog = papiLog.child(`${papiParams.method}_${papiParams.path}`);
|
|
400
|
+
app[method](path, {
|
|
401
|
+
schema,
|
|
402
|
+
errorHandler: async (error, req, res) => {
|
|
389
403
|
var _a;
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
404
|
+
res.status(error.validation ? 400 : 503);
|
|
405
|
+
childLog.error(error);
|
|
406
|
+
return {
|
|
407
|
+
resultCode: 'INTERNAL_ERROR',
|
|
408
|
+
errorMessage: (_a = error.message) !== null && _a !== void 0 ? _a : 'internal error',
|
|
409
|
+
};
|
|
410
|
+
},
|
|
411
|
+
}, async (req, res) => {
|
|
412
|
+
const childDi = dippy.createChildContainer(di, [
|
|
413
|
+
{
|
|
414
|
+
provide: tokensCommon.FASTIFY_REQUEST,
|
|
415
|
+
scope: dippy.Scope.REQUEST,
|
|
393
416
|
useValue: req,
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
provide: tokensCommon.
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
provide: tokensCommon.FASTIFY_RESPONSE,
|
|
420
|
+
scope: dippy.Scope.REQUEST,
|
|
397
421
|
useValue: res,
|
|
398
|
-
}
|
|
399
|
-
|
|
422
|
+
},
|
|
423
|
+
// TODO: remove deprecated after removing express compatibility layer
|
|
424
|
+
{
|
|
425
|
+
provide: tokensCommon.REQUEST,
|
|
426
|
+
scope: dippy.Scope.REQUEST,
|
|
427
|
+
useValue: req.raw,
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
provide: tokensCommon.RESPONSE,
|
|
431
|
+
scope: dippy.Scope.REQUEST,
|
|
432
|
+
useValue: res.raw,
|
|
433
|
+
},
|
|
434
|
+
]);
|
|
435
|
+
const papiExecutor = childDi.get(tokensServerPrivate.PAPI_EXECUTOR);
|
|
436
|
+
// TODO: use abortSignal
|
|
437
|
+
const payload = await Promise.race([
|
|
438
|
+
papiExecutor(papi$1),
|
|
439
|
+
new Promise((resolve, reject) => setTimeout(() => reject(new errors.HttpError({ httpStatus: 503, message: 'Execution timeout' })), timeout)),
|
|
440
|
+
]);
|
|
441
|
+
const responseManager = childDi.get(tokensCommon.RESPONSE_MANAGER_TOKEN);
|
|
442
|
+
res.headers(responseManager.getHeaders()).status(responseManager.getStatus());
|
|
443
|
+
if (res.sent) {
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
if (!payload && responseManager.getBody()) {
|
|
447
|
+
res.send(responseManager.getBody());
|
|
448
|
+
return res;
|
|
449
|
+
}
|
|
450
|
+
return {
|
|
451
|
+
resultCode: 'OK',
|
|
452
|
+
payload,
|
|
400
453
|
};
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
papi.middlewares.error({ logger }),
|
|
405
|
-
]);
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}, { prefix: baseUrl });
|
|
406
457
|
}
|
|
407
458
|
|
|
408
|
-
let
|
|
459
|
+
let papis;
|
|
409
460
|
try {
|
|
410
461
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
411
|
-
|
|
462
|
+
papis = require('@tramvai/cli/lib/external/api').default; // eslint-disable-line import/no-unresolved
|
|
412
463
|
}
|
|
413
464
|
catch (e) { }
|
|
414
465
|
const getFileApi = ({ logger }) => {
|
|
415
466
|
const log = logger('papi:fileApi');
|
|
416
467
|
const result = [];
|
|
417
|
-
eachObj__default["default"]((
|
|
418
|
-
|
|
419
|
-
if (!handler) {
|
|
468
|
+
eachObj__default["default"](({ default: entry }, path) => {
|
|
469
|
+
if (!papi.isPapiMethod(entry)) {
|
|
420
470
|
log.error({
|
|
471
|
+
path,
|
|
472
|
+
entry,
|
|
421
473
|
message: `Cannot resolve a papi handler.
|
|
422
474
|
Check that you are using file based papi right way by docs https://tramvai.dev/docs/how-to/how-create-papi#automatic-handler-creation
|
|
423
475
|
In case you have not added any file papi handler, consider renaming directory ./src/api (by default) to the other name to resolve conflicts with papi, or
|
|
@@ -425,14 +477,12 @@ change settings application.commands.build.options.serverApiDir in tramvai.json`
|
|
|
425
477
|
});
|
|
426
478
|
throw new Error('Not a papi');
|
|
427
479
|
}
|
|
428
|
-
const papiParameters = papi.getPapiParameters(
|
|
480
|
+
const papiParameters = papi.getPapiParameters(entry);
|
|
429
481
|
result.push(papi.createPapiMethod({
|
|
430
|
-
...
|
|
431
|
-
|
|
432
|
-
path: `/${k}`,
|
|
433
|
-
handler,
|
|
482
|
+
...papiParameters,
|
|
483
|
+
path: `/${path}`,
|
|
434
484
|
}));
|
|
435
|
-
},
|
|
485
|
+
}, papis);
|
|
436
486
|
return result;
|
|
437
487
|
};
|
|
438
488
|
const fileApiProvider = {
|
|
@@ -465,16 +515,50 @@ const sharedProviders = [
|
|
|
465
515
|
},
|
|
466
516
|
];
|
|
467
517
|
|
|
518
|
+
const papiExecutorProvider = dippy.provide({
|
|
519
|
+
provide: tokensServerPrivate.PAPI_EXECUTOR,
|
|
520
|
+
scope: dippy.Scope.REQUEST,
|
|
521
|
+
useFactory: ({ di, logger, fastifyRequest, requestManager, responseManager }) => {
|
|
522
|
+
var _a;
|
|
523
|
+
const papiLog = logger('papi');
|
|
524
|
+
const papiOptions = {
|
|
525
|
+
requestManager,
|
|
526
|
+
responseManager,
|
|
527
|
+
params: (_a = fastifyRequest.params) !== null && _a !== void 0 ? _a : {},
|
|
528
|
+
cookies: requestManager.getCookies(),
|
|
529
|
+
headers: requestManager.getHeaders(),
|
|
530
|
+
body: requestManager.getBody(),
|
|
531
|
+
parsedUrl: requestManager.getParsedUrl(),
|
|
532
|
+
};
|
|
533
|
+
return (papi$1) => {
|
|
534
|
+
const { handler, deps, method, path } = papi.getPapiParameters(papi$1);
|
|
535
|
+
const papiContext = {
|
|
536
|
+
deps: di.getOfDeps(deps),
|
|
537
|
+
log: papiLog.child(`${method}_${path}`),
|
|
538
|
+
};
|
|
539
|
+
return handler.call(papiContext, papiOptions);
|
|
540
|
+
};
|
|
541
|
+
},
|
|
542
|
+
deps: {
|
|
543
|
+
di: dippy.DI_TOKEN,
|
|
544
|
+
logger: tokensCommon.LOGGER_TOKEN,
|
|
545
|
+
fastifyRequest: tokensCommon.FASTIFY_REQUEST,
|
|
546
|
+
requestManager: tokensCommon.REQUEST_MANAGER_TOKEN,
|
|
547
|
+
responseManager: tokensCommon.RESPONSE_MANAGER_TOKEN,
|
|
548
|
+
},
|
|
549
|
+
});
|
|
550
|
+
|
|
468
551
|
let ServerPapiModule = class ServerPapiModule {
|
|
469
552
|
};
|
|
470
553
|
ServerPapiModule = tslib.__decorate([
|
|
471
554
|
core.Module({
|
|
472
555
|
providers: [
|
|
556
|
+
papiExecutorProvider,
|
|
473
557
|
fileApiProvider,
|
|
474
558
|
...sharedProviders,
|
|
475
559
|
core.provide({
|
|
476
|
-
provide:
|
|
477
|
-
useFactory: ({ di,
|
|
560
|
+
provide: tokensServerPrivate.WEB_FASTIFY_APP_BEFORE_INIT_TOKEN,
|
|
561
|
+
useFactory: ({ di, logger, privateRoutes, publicRoutes, publicBaseUrl, privateBaseUrl }) => (app) => {
|
|
478
562
|
if (process.env.NODE_ENV === 'development') {
|
|
479
563
|
const papiListRoute = papi.createPapiMethod({
|
|
480
564
|
method: 'get',
|
|
@@ -508,22 +592,21 @@ ServerPapiModule = tslib.__decorate([
|
|
|
508
592
|
: [papiListRoute];
|
|
509
593
|
}
|
|
510
594
|
if (privateRoutes) {
|
|
511
|
-
|
|
595
|
+
createApi(app, flatten__default["default"](privateRoutes), {
|
|
596
|
+
baseUrl: privateBaseUrl,
|
|
512
597
|
di,
|
|
513
598
|
logger,
|
|
514
599
|
});
|
|
515
|
-
app.use(privateBaseUrl, apiPrivate);
|
|
516
600
|
}
|
|
517
601
|
if (publicRoutes) {
|
|
518
|
-
|
|
602
|
+
createApi(app, flatten__default["default"](publicRoutes), {
|
|
603
|
+
baseUrl: publicBaseUrl,
|
|
519
604
|
di,
|
|
520
605
|
logger,
|
|
521
606
|
});
|
|
522
|
-
app.use(publicBaseUrl, apiPublic);
|
|
523
607
|
}
|
|
524
608
|
},
|
|
525
609
|
deps: {
|
|
526
|
-
app: tokensServer.WEB_APP_TOKEN,
|
|
527
610
|
di: core.DI_TOKEN,
|
|
528
611
|
logger: tokensCommon.LOGGER_TOKEN,
|
|
529
612
|
privateRoutes: {
|
|
@@ -548,14 +631,14 @@ ServerPapiModule = tslib.__decorate([
|
|
|
548
631
|
useValue: papi.createPapiMethod({
|
|
549
632
|
method: 'get',
|
|
550
633
|
path: '/version',
|
|
551
|
-
|
|
552
|
-
envManager: tokensCommon.ENV_MANAGER_TOKEN,
|
|
553
|
-
},
|
|
554
|
-
async handler(req, res, { envManager }) {
|
|
634
|
+
async handler() {
|
|
555
635
|
return {
|
|
556
|
-
version: envManager.get('APP_VERSION'),
|
|
636
|
+
version: this.deps.envManager.get('APP_VERSION'),
|
|
557
637
|
};
|
|
558
638
|
},
|
|
639
|
+
deps: {
|
|
640
|
+
envManager: tokensCommon.ENV_MANAGER_TOKEN,
|
|
641
|
+
},
|
|
559
642
|
}),
|
|
560
643
|
}),
|
|
561
644
|
],
|
|
@@ -777,8 +860,8 @@ ${parsed.href || `${parsed.protocol}//${parsed.hostname}${parsed.path}`}
|
|
|
777
860
|
return papi.createPapiMethod({
|
|
778
861
|
method: 'post',
|
|
779
862
|
path: '/debug-http-request',
|
|
780
|
-
async handler({
|
|
781
|
-
const { delay = 10000 } =
|
|
863
|
+
async handler({ body }) {
|
|
864
|
+
const { delay = 10000 } = body;
|
|
782
865
|
if (delay) {
|
|
783
866
|
interceptor.setDelay(delay);
|
|
784
867
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tramvai/module-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.24.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"browser": "lib/browser.js",
|
|
6
6
|
"main": "lib/server.js",
|
|
@@ -22,15 +22,15 @@
|
|
|
22
22
|
"build-for-publish": "true"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@tinkoff/errors": "0.3.
|
|
25
|
+
"@tinkoff/errors": "0.3.3",
|
|
26
26
|
"@tinkoff/monkeypatch": "2.0.2",
|
|
27
27
|
"@tinkoff/terminus": "0.1.2",
|
|
28
28
|
"@tinkoff/url": "0.8.2",
|
|
29
|
-
"@tramvai/module-cache-warmup": "2.
|
|
30
|
-
"@tramvai/module-metrics": "2.
|
|
31
|
-
"@tramvai/papi": "2.
|
|
32
|
-
"@tramvai/tokens-server": "2.
|
|
33
|
-
"@tramvai/tokens-server-private": "2.
|
|
29
|
+
"@tramvai/module-cache-warmup": "2.24.0",
|
|
30
|
+
"@tramvai/module-metrics": "2.24.0",
|
|
31
|
+
"@tramvai/papi": "2.24.0",
|
|
32
|
+
"@tramvai/tokens-server": "2.24.0",
|
|
33
|
+
"@tramvai/tokens-server-private": "2.24.0",
|
|
34
34
|
"compression": "^1.7.4",
|
|
35
35
|
"express": "^4.17.1",
|
|
36
36
|
"fastify": "^3.29.0",
|
|
@@ -43,11 +43,11 @@
|
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"@tinkoff/dippy": "0.8.2",
|
|
45
45
|
"@tinkoff/utils": "^2.1.2",
|
|
46
|
-
"@tramvai/cli": "2.
|
|
47
|
-
"@tramvai/core": "2.
|
|
48
|
-
"@tramvai/module-common": "2.
|
|
49
|
-
"@tramvai/module-environment": "2.
|
|
50
|
-
"@tramvai/tokens-common": "2.
|
|
46
|
+
"@tramvai/cli": "2.24.0",
|
|
47
|
+
"@tramvai/core": "2.24.0",
|
|
48
|
+
"@tramvai/module-common": "2.24.0",
|
|
49
|
+
"@tramvai/module-environment": "2.24.0",
|
|
50
|
+
"@tramvai/tokens-common": "2.24.0",
|
|
51
51
|
"tslib": "^2.0.3"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { Provider } from '@tramvai/core';
|
|
2
|
-
declare module '@tramvai/papi/lib/types' {
|
|
3
|
-
interface PapiParameters<Deps, Result> {
|
|
4
|
-
rootDeps?: Record<string, any>;
|
|
5
|
-
mapRootDeps?: (rootDeps: Record<string, any>) => Record<string, any>;
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
export declare const fileApiProvider: Provider;
|