Move server into services

This commit is contained in:
Alexandre Bodin 2021-09-02 22:09:17 +02:00
parent 58dde6837f
commit e4abd6f4dc
8 changed files with 212 additions and 194 deletions

View File

@ -6,12 +6,12 @@ const { Database } = require('@strapi/database');
const loadConfiguration = require('./core/app-configuration');
const { createServer } = require('./server');
const { createContainer } = require('./container');
const utils = require('./utils');
const initializeMiddlewares = require('./middlewares');
const createStrapiFs = require('./services/fs');
const createEventHub = require('./services/event-hub');
const { createServer } = require('./services/server');
const createWebhookRunner = require('./services/webhook-runner');
const { webhookModel, createWebhookStore } = require('./services/webhook-store');
const { createCoreStore, coreStoreModel } = require('./services/core-store');

View File

@ -0,0 +1,94 @@
'use strict';
const { toLower, castArray, trim } = require('lodash/fp');
const compose = require('koa-compose');
const { validateRouteConfig } = require('./route');
const { resolveMiddlewares } = require('./middleware');
const { resolvePolicies } = require('./policy');
const getMethod = route => trim(toLower(route.method));
const getPath = route => trim(route.path);
module.exports = strapi => {
return (routeConfig, { pluginName, router, apiName }) => {
validateRouteConfig(routeConfig);
try {
const method = getMethod(routeConfig);
const path = getPath(routeConfig);
const middlewares = resolveMiddlewares(routeConfig);
const policies = resolvePolicies(routeConfig, { pluginName, apiName });
const routeInfo = (ctx, next) => {
if (typeof routeConfig.handler === 'string') {
const [controllerName, actionName] = routeConfig.handler.split('.');
ctx.request.route = {
endpoint: `${method} ${path}`,
controller: toLower(controllerName),
action: toLower(actionName),
verb: toLower(method),
plugin: pluginName,
};
}
return next();
};
const auth = (ctx, next) => {
return next();
// if (routeConfig.auth && routeConfig.auth.public === true) {
// return next();
// }
// const { credentials, isAuthenticated = false } = ({} = ctx.state.auth);
// if (!isAuthenticated) {
// throw new ctx.unauthorized();
// }
// check credentials scope with routeConfig scope
};
const action = getAction(routeConfig, { pluginName, apiName }, strapi);
const handler = compose([routeInfo, auth, ...policies, ...middlewares, ...castArray(action)]);
router[method](path, handler);
} catch (error) {
throw new Error(
`Error creating endpoint ${routeConfig.method} ${routeConfig.path}: ${error.message}`
);
}
};
};
const getController = (name, { pluginName, apiName }, strapi) => {
if (pluginName) {
if (pluginName === 'admin') {
return strapi.controller(`admin::${name}`);
}
return strapi.plugin(pluginName).controller(name);
} else if (apiName) {
return strapi.controller(`api::${apiName}.${name}`);
}
return strapi.controller(name);
};
const getAction = ({ handler }, { pluginName, apiName }, strapi) => {
if (Array.isArray(handler) || typeof handler === 'function') {
return handler;
}
const [controllerName, actionName] = trim(handler).split('.');
const controller = getController(toLower(controllerName), { pluginName, apiName }, strapi);
if (typeof controller[actionName] !== 'function') {
throw new Error(`Handler not found "${handler}"`);
}
return controller[actionName].bind(controller);
};

View File

@ -6,7 +6,7 @@ const Router = require('@koa/router');
const { createHTTPServer } = require('./http-server');
const createEndpointComposer = require('./utils/compose-endpoint');
const createEndpointComposer = require('./compose-endpoint');
const createRouteManager = strapi => {
const composeEndpoint = createEndpointComposer(strapi);
@ -95,7 +95,7 @@ const createServer = strapi => {
const apis = {
admin: createAPI(strapi, { prefix: '/admin' }),
// TODO: set prefix to api
// { prefix: strapi.config.get('api.prefix', '/api') }
'content-api': createAPI(strapi),
};

View File

@ -0,0 +1,27 @@
'use strict';
const { propOr } = require('lodash/fp');
const getMiddlewareConfig = propOr([], 'config.middlewares');
const resolveMiddlewares = route => {
const middlewaresConfig = getMiddlewareConfig(route);
return middlewaresConfig.map(middlewareConfig => {
if (typeof middlewareConfig === 'function') {
return middlewareConfig;
}
const middleware = strapi.middleware(middlewareConfig);
if (!middleware) {
throw new Error(`Middleware ${middlewareConfig} not found.`);
}
return middleware;
});
};
module.exports = {
resolveMiddlewares,
};

View File

@ -0,0 +1,19 @@
'use strict';
const { propOr } = require('lodash/fp');
const policy = require('@strapi/utils/lib/policy');
const { bodyPolicy } = policy;
const getPoliciesConfig = propOr([], 'config.policies');
const resolvePolicies = (route, opts = {}) => {
const policiesConfig = getPoliciesConfig(route);
const policies = policiesConfig.map(policyName => policy.get(policyName, opts));
return [...policies, bodyPolicy];
};
module.exports = {
resolvePolicies,
};

View File

@ -0,0 +1,69 @@
'use strict';
const { yup } = require('@strapi/utils');
const policyOrMiddlewareSchema = yup.lazy(value => {
if (typeof value === 'string') {
return yup.string().required();
}
if (typeof value === 'function') {
return yup.mixed().isFunction();
}
return yup.object({
name: yup.string().required(),
options: yup.object().notRequired(), // any options
});
});
const routeSchema = yup.object({
method: yup
.string()
.oneOf(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'ALL'])
.required(),
path: yup.string().required(),
handler: yup.lazy(value => {
if (typeof value === 'string') {
return yup.string().required();
}
if (Array.isArray(value)) {
return yup.array().required();
}
return yup
.mixed()
.isFunction()
.required();
}),
config: yup
.object({
policies: yup
.array()
.of(policyOrMiddlewareSchema)
.notRequired(),
middlwares: yup
.array()
.of(policyOrMiddlewareSchema)
.notRequired(),
})
.notRequired(),
});
const validateRouteConfig = routeConfig => {
try {
return routeSchema.validateSync(routeConfig, {
strict: true,
abortEarly: false,
stripUnknown: true,
});
} catch (error) {
console.error(error);
throw new Error('Invalid route config');
}
};
module.exports = {
validateRouteConfig,
};

View File

@ -1,191 +0,0 @@
'use strict';
const _ = require('lodash');
const compose = require('koa-compose');
const { yup, policy: policyUtils } = require('@strapi/utils');
const { bodyPolicy } = policyUtils;
const policyOrMiddlewareSchema = yup.lazy(value => {
if (typeof value === 'string') {
return yup.string().required();
}
if (typeof value === 'function') {
return yup.mixed().isFunction();
}
return yup.object({
name: yup.string().required(),
options: yup.object().notRequired(), // any options
});
});
const routeSchema = yup.object({
method: yup
.string()
.oneOf(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'ALL'])
.required(),
path: yup.string().required(),
handler: yup.lazy(value => {
if (typeof value === 'string') {
return yup.string().required();
}
if (Array.isArray(value)) {
return yup.array().required();
}
return yup
.mixed()
.isFunction()
.required();
}),
config: yup
.object({
policies: yup
.array()
.of(policyOrMiddlewareSchema)
.notRequired(),
middlwares: yup
.array()
.of(policyOrMiddlewareSchema)
.notRequired(),
})
.notRequired(),
});
const validateRouteConfig = routeConfig => {
try {
return routeSchema.validateSync(routeConfig, {
strict: true,
abortEarly: false,
stripUnknown: true,
});
} catch (error) {
console.error(error);
throw new Error('Invalid route config');
}
};
module.exports = strapi => {
const routerChecker = createRouteChecker(strapi);
return (routeConfig, { pluginName, router, apiName }) => {
validateRouteConfig(routeConfig);
try {
const middlewares = resolveMiddlewares(routeConfig);
const { method, endpoint, policies, action } = routerChecker(routeConfig, {
pluginName,
apiName,
});
if (_.isUndefined(action)) {
return strapi.log.warn(
`Ignored attempt to bind route '${routeConfig.method} ${routeConfig.path}' to unknown controller/action.`
);
}
router[method](endpoint, compose([...policies, ...middlewares, ..._.castArray(action)]));
} catch (error) {
throw new Error(
`Error creating endpoint ${routeConfig.method} ${routeConfig.path}: ${error.message}`
);
}
};
};
const resolveMiddlewares = route => {
const middlewaresConfig = _.get(route, 'config.middlewares', []);
return middlewaresConfig.map(middlewareConfig => {
if (typeof middlewareConfig === 'function') {
return middlewareConfig;
}
const middleware = strapi.middleware(middlewareConfig);
if (!middleware) {
throw new Error(`Middleware ${middlewareConfig} not found.`);
}
return middleware;
});
};
const getMethod = route => _.trim(_.toLower(route.method));
const getEndpoint = route => _.trim(route.path);
const getAction = ({ method, endpoint, handler }, { pluginName, apiName }, strapi) => {
// Define controller and action names.
const [controllerName, actionName] = _.trim(handler).split('.');
const controllerKey = _.toLower(controllerName);
let controller;
if (pluginName) {
if (pluginName === 'admin') {
controller = strapi.admin.controllers[controllerKey];
} else {
controller = strapi.plugin(pluginName).controller(controllerKey);
}
} else {
controller = strapi.container.get('controllers').get(`api::${apiName}.${controllerKey}`);
}
if (!_.isFunction(controller[actionName])) {
strapi.stopWithError(
`Error creating endpoint ${method} ${endpoint}: handler not found "${controllerKey}.${actionName}"`
);
}
return {
action: controller[actionName].bind(controller),
actionName,
controllerKey,
};
};
const createRouteChecker = strapi => {
return (value, { pluginName, apiName }) => {
const { handler } = value;
const method = getMethod(value);
const endpoint = getEndpoint(value);
const policies = [];
let action;
if (Array.isArray(handler) || typeof handler === 'function') {
action = handler;
} else {
const actionInfo = getAction({ method, endpoint, handler }, { pluginName, apiName }, strapi);
action = actionInfo.action;
const globalPolicy = policyUtils.globalPolicy({
controller: actionInfo.controllerKey,
action: actionInfo.actionName,
method,
endpoint,
plugin: pluginName,
});
policies.push(globalPolicy);
}
const policyOption = _.get(value, 'config.policies', []);
const routePolicies = policyOption.map(policyConfig =>
policyUtils.get(policyConfig, { pluginName, apiName })
);
// Init policies array.
policies.push(...routePolicies, bodyPolicy);
return {
method,
endpoint,
policies,
action,
};
};
};