@midwayjs/web
Version:
Midway Web Framework for Egg.js
339 lines • 14.1 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MidwayWebFramework = void 0;
const core_1 = require("@midwayjs/core");
const router_1 = require("@eggjs/router");
const logger_1 = require("@midwayjs/logger");
const path_1 = require("path");
const util_1 = require("util");
const net_1 = require("net");
const debug = (0, util_1.debuglog)('midway:debug');
class EggControllerGenerator extends core_1.WebControllerGenerator {
app;
webRouterService;
constructor(app, webRouterService) {
super(app, webRouterService);
this.app = app;
this.webRouterService = webRouterService;
}
createRouter(routerOptions) {
const router = new router_1.EggRouter(routerOptions, this.app);
router.prefix(routerOptions.prefix);
return router;
}
generateController(routeInfo) {
return this.generateKoaController(routeInfo);
}
}
let MidwayWebFramework = class MidwayWebFramework extends core_1.BaseFramework {
loggers;
generator;
server;
agent;
isClusterMode = false;
webRouterService;
appDir;
configure() {
process.env.EGG_TYPESCRIPT = 'true';
if (process.env['EGG_CLUSTER_MODE'] === 'true') {
this.isClusterMode = true;
}
return this.configService.getConfiguration('egg');
}
async initSingleProcessEgg() {
const opts = {
baseDir: this.appDir,
framework: (0, path_1.resolve)(__dirname, '../application'),
plugins: this.configurationOptions.plugins,
mode: 'single',
isTsMode: true,
applicationContext: this.applicationContext,
midwaySingleton: true,
};
debug('[egg]: init single process egg agent');
const Agent = require(opts.framework).Agent;
const Application = require(opts.framework).Application;
const agent = (this.agent = new Agent(Object.assign({}, opts)));
await agent.ready();
debug('[egg]: init single process egg application');
const application = (this.app = new Application(Object.assign({}, opts)));
application.agent = agent;
agent.application = application;
debug('[egg]: init single process egg end');
}
async applicationInitialize(options) {
if (!this.isClusterMode) {
await this.initSingleProcessEgg();
}
else {
// get app in cluster mode
this.app = options['application'];
}
// not found middleware
const midwayRouterNotFound = async (ctx, next) => {
await next();
if (!ctx._matchedRoute && ctx.body === undefined) {
throw new core_1.httpError.NotFoundError(`${ctx.path} Not Found`);
}
};
const applyMiddlewares = [midwayRouterNotFound];
const versioningConfig = this.configurationOptions.versioning;
// 如果启用版本控制,添加版本处理中间件
if (versioningConfig?.enabled) {
applyMiddlewares.push(this.createVersioningMiddleware(versioningConfig));
}
// insert error handler
const midwayRootMiddleware = async (ctx, next) => {
await (await this.applyMiddleware(applyMiddlewares))(ctx, next);
};
this.app.use(midwayRootMiddleware);
this.webRouterService = await this.applicationContext.getAsync(core_1.MidwayWebRouterService, [
{
globalPrefix: this.configurationOptions.globalPrefix,
},
]);
this.generator = new EggControllerGenerator(this.app, this.webRouterService);
this.overwriteApplication('app');
this.app.loader.app = this.app;
this.app.loader.loadOrigin();
// 这里拦截 app.use 方法,让他可以加到 midway 的 middlewareManager 中
this.app.originUse = this.app.use;
this.app.use = this.app.useMiddleware;
if (!this.isClusterMode) {
await new Promise(resolve => {
this.app.once('application-ready', () => {
debug('[egg]: web framework: init egg end');
resolve();
});
this.app.ready();
});
}
}
overwriteApplication(processType) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
// 单进程下,先把egg的配置覆盖进来,有可能业务测没写 importConfigs
debug(`[egg]: overwrite egg config to configService in "${processType}"`);
this.configService.addObject(this.app.config);
Object.defineProperty(this.app, 'config', {
get() {
return self.getConfiguration();
},
});
debug(`[egg]: overwrite applicationContext config to "${processType}"`);
Object.defineProperty(this.app, 'applicationContext', {
get() {
return self.applicationContext;
},
});
debug(`[egg]: overwrite properties to "${processType}"`);
this.defineApplicationProperties({
generateController: (controllerMapping) => {
const [controllerId, methodName] = controllerMapping.split('.');
return this.generator.generateController({
id: controllerId,
method: methodName,
});
},
generateMiddleware: async (middlewareId) => {
return this.generateMiddleware(middlewareId);
},
getProcessType: () => {
if (processType === 'app') {
return core_1.MidwayProcessTypeEnum.APPLICATION;
}
if (processType === 'agent') {
return core_1.MidwayProcessTypeEnum.AGENT;
}
},
createContextLogger: (ctx, name) => {
return this.createContextLogger(ctx, name);
},
}, ['createAnonymousContext']);
// if use midway logger will be use midway custom context logger
debug(`[egg]: overwrite BaseContextLoggerClass to "${processType}"`);
this.setContextLoggerClass();
}
async loadMidwayController() {
// move egg router to last
this.app.getMiddleware().findAndInsertLast('eggRouterMiddleware');
await this.generator.loadMidwayController(newRouter => {
const dispatchFn = newRouter.middleware();
dispatchFn._name = `midwayController(${newRouter?.opts?.prefix || '/'})`;
this.app.useMiddleware(dispatchFn);
});
// restore use method
this.app.use = this.app.originUse;
debug(`[egg]: current middleware = ${this.middlewareManager.getNames()}`);
}
async run() {
// cluster 模式加载路由需在 run 之前,因为 run 需要在拿到 server 之后执行
if (!this.isClusterMode) {
// load controller
await this.loadMidwayController();
const serverOptions = {
...this.configurationOptions,
...this.configurationOptions.serverOptions,
};
// https config
if (serverOptions.key && serverOptions.cert) {
serverOptions.key = core_1.PathFileUtils.getFileContentSync(serverOptions.key);
serverOptions.cert = core_1.PathFileUtils.getFileContentSync(serverOptions.cert);
serverOptions.ca = core_1.PathFileUtils.getFileContentSync(serverOptions.ca);
process.env.MIDWAY_HTTP_SSL = 'true';
if (serverOptions.http2) {
this.server = require('http2').createSecureServer(serverOptions, this.app.callback());
}
else {
this.server = require('https').createServer(serverOptions, this.app.callback());
}
}
else {
if (serverOptions.http2) {
this.server = require('http2').createServer(serverOptions, this.app.callback());
}
else {
this.server = require('http').createServer(serverOptions, this.app.callback());
}
}
// emit egg-ready message in agent and application
this.app.messenger.broadcast('egg-ready', undefined);
// emit `server` event in app
this.app.emit('server', this.server);
// register httpServer to applicationContext
this.getApplicationContext().registerObject(core_1.HTTP_SERVER_KEY, this.server);
const eggConfig = this.configService.getConfiguration('egg');
if (!this.isClusterMode && eggConfig) {
let customPort = process.env.MIDWAY_HTTP_PORT ?? eggConfig.port;
if (customPort === 0) {
customPort = await getFreePort();
}
if (customPort) {
new Promise(resolve => {
const args = [customPort];
if (eggConfig.hostname) {
args.push(eggConfig.hostname);
}
args.push(() => {
resolve();
});
this.server.listen(...args);
process.env.MIDWAY_HTTP_PORT = String(customPort);
});
}
}
}
}
getLogger(name) {
if (name) {
return this.app.loggers[name] || logger_1.loggers.getLogger(name);
}
return this.loggerService.getLogger(this.frameworkLoggerName);
}
setContextLoggerClass() {
const contextFormat = this.configService.getConfiguration('midwayLogger.clients.appLogger.contextFormat');
class MidwayEggContextLogger extends logger_1.MidwayContextLogger {
constructor(ctx, appLogger) {
super(ctx, appLogger, {
contextFormat,
});
}
}
this.app.ContextLogger = MidwayEggContextLogger;
}
async generateMiddleware(middlewareId) {
const mwIns = await this.getApplicationContext().getAsync(middlewareId);
return mwIns.resolve();
}
async beforeStop() {
if (!this.isClusterMode) {
await new Promise(resolve => {
this.server.close(resolve);
});
await this.app.close();
await this.agent.close();
}
}
setServer(server) {
this.server = server;
}
createVersioningMiddleware(config) {
return async (ctx, next) => {
// 提取版本信息
const version = this.extractVersion(ctx, config);
ctx.apiVersion = version;
// 对于 URI 版本控制,重写路径
if (config.type === 'URI' && version) {
const versionPrefix = `/${config.prefix || 'v'}${version}`;
if (ctx.path.startsWith(versionPrefix)) {
ctx.originalPath = ctx.path;
ctx.path = ctx.path.replace(versionPrefix, '') || '/';
}
}
await next();
};
}
extractVersion(ctx, config) {
// 自定义提取函数优先
if (config.extractVersionFn) {
return config.extractVersionFn(ctx);
}
const type = config.type || 'URI';
switch (type) {
case 'HEADER': {
const headerName = config.header || 'x-api-version';
const headerValue = ctx.headers[headerName];
if (typeof headerValue === 'string') {
return headerValue.replace(/^v/, '');
}
return undefined;
}
case 'MEDIA_TYPE': {
const accept = ctx.headers.accept;
const paramName = config.mediaTypeParam || 'version';
const match = accept?.match(new RegExp(`${paramName}=(\\\\d+)`));
return match ? match[1] : undefined;
}
case 'URI': {
const prefix = config.prefix || 'v';
const uriMatch = ctx.path.match(new RegExp(`^/${prefix}(\\\\d+)`));
return uriMatch ? uriMatch[1] : undefined;
}
default:
return config.defaultVersion;
}
}
};
exports.MidwayWebFramework = MidwayWebFramework;
__decorate([
(0, core_1.Inject)(),
__metadata("design:type", Object)
], MidwayWebFramework.prototype, "appDir", void 0);
exports.MidwayWebFramework = MidwayWebFramework = __decorate([
(0, core_1.Framework)()
], MidwayWebFramework);
async function getFreePort() {
return new Promise((resolve, reject) => {
const server = (0, net_1.createServer)();
server.listen(0, () => {
try {
const port = server.address().port;
server.close();
resolve(port);
}
catch (err) {
reject(err);
}
});
});
}
//# sourceMappingURL=web.js.map