mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-07 14:39:25 +08:00
chore: add middleware name (#5594)
* chore: middleware name * chore: add name to middleware * chore: logging
This commit is contained in:
parent
124379528d
commit
aad8fd7bab
@ -106,7 +106,9 @@ export class AuthManager {
|
||||
* @description Auth middleware, used to check the authentication status.
|
||||
*/
|
||||
middleware() {
|
||||
return async (ctx: Context & { auth: Auth }, next: Next) => {
|
||||
const self = this;
|
||||
|
||||
return async function AuthManagerMiddleware(ctx: Context & { auth: Auth }, next: Next) {
|
||||
const token = ctx.getBearerToken();
|
||||
if (token && (await ctx.app.authManager.jwt.blacklist?.has(token))) {
|
||||
return ctx.throw(401, {
|
||||
@ -115,7 +117,8 @@ export class AuthManager {
|
||||
});
|
||||
}
|
||||
|
||||
const name = ctx.get(this.options.authKey) || this.options.default;
|
||||
const name = ctx.get(self.options.authKey) || self.options.default;
|
||||
|
||||
let authenticator: Auth;
|
||||
try {
|
||||
authenticator = await ctx.app.authManager.get(name, ctx);
|
||||
|
@ -84,17 +84,20 @@ export class DataSourceManager {
|
||||
}
|
||||
|
||||
middleware() {
|
||||
return async (ctx, next) => {
|
||||
const self = this;
|
||||
|
||||
return async function dataSourceManager(ctx, next) {
|
||||
const name = ctx.get('x-data-source') || 'main';
|
||||
|
||||
if (!this.dataSources.has(name)) {
|
||||
if (!self.dataSources.has(name)) {
|
||||
ctx.throw(`data source ${name} does not exist`);
|
||||
}
|
||||
|
||||
const ds = this.dataSources.get(name);
|
||||
const ds = self.dataSources.get(name);
|
||||
ctx.dataSource = ds;
|
||||
|
||||
return ds.middleware(this.middlewares)(ctx, next);
|
||||
const composedFn = ds.middleware(self.middlewares);
|
||||
return composedFn(ctx, next);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ import compose from 'koa-compose';
|
||||
import { loadDefaultActions } from './load-default-actions';
|
||||
import { ICollectionManager } from './types';
|
||||
import { Logger } from '@nocobase/logger';
|
||||
import { wrapMiddlewareWithLogging } from '@nocobase/utils';
|
||||
|
||||
export type DataSourceOptions = any;
|
||||
|
||||
@ -79,6 +80,7 @@ export abstract class DataSource extends EventEmitter {
|
||||
for (const [fn, options] of middlewares) {
|
||||
this.resourceManager.use(fn, options);
|
||||
}
|
||||
|
||||
this['_used'] = true;
|
||||
}
|
||||
|
||||
@ -91,7 +93,9 @@ export abstract class DataSource extends EventEmitter {
|
||||
return this.collectionManager.getRepository(resourceName, resourceOf);
|
||||
};
|
||||
|
||||
return compose([this.collectionToResourceMiddleware(), this.resourceManager.middleware()])(ctx, next);
|
||||
const middlewares = [this.collectionToResourceMiddleware(), this.resourceManager.middleware()];
|
||||
|
||||
return compose(middlewares.map((fn) => wrapMiddlewareWithLogging(fn)))(ctx, next);
|
||||
};
|
||||
}
|
||||
|
||||
@ -117,15 +121,16 @@ export abstract class DataSource extends EventEmitter {
|
||||
abstract createCollectionManager(options?: any): ICollectionManager;
|
||||
|
||||
protected collectionToResourceMiddleware() {
|
||||
return async (ctx, next) => {
|
||||
const self = this;
|
||||
return async function collectionToResource(ctx, next) {
|
||||
const params = parseRequest(
|
||||
{
|
||||
path: ctx.request.path,
|
||||
method: ctx.request.method,
|
||||
},
|
||||
{
|
||||
prefix: this.resourceManager.options.prefix,
|
||||
accessors: this.resourceManager.options.accessors,
|
||||
prefix: self.resourceManager.options.prefix,
|
||||
accessors: self.resourceManager.options.accessors,
|
||||
},
|
||||
);
|
||||
if (!params) {
|
||||
@ -133,7 +138,7 @@ export abstract class DataSource extends EventEmitter {
|
||||
}
|
||||
const resourceName = getNameByParams(params);
|
||||
// 如果资源名称未被定义
|
||||
if (this.resourceManager.isDefined(resourceName)) {
|
||||
if (self.resourceManager.isDefined(resourceName)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
@ -141,11 +146,11 @@ export abstract class DataSource extends EventEmitter {
|
||||
|
||||
const collectionName = splitResult[0];
|
||||
|
||||
if (!this.collectionManager.hasCollection(collectionName)) {
|
||||
if (!self.collectionManager.hasCollection(collectionName)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
this.resourceManager.define({
|
||||
self.resourceManager.define({
|
||||
name: resourceName,
|
||||
});
|
||||
|
||||
|
@ -34,7 +34,7 @@ export interface RequestLoggerOptions extends LoggerOptions {
|
||||
}
|
||||
|
||||
export const requestLogger = (appName: string, requestLogger: Logger, options?: RequestLoggerOptions) => {
|
||||
return async (ctx, next) => {
|
||||
return async function requestLoggerMiddleware(ctx, next) {
|
||||
const reqId = ctx.reqId;
|
||||
const path = /^\/api\/(.+):(.+)/.exec(ctx.path);
|
||||
const contextLogger = ctx.app.log.child({ reqId, module: path?.[1], submodule: path?.[2] });
|
||||
|
@ -7,7 +7,7 @@
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { assign, MergeStrategies, requireModule } from '@nocobase/utils';
|
||||
import { assign, MergeStrategies, requireModule, wrapMiddlewareWithLogging } from '@nocobase/utils';
|
||||
import compose from 'koa-compose';
|
||||
import _ from 'lodash';
|
||||
import Middleware, { MiddlewareType } from './middleware';
|
||||
@ -375,9 +375,7 @@ export class Action {
|
||||
this.getHandler(),
|
||||
].filter(Boolean);
|
||||
|
||||
// handlers = handlers.map((handler) => prePerfHooksWrap(handler));
|
||||
|
||||
return handlers;
|
||||
return handlers.map((fn) => wrapMiddlewareWithLogging(fn));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -306,7 +306,9 @@ export class ResourceManager {
|
||||
}
|
||||
|
||||
middleware({ prefix, accessors, skipIfDataSourceExists = false }: KoaMiddlewareOptions = {}) {
|
||||
return async (ctx: ResourcerContext, next: () => Promise<any>) => {
|
||||
const self = this;
|
||||
|
||||
return async function resourcerMiddleware(ctx: ResourcerContext, next: () => Promise<any>) {
|
||||
if (skipIfDataSourceExists) {
|
||||
const dataSource = ctx.get('x-data-source');
|
||||
if (dataSource) {
|
||||
@ -314,7 +316,7 @@ export class ResourceManager {
|
||||
}
|
||||
}
|
||||
|
||||
ctx.resourcer = this;
|
||||
ctx.resourcer = self;
|
||||
|
||||
let params = parseRequest(
|
||||
{
|
||||
@ -322,8 +324,8 @@ export class ResourceManager {
|
||||
method: ctx.request.method,
|
||||
},
|
||||
{
|
||||
prefix: this.options.prefix || prefix,
|
||||
accessors: this.options.accessors || accessors,
|
||||
prefix: self.options.prefix || prefix,
|
||||
accessors: self.options.accessors || accessors,
|
||||
},
|
||||
);
|
||||
|
||||
@ -332,7 +334,7 @@ export class ResourceManager {
|
||||
}
|
||||
|
||||
try {
|
||||
const resource = this.getResource(getNameByParams(params));
|
||||
const resource = self.getResource(getNameByParams(params));
|
||||
|
||||
// 为关系资源时,暂时需要再执行一遍 parseRequest
|
||||
if (resource.options.type && resource.options.type !== 'single') {
|
||||
@ -343,8 +345,8 @@ export class ResourceManager {
|
||||
type: resource.options.type,
|
||||
},
|
||||
{
|
||||
prefix: this.options.prefix || prefix,
|
||||
accessors: this.options.accessors || accessors,
|
||||
prefix: self.options.prefix || prefix,
|
||||
accessors: self.options.accessors || accessors,
|
||||
},
|
||||
);
|
||||
|
||||
@ -354,7 +356,7 @@ export class ResourceManager {
|
||||
}
|
||||
|
||||
// action 需要 clone 之后再赋给 ctx
|
||||
ctx.action = this.getAction(getNameByParams(params), params.actionName).clone();
|
||||
ctx.action = self.getAction(getNameByParams(params), params.actionName).clone();
|
||||
|
||||
ctx.action.setContext(ctx);
|
||||
ctx.action.actionName = params.actionName;
|
||||
|
@ -24,7 +24,16 @@ import {
|
||||
} from '@nocobase/logger';
|
||||
import { ResourceOptions, Resourcer } from '@nocobase/resourcer';
|
||||
import { Telemetry, TelemetryOptions } from '@nocobase/telemetry';
|
||||
import { applyMixins, AsyncEmitter, importModule, Toposort, ToposortOptions } from '@nocobase/utils';
|
||||
|
||||
import {
|
||||
applyMixins,
|
||||
AsyncEmitter,
|
||||
importModule,
|
||||
Toposort,
|
||||
ToposortOptions,
|
||||
wrapMiddlewareWithLogging,
|
||||
} from '@nocobase/utils';
|
||||
|
||||
import { Command, CommandOptions, ParseOptions } from 'commander';
|
||||
import { randomUUID } from 'crypto';
|
||||
import glob from 'glob';
|
||||
@ -50,7 +59,6 @@ import {
|
||||
enablePerfHooks,
|
||||
getCommandFullName,
|
||||
registerMiddlewares,
|
||||
wrapMiddlewareWithLogging,
|
||||
} from './helper';
|
||||
import { ApplicationVersion } from './helpers/application-version';
|
||||
import { Locale } from './locale';
|
||||
@ -466,10 +474,7 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
|
||||
middleware: Koa.Middleware<StateT & NewStateT, ContextT & NewContextT>,
|
||||
options?: ToposortOptions,
|
||||
) {
|
||||
this.middleware.add(
|
||||
wrapMiddlewareWithLogging(middleware, middleware.toString().slice(0, 100), this.logger),
|
||||
options,
|
||||
);
|
||||
this.middleware.add(wrapMiddlewareWithLogging(middleware, this.logger), options);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -1197,6 +1202,7 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
|
||||
group: 'parseVariables',
|
||||
after: 'acl',
|
||||
});
|
||||
|
||||
this._dataSourceManager.use(dataTemplate, { group: 'dataTemplate', after: 'acl' });
|
||||
|
||||
this._locales = new Locale(createAppProxy(this));
|
||||
|
@ -76,7 +76,7 @@ export function registerMiddlewares(app: Application, options: ApplicationOption
|
||||
);
|
||||
}
|
||||
|
||||
app.use(async (ctx, next) => {
|
||||
app.use(async function getBearerToken(ctx, next) {
|
||||
ctx.getBearerToken = () => {
|
||||
const token = ctx.get('Authorization').replace(/^Bearer\s+/gi, '');
|
||||
return token || ctx.query.token;
|
||||
@ -158,22 +158,3 @@ export const enablePerfHooks = (app: Application) => {
|
||||
|
||||
app.acl.allow('perf', '*', 'public');
|
||||
};
|
||||
|
||||
export function wrapMiddlewareWithLogging(fn, name, logger) {
|
||||
if (process.env['LOGGER_LEVEL'] !== 'trace') {
|
||||
return fn;
|
||||
}
|
||||
|
||||
return async (ctx, next) => {
|
||||
const reqId = ctx.reqId;
|
||||
logger.trace(`--> Entering middleware: ${name}`, { reqId });
|
||||
const start = Date.now();
|
||||
|
||||
await fn(ctx, async () => {
|
||||
await next();
|
||||
});
|
||||
|
||||
const ms = Date.now() - start;
|
||||
logger.trace(`<-- Exiting middleware: ${name} - ${ms}ms`, { reqId });
|
||||
};
|
||||
}
|
||||
|
@ -28,4 +28,3 @@ export {
|
||||
} from './plugin-manager/findPackageNames';
|
||||
|
||||
export { runPluginStaticImports } from './run-plugin-static-imports';
|
||||
export { wrapMiddlewareWithLogging } from './helper';
|
||||
|
@ -10,7 +10,7 @@
|
||||
import { Context } from '@nocobase/actions';
|
||||
import { Collection } from '@nocobase/database';
|
||||
|
||||
export const dataTemplate = async (ctx: Context, next) => {
|
||||
export async function dataTemplate(ctx: Context, next) {
|
||||
const { resourceName, actionName } = ctx.action;
|
||||
const { isTemplate, fields } = ctx.action.params;
|
||||
|
||||
@ -22,7 +22,7 @@ export const dataTemplate = async (ctx: Context, next) => {
|
||||
include: fields,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
type TraverseOptions = {
|
||||
collection: Collection;
|
||||
|
@ -36,7 +36,7 @@ function isNumeric(str: any) {
|
||||
return !isNaN(str as any) && !isNaN(parseFloat(str));
|
||||
}
|
||||
|
||||
export const parseVariables = async (ctx, next) => {
|
||||
export async function parseVariables(ctx, next) {
|
||||
const filter = ctx.action.params.filter;
|
||||
if (!filter) {
|
||||
return next();
|
||||
@ -66,4 +66,4 @@ export const parseVariables = async (ctx, next) => {
|
||||
},
|
||||
});
|
||||
await next();
|
||||
};
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ export * from './toposort';
|
||||
export * from './uid';
|
||||
export * from './url';
|
||||
export * from './i18n';
|
||||
export * from './wrap-middleware';
|
||||
|
||||
export { dayjs, lodash };
|
||||
export { Schema } from '@formily/json-schema';
|
||||
|
36
packages/core/utils/src/wrap-middleware.ts
Normal file
36
packages/core/utils/src/wrap-middleware.ts
Normal file
@ -0,0 +1,36 @@
|
||||
export function wrapMiddlewareWithLogging(fn, logger?) {
|
||||
if (process.env['LOGGER_LEVEL'] !== 'trace') {
|
||||
return fn;
|
||||
}
|
||||
|
||||
const name = fn.name || fn.toString().slice(0, 100);
|
||||
|
||||
return async (ctx, next) => {
|
||||
const reqId = ctx.reqId;
|
||||
|
||||
if (!logger && !ctx.logger) {
|
||||
return await fn(ctx, next);
|
||||
}
|
||||
|
||||
if (!logger && ctx.logger) {
|
||||
logger = ctx.logger;
|
||||
}
|
||||
|
||||
logger.trace(`--> Entering middleware: ${name}`, { reqId });
|
||||
|
||||
const start = Date.now();
|
||||
|
||||
await fn(ctx, async () => {
|
||||
const beforeNext = Date.now();
|
||||
logger.trace(`--> Before next middleware: ${name} - ${beforeNext - start}ms`, { reqId });
|
||||
|
||||
await next();
|
||||
|
||||
const afterNext = Date.now();
|
||||
logger.trace(`<-- After next middleware: ${name} - ${afterNext - beforeNext}ms`, { reqId });
|
||||
});
|
||||
|
||||
const ms = Date.now() - start;
|
||||
logger.trace(`<-- Exiting middleware: ${name} - ${ms}ms`, { reqId });
|
||||
};
|
||||
}
|
@ -452,7 +452,7 @@ export class PluginACLServer extends Plugin {
|
||||
};
|
||||
});
|
||||
|
||||
this.app.resourcer.use(async (ctx, next) => {
|
||||
this.app.resourceManager.use(async function showAnonymous(ctx, next) {
|
||||
const { actionName, resourceName, params } = ctx.action;
|
||||
const { showAnonymous } = params || {};
|
||||
if (actionName === 'list' && resourceName === 'roles') {
|
||||
@ -578,7 +578,7 @@ export class PluginACLServer extends Plugin {
|
||||
|
||||
// append allowedActions to list & get response
|
||||
this.app.use(
|
||||
async (ctx, next) => {
|
||||
async function withACLMetaMiddleware(ctx, next) {
|
||||
try {
|
||||
await withACLMeta(ctx, next);
|
||||
} catch (error) {
|
||||
|
@ -304,7 +304,7 @@ export class PluginDataSourceMainServer extends Plugin {
|
||||
|
||||
this.app.on('beforeStart', loadCollections);
|
||||
|
||||
this.app.resourcer.use(async (ctx, next) => {
|
||||
this.app.resourceManager.use(async function pushUISchemaWhenUpdateCollectionField(ctx, next) {
|
||||
const { resourceName, actionName } = ctx.action;
|
||||
if (resourceName === 'collections.fields' && actionName === 'update') {
|
||||
const { updateAssociationValues = [] } = ctx.action.params;
|
||||
@ -384,7 +384,7 @@ export class PluginDataSourceMainServer extends Plugin {
|
||||
},
|
||||
);
|
||||
|
||||
this.app.resourcer.use(async (ctx, next) => {
|
||||
this.app.resourceManager.use(async function mergeReverseFieldWhenSaveCollectionField(ctx, next) {
|
||||
if (ctx.action.resourceName === 'collections.fields' && ['create', 'update'].includes(ctx.action.actionName)) {
|
||||
ctx.action.mergeParams({
|
||||
updateAssociationValues: ['reverseField'],
|
||||
@ -425,7 +425,7 @@ export class PluginDataSourceMainServer extends Plugin {
|
||||
}
|
||||
};
|
||||
|
||||
this.app.resourcer.use(async (ctx, next) => {
|
||||
this.app.resourceManager.use(async function handleFieldSourceMiddleware(ctx, next) {
|
||||
await next();
|
||||
|
||||
// handle collections:list
|
||||
|
@ -492,8 +492,9 @@ export class PluginDataSourceManagerServer extends Plugin {
|
||||
});
|
||||
});
|
||||
|
||||
const self = this;
|
||||
// add global roles check
|
||||
this.app.resourcer.use(async (ctx, next) => {
|
||||
this.app.resourceManager.use(async function appendDataToRolesCheck(ctx, next) {
|
||||
const action = ctx.action;
|
||||
await next();
|
||||
const { resourceName, actionName } = action.params;
|
||||
@ -503,12 +504,12 @@ export class PluginDataSourceManagerServer extends Plugin {
|
||||
|
||||
ctx.bodyMeta = {
|
||||
dataSources: dataSources.reduce((carry, dataSourceModel) => {
|
||||
const dataSource = this.app.dataSourceManager.dataSources.get(dataSourceModel.get('key'));
|
||||
const dataSource = self.app.dataSourceManager.dataSources.get(dataSourceModel.get('key'));
|
||||
if (!dataSource) {
|
||||
return carry;
|
||||
}
|
||||
|
||||
const dataSourceStatus = this.dataSourceStatus[dataSourceModel.get('key')];
|
||||
const dataSourceStatus = self.dataSourceStatus[dataSourceModel.get('key')];
|
||||
if (dataSourceStatus !== 'loaded') {
|
||||
return carry;
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ export class PluginFieldChinaRegionServer extends Plugin {
|
||||
|
||||
this.app.acl.allow('chinaRegions', 'list', 'loggedIn');
|
||||
|
||||
this.app.resourcer.use(async (ctx, next) => {
|
||||
this.app.resourceManager.use(async function blockChinaRegionList(ctx, next) {
|
||||
const { resourceName, actionName } = ctx.action.params;
|
||||
|
||||
if (resourceName == 'chinaRegions' && actionName !== 'list') {
|
||||
|
@ -153,7 +153,7 @@ export default class PluginUsersServer extends Plugin {
|
||||
}
|
||||
|
||||
async load() {
|
||||
this.app.resourceManager.use(async (ctx, next) => {
|
||||
this.app.resourceManager.use(async function deleteRolesCache(ctx, next) {
|
||||
await next();
|
||||
const { associatedName, resourceName, actionName, values } = ctx.action.params;
|
||||
if (
|
||||
|
@ -138,16 +138,17 @@ export default class PluginVerficationServer extends Plugin {
|
||||
await initProviders(this);
|
||||
initActions(this);
|
||||
|
||||
const self = this;
|
||||
// add middleware to action
|
||||
app.resourcer.use(async (context, next) => {
|
||||
app.resourceManager.use(async function verificationIntercept(context, next) {
|
||||
const { resourceName, actionName, values } = context.action.params;
|
||||
const key = `${resourceName}:${actionName}`;
|
||||
const interceptor = this.interceptors.get(key);
|
||||
const interceptor = self.interceptors.get(key);
|
||||
if (!interceptor || interceptor.manual) {
|
||||
return next();
|
||||
}
|
||||
|
||||
return this.intercept(context, next);
|
||||
return self.intercept(context, next);
|
||||
});
|
||||
|
||||
app.acl.allow('verifications', 'create', 'public');
|
||||
|
@ -24,7 +24,25 @@ export default class extends Trigger {
|
||||
constructor(workflow: WorkflowPlugin) {
|
||||
super(workflow);
|
||||
|
||||
workflow.app.dataSourceManager.use(this.middleware);
|
||||
const self = this;
|
||||
|
||||
async function triggerWorkflowActionMiddleware(context: Context, next: Next) {
|
||||
const { resourceName, actionName } = context.action;
|
||||
|
||||
if (resourceName === 'workflows' && actionName === 'trigger') {
|
||||
return self.workflowTriggerAction(context, next);
|
||||
}
|
||||
|
||||
await next();
|
||||
|
||||
if (!['create', 'update'].includes(actionName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return self.collectionTriggerAction(context);
|
||||
}
|
||||
|
||||
workflow.app.dataSourceManager.use(triggerWorkflowActionMiddleware);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -43,22 +61,6 @@ export default class extends Trigger {
|
||||
return this.collectionTriggerAction(context);
|
||||
}
|
||||
|
||||
middleware = async (context: Context, next: Next) => {
|
||||
const { resourceName, actionName } = context.action;
|
||||
|
||||
if (resourceName === 'workflows' && actionName === 'trigger') {
|
||||
return this.workflowTriggerAction(context, next);
|
||||
}
|
||||
|
||||
await next();
|
||||
|
||||
if (!['create', 'update'].includes(actionName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.collectionTriggerAction(context);
|
||||
};
|
||||
|
||||
private async collectionTriggerAction(context: Context) {
|
||||
const {
|
||||
resourceName,
|
||||
|
Loading…
x
Reference in New Issue
Block a user