mirror of
https://github.com/strapi/strapi.git
synced 2025-10-26 23:51:10 +00:00
commit
2d0f6af9e4
@ -1,5 +0,0 @@
|
||||
{
|
||||
"features-routes": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// eslint-disable-next-line node/no-extraneous-require
|
||||
const { features } = require('@strapi/strapi/lib/utils/ee');
|
||||
const routes = require('./routes');
|
||||
|
||||
module.exports = strapi => ({
|
||||
beforeInitialize() {
|
||||
strapi.config.middleware.load.before.unshift('features-routes');
|
||||
},
|
||||
|
||||
initialize() {
|
||||
loadFeaturesRoutes();
|
||||
},
|
||||
});
|
||||
|
||||
const loadFeaturesRoutes = () => {
|
||||
for (const [feature, getFeatureRoutes] of Object.entries(routes)) {
|
||||
if (features.isEnabled(feature)) {
|
||||
strapi.admin.routes.push(...getFeatureRoutes); // TODO
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1,7 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
// TODO: update load middleware to not load the admin middleware from here
|
||||
bootstrap: require('./bootstrap'),
|
||||
routes: require('./routes'),
|
||||
services: require('./services'),
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/roles',
|
||||
handler: 'role.create',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/roles/:id',
|
||||
handler: 'role.deleteOne',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/roles/batch-delete',
|
||||
handler: 'role.deleteMany',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
];
|
||||
65
packages/core/admin/ee/server/routes/index.js
Normal file
65
packages/core/admin/ee/server/routes/index.js
Normal file
@ -0,0 +1,65 @@
|
||||
'use strict';
|
||||
|
||||
// eslint-disable-next-line node/no-extraneous-require
|
||||
const { features } = require('@strapi/strapi/lib/utils/ee');
|
||||
const featuresRoutes = require('./features-routes');
|
||||
|
||||
const getFeaturesRoutes = () => {
|
||||
return Object.entries(featuresRoutes).flatMap(([featureName, featureRoutes]) => {
|
||||
if (features.isEnabled(featureName)) {
|
||||
return featureRoutes;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/roles',
|
||||
handler: 'role.create',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: {
|
||||
actions: ['admin::roles.create'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/roles/:id',
|
||||
handler: 'role.deleteOne',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: {
|
||||
actions: ['admin::roles.delete'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/roles/batch-delete',
|
||||
handler: 'role.deleteMany',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: {
|
||||
actions: ['admin::roles.delete'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
...getFeaturesRoutes(),
|
||||
];
|
||||
@ -95,7 +95,7 @@ describe('Provider Login', () => {
|
||||
test('It should fail with a public request', async () => {
|
||||
const res = await requests.public.get('/admin/providers/options');
|
||||
|
||||
expect(res.status).toBe(hasSSO ? 403 : 404);
|
||||
expect(res.status).toBe(hasSSO ? 401 : 404);
|
||||
});
|
||||
|
||||
test('It should fail with an authenticated request (restricted user)', async () => {
|
||||
@ -133,7 +133,7 @@ describe('Provider Login', () => {
|
||||
test('It should fail with a public request', async () => {
|
||||
const res = await requests.public.put('/admin/providers/options', { body: newOptions });
|
||||
|
||||
expect(res.status).toBe(hasSSO ? 403 : 405);
|
||||
expect(res.status).toBe(hasSSO ? 401 : 405);
|
||||
});
|
||||
|
||||
test('It should fail with an authenticated request (restricted user)', async () => {
|
||||
|
||||
@ -149,10 +149,6 @@ async function copyAdmin(dest) {
|
||||
|
||||
await fs.ensureDir(path.resolve(dest, 'config'));
|
||||
await fs.copy(path.resolve(adminPath, 'admin'), path.resolve(dest, 'admin'));
|
||||
await fs.copy(
|
||||
path.resolve(adminPath, 'server', 'config', 'layout.js'),
|
||||
path.resolve(dest, 'config', 'layout.js')
|
||||
);
|
||||
|
||||
// Copy package.json
|
||||
await fs.copy(path.resolve(adminPath, 'package.json'), path.resolve(dest, 'package.json'));
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
{
|
||||
"auth": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = strapi => ({
|
||||
initialize() {
|
||||
const passportMiddleware = strapi.admin.services.passport.init();
|
||||
|
||||
strapi.app.use(passportMiddleware);
|
||||
|
||||
strapi.app.use(async (ctx, next) => {
|
||||
if (
|
||||
ctx.request.header.authorization &&
|
||||
ctx.request.header.authorization.split(' ')[0] === 'Bearer'
|
||||
) {
|
||||
const token = ctx.request.header.authorization.split(' ')[1];
|
||||
|
||||
const { payload, isValid } = strapi.admin.services.token.decodeJwtToken(token);
|
||||
|
||||
if (isValid) {
|
||||
// request is made by an admin
|
||||
const admin = await strapi
|
||||
.query('admin::user')
|
||||
.findOne({ where: { id: payload.id }, populate: ['roles'] });
|
||||
|
||||
if (!admin || !(admin.isActive === true)) {
|
||||
return ctx.forbidden('Invalid credentials');
|
||||
}
|
||||
|
||||
ctx.state.admin = admin;
|
||||
ctx.state.user = admin;
|
||||
ctx.state.userAbility = await strapi.admin.services.permission.engine.generateUserAbility(
|
||||
admin
|
||||
);
|
||||
ctx.state.isAuthenticatedAdmin = true;
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
return next();
|
||||
});
|
||||
},
|
||||
});
|
||||
@ -1,6 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const forgotPasswordTemplate = require('./email-templates/forgot-password');
|
||||
|
||||
module.exports = {
|
||||
layout: require('./layout'),
|
||||
...require('./settings'),
|
||||
forgotPassword: {
|
||||
emailTemplate: forgotPasswordTemplate,
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
administrator: {
|
||||
actions: {
|
||||
create: 'Admin.create',
|
||||
update: 'Admin.update',
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -52,15 +52,13 @@ describe('Admin Controller', () => {
|
||||
describe('information', () => {
|
||||
beforeAll(() => {
|
||||
global.strapi = {
|
||||
app: {
|
||||
env: 'development',
|
||||
},
|
||||
config: {
|
||||
get: jest.fn(
|
||||
(key, value) =>
|
||||
({
|
||||
autoReload: undefined,
|
||||
'info.strapi': '1.0.0',
|
||||
environment: 'development',
|
||||
}[key] || value)
|
||||
),
|
||||
},
|
||||
@ -71,7 +69,11 @@ describe('Admin Controller', () => {
|
||||
test('Returns application information', async () => {
|
||||
const result = await adminController.information();
|
||||
|
||||
expect(global.strapi.config.get).toHaveBeenCalledTimes(2);
|
||||
expect(global.strapi.config.get.mock.calls).toEqual([
|
||||
['environment'],
|
||||
['autoReload', false],
|
||||
['info.strapi', null],
|
||||
]);
|
||||
expect(result.data).toBeDefined();
|
||||
expect(result.data).toStrictEqual({
|
||||
currentEnvironment: 'development',
|
||||
|
||||
@ -41,7 +41,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
async information() {
|
||||
const currentEnvironment = strapi.app.env;
|
||||
const currentEnvironment = strapi.config.get('environment');
|
||||
const autoReload = strapi.config.get('autoReload', false);
|
||||
const strapiVersion = strapi.config.get('info.strapi', null);
|
||||
const nodeVersion = process.version;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
module.exports = (ctx, next) => {
|
||||
if (!ctx.state.isAuthenticatedAdmin) {
|
||||
throw strapi.errors.forbidden();
|
||||
return ctx.unauthorized();
|
||||
}
|
||||
|
||||
return next();
|
||||
|
||||
@ -3,7 +3,57 @@
|
||||
|
||||
// const permissionsFieldsToPropertiesMigration = require('../migrations/permissions-fields-to-properties');
|
||||
|
||||
/**
|
||||
* Tries to authenticated admin user and calls next.
|
||||
* @param {KoaContext} ctx
|
||||
* @param {Middleware} next
|
||||
* @returns {undefined}
|
||||
*/
|
||||
const authMiddleware = async (ctx, next) => {
|
||||
if (!ctx.request.header.authorization) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (
|
||||
ctx.request.header.authorization &&
|
||||
ctx.request.header.authorization.split(' ')[0] === 'Bearer'
|
||||
) {
|
||||
const token = ctx.request.header.authorization.split(' ')[1];
|
||||
|
||||
const { payload, isValid } = strapi.admin.services.token.decodeJwtToken(token);
|
||||
|
||||
if (isValid) {
|
||||
const admin = await strapi
|
||||
.query('admin::user')
|
||||
.findOne({ where: { id: payload.id }, populate: ['roles'] });
|
||||
|
||||
if (!admin || !(admin.isActive === true)) {
|
||||
return ctx.unauthorized('Invalid credentials');
|
||||
}
|
||||
|
||||
// TODO: use simple user & isAuthenticated
|
||||
|
||||
ctx.state.admin = admin;
|
||||
ctx.state.user = admin;
|
||||
ctx.state.userAbility = await strapi.admin.services.permission.engine.generateUserAbility(
|
||||
admin
|
||||
);
|
||||
|
||||
ctx.state.isAuthenticatedAdmin = true;
|
||||
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
ctx.unauthorized('Invalid credentials');
|
||||
};
|
||||
|
||||
module.exports = () => {
|
||||
const passportMiddleware = strapi.admin.services.passport.init();
|
||||
|
||||
strapi.server.api('admin').use(passportMiddleware);
|
||||
strapi.server.api('admin').use(authMiddleware);
|
||||
|
||||
// FIXME: to implement
|
||||
// strapi.db.migrations.register(permissionsFieldsToPropertiesMigration);
|
||||
};
|
||||
|
||||
@ -1,17 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/plugins',
|
||||
handler: 'admin.plugins',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::marketplace.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/init',
|
||||
@ -30,6 +19,17 @@ module.exports = [
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/plugins',
|
||||
handler: 'admin.plugins',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['admin::marketplace.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/plugins/install',
|
||||
@ -44,7 +44,7 @@ describe('Authenticated User', () => {
|
||||
body: {},
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(403);
|
||||
expect(res.statusCode).toBe(401);
|
||||
});
|
||||
});
|
||||
|
||||
@ -57,7 +57,7 @@ describe('Authenticated User', () => {
|
||||
body: {},
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(403);
|
||||
expect(res.statusCode).toBe(401);
|
||||
});
|
||||
|
||||
test('Fails when trying to edit roles', async () => {
|
||||
|
||||
1
packages/core/content-manager/.gitignore
vendored
1
packages/core/content-manager/.gitignore
vendored
@ -2,7 +2,6 @@
|
||||
coverage
|
||||
node_modules
|
||||
stats.json
|
||||
config/layout.json
|
||||
package-lock.json
|
||||
|
||||
|
||||
|
||||
@ -12,10 +12,19 @@ module.exports = async (ctx, next) => {
|
||||
}
|
||||
|
||||
const target = ct.plugin === 'admin' ? strapi.admin : strapi.plugin(ct.plugin);
|
||||
|
||||
const { route } = ctx.state;
|
||||
|
||||
if (typeof route.handler !== 'string') {
|
||||
return next();
|
||||
}
|
||||
|
||||
const [, action] = route.handler.split('.');
|
||||
|
||||
const configPath =
|
||||
ct.plugin === 'admin'
|
||||
? ['server.admin.layout', ct.modelName, 'actions', ctx.request.route.action]
|
||||
: ['plugin', ct.plugin, 'layout', ct.modelName, 'actions', ctx.request.route.action];
|
||||
? ['server.admin.layout', ct.modelName, 'actions', action]
|
||||
: ['plugin', ct.plugin, 'layout', ct.modelName, 'actions', action];
|
||||
|
||||
const actionConfig = strapi.config.get(configPath);
|
||||
|
||||
|
||||
321
packages/core/content-manager/server/routes/admin.js
Normal file
321
packages/core/content-manager/server/routes/admin.js
Normal file
@ -0,0 +1,321 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
type: 'admin',
|
||||
routes: [
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/content-types',
|
||||
handler: 'content-types.findContentTypes',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/content-types-settings',
|
||||
handler: 'content-types.findContentTypesSettings',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/content-types/:uid/configuration',
|
||||
handler: 'content-types.findContentTypeConfiguration',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/content-types/:uid/configuration',
|
||||
handler: 'content-types.updateContentTypeConfiguration',
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/components',
|
||||
handler: 'components.findComponents',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/components/:uid/configuration',
|
||||
handler: 'components.findComponentConfiguration',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/components/:uid/configuration',
|
||||
handler: 'components.updateComponentConfiguration',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/uid/generate',
|
||||
handler: 'uid.generateUID',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/uid/check-availability',
|
||||
handler: 'uid.checkUIDAvailability',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/relations/:model/:targetField',
|
||||
handler: 'relations.find',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: {
|
||||
actions: [
|
||||
'plugin::content-manager.explorer.create',
|
||||
'plugin::content-manager.explorer.update',
|
||||
],
|
||||
hasAtLeastOne: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/single-types/:model',
|
||||
handler: 'single-types.find',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/single-types/:model',
|
||||
handler: 'single-types.createOrUpdate',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: {
|
||||
actions: [
|
||||
'plugin::content-manager.explorer.create',
|
||||
'plugin::content-manager.explorer.update',
|
||||
],
|
||||
hasAtLeastOne: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/single-types/:model',
|
||||
handler: 'single-types.delete',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.delete'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/single-types/:model/actions/publish',
|
||||
handler: 'single-types.publish',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'plugin::content-manager.has-draft-and-publish',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.publish'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/single-types/:model/actions/unpublish',
|
||||
handler: 'single-types.unpublish',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'plugin::content-manager.has-draft-and-publish',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.publish'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/collection-types/:model/:id/:targetField',
|
||||
handler: 'collection-types.previewManyRelations',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/collection-types/:model',
|
||||
handler: 'collection-types.find',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/collection-types/:model',
|
||||
handler: 'collection-types.create',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.create'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/collection-types/:model/:id',
|
||||
handler: 'collection-types.findOne',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/collection-types/:model/:id',
|
||||
handler: 'collection-types.update',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.update'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/collection-types/:model/:id',
|
||||
handler: 'collection-types.delete',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.delete'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/collection-types/:model/:id/actions/publish',
|
||||
handler: 'collection-types.publish',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'plugin::content-manager.has-draft-and-publish',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.publish'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/collection-types/:model/:id/actions/unpublish',
|
||||
handler: 'collection-types.unpublish',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'plugin::content-manager.has-draft-and-publish',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.publish'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/collection-types/:model/actions/bulkDelete',
|
||||
handler: 'collection-types.bulkDelete',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.delete'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -1,318 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/content-types',
|
||||
handler: 'content-types.findContentTypes',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/content-types-settings',
|
||||
handler: 'content-types.findContentTypesSettings',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/content-types/:uid/configuration',
|
||||
handler: 'content-types.findContentTypeConfiguration',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/content-types/:uid/configuration',
|
||||
handler: 'content-types.updateContentTypeConfiguration',
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/components',
|
||||
handler: 'components.findComponents',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/components/:uid/configuration',
|
||||
handler: 'components.findComponentConfiguration',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/components/:uid/configuration',
|
||||
handler: 'components.updateComponentConfiguration',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/uid/generate',
|
||||
handler: 'uid.generateUID',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/uid/check-availability',
|
||||
handler: 'uid.checkUIDAvailability',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/relations/:model/:targetField',
|
||||
handler: 'relations.find',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: {
|
||||
actions: [
|
||||
'plugin::content-manager.explorer.create',
|
||||
'plugin::content-manager.explorer.update',
|
||||
],
|
||||
hasAtLeastOne: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/single-types/:model',
|
||||
handler: 'single-types.find',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/single-types/:model',
|
||||
handler: 'single-types.createOrUpdate',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: {
|
||||
actions: [
|
||||
'plugin::content-manager.explorer.create',
|
||||
'plugin::content-manager.explorer.update',
|
||||
],
|
||||
hasAtLeastOne: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/single-types/:model',
|
||||
handler: 'single-types.delete',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.delete'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/single-types/:model/actions/publish',
|
||||
handler: 'single-types.publish',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'plugin::content-manager.has-draft-and-publish',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.publish'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/single-types/:model/actions/unpublish',
|
||||
handler: 'single-types.unpublish',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'plugin::content-manager.has-draft-and-publish',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.publish'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/collection-types/:model/:id/:targetField',
|
||||
handler: 'collection-types.previewManyRelations',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/collection-types/:model',
|
||||
handler: 'collection-types.find',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/collection-types/:model',
|
||||
handler: 'collection-types.create',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.create'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/collection-types/:model/:id',
|
||||
handler: 'collection-types.findOne',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/collection-types/:model/:id',
|
||||
handler: 'collection-types.update',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.update'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/collection-types/:model/:id',
|
||||
handler: 'collection-types.delete',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.delete'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/collection-types/:model/:id/actions/publish',
|
||||
handler: 'collection-types.publish',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'plugin::content-manager.has-draft-and-publish',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.publish'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/collection-types/:model/:id/actions/unpublish',
|
||||
handler: 'collection-types.unpublish',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'plugin::content-manager.has-draft-and-publish',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.publish'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/collection-types/:model/actions/bulkDelete',
|
||||
handler: 'collection-types.bulkDelete',
|
||||
config: {
|
||||
policies: [
|
||||
'routing',
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'plugin::content-manager.hasPermissions',
|
||||
options: { actions: ['plugin::content-manager.explorer.delete'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
module.exports = {
|
||||
admin: require('./admin'),
|
||||
};
|
||||
|
||||
176
packages/core/content-type-builder/server/routes/admin.js
Normal file
176
packages/core/content-type-builder/server/routes/admin.js
Normal file
@ -0,0 +1,176 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
type: 'admin',
|
||||
routes: [
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/reserved-names',
|
||||
handler: 'builder.getReservedNames',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/content-types',
|
||||
handler: 'content-types.getContentTypes',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/content-types/:uid',
|
||||
handler: 'content-types.getContentType',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/content-types',
|
||||
handler: 'content-types.createContentType',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/content-types/:uid',
|
||||
handler: 'content-types.updateContentType',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/content-types/:uid',
|
||||
handler: 'content-types.deleteContentType',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/components',
|
||||
handler: 'components.getComponents',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/components/:uid',
|
||||
handler: 'components.getComponent',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/components',
|
||||
handler: 'components.createComponent',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/components/:uid',
|
||||
handler: 'components.updateComponent',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/components/:uid',
|
||||
handler: 'components.deleteComponent',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/component-categories/:name',
|
||||
handler: 'component-categories.editCategory',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/component-categories/:name',
|
||||
handler: 'component-categories.deleteCategory',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
type: 'content-api',
|
||||
routes: [
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/content-types',
|
||||
handler: 'content-types.getContentTypes',
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/content-types/:uid',
|
||||
handler: 'content-types.getContentType',
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/components',
|
||||
handler: 'components.getComponents',
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/components/:uid',
|
||||
handler: 'components.getComponent',
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -1,173 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/reserved-names',
|
||||
handler: 'builder.getReservedNames',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/content-types',
|
||||
handler: 'content-types.getContentTypes',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/content-types/:uid',
|
||||
handler: 'content-types.getContentType',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/content-types',
|
||||
handler: 'content-types.createContentType',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/content-types/:uid',
|
||||
handler: 'content-types.updateContentType',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/content-types/:uid',
|
||||
handler: 'content-types.deleteContentType',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/components',
|
||||
handler: 'components.getComponents',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/components/:uid',
|
||||
handler: 'components.getComponent',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/components',
|
||||
handler: 'components.createComponent',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/components/:uid',
|
||||
handler: 'components.updateComponent',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/components/:uid',
|
||||
handler: 'components.deleteComponent',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/component-categories/:name',
|
||||
handler: 'component-categories.editCategory',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/component-categories/:name',
|
||||
handler: 'component-categories.deleteCategory',
|
||||
config: {
|
||||
policies: [
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: { actions: ['plugin::content-type-builder.read'] },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
module.exports = {
|
||||
admin: require('./admin'),
|
||||
'content-api': require('./content-api'),
|
||||
};
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
const _ = require('lodash');
|
||||
const { getOr } = require('lodash/fp');
|
||||
const strapiGenerators = require('@strapi/generators');
|
||||
|
||||
const { nameToSlug, contentTypes: contentTypesUtils } = require('@strapi/utils');
|
||||
const { formatAttributes, replaceTemporaryUIDs } = require('../utils/attributes');
|
||||
@ -127,6 +126,7 @@ const createContentType = async ({ contentType, components = [] }, options = {})
|
||||
* @param {string} name
|
||||
*/
|
||||
const generateAPI = ({ name, kind = 'collectionType' }) => {
|
||||
const strapiGenerators = require('@strapi/generators');
|
||||
return strapiGenerators.generate('api', { id: nameToSlug(name), kind }, { dir: strapi.dir });
|
||||
};
|
||||
|
||||
|
||||
@ -227,15 +227,7 @@ describe('Content Type Builder - Content types', () => {
|
||||
|
||||
// create data
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const res = await rq({
|
||||
method: 'POST',
|
||||
url: `/test-collections`,
|
||||
body: {
|
||||
title: 'Test',
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
await strapi.query(uid).create({ data: { title: 'Test' } });
|
||||
}
|
||||
|
||||
const updateRes = await rq({
|
||||
|
||||
@ -29,7 +29,6 @@ const toAssocs = data => {
|
||||
});
|
||||
};
|
||||
|
||||
// TODO: handle programmatic defaults
|
||||
const toRow = (metadata, data = {}, { withDefaults = false } = {}) => {
|
||||
const { attributes } = metadata;
|
||||
|
||||
@ -42,6 +41,7 @@ const toRow = (metadata, data = {}, { withDefaults = false } = {}) => {
|
||||
if (types.isScalar(attribute.type)) {
|
||||
const field = createField(attribute);
|
||||
|
||||
// TODO: move application level default to entity service
|
||||
if (_.isUndefined(data[attributeName])) {
|
||||
if (!_.isUndefined(attribute.default) && withDefaults) {
|
||||
if (typeof attribute.default === 'function') {
|
||||
@ -654,7 +654,10 @@ const createEntityManager = db => {
|
||||
.where(joinTable.on || {})
|
||||
.execute();
|
||||
|
||||
if (['oneToOne', 'oneToMany'].includes(attribute.relation)) {
|
||||
if (
|
||||
isBidirectional(attribute) &&
|
||||
['oneToOne', 'oneToMany'].includes(attribute.relation)
|
||||
) {
|
||||
await this.createQueryBuilder(joinTable.name)
|
||||
.delete()
|
||||
.where({ [inverseJoinColumn.name]: toIds(data[attributeName]) })
|
||||
|
||||
@ -10,6 +10,7 @@ const { isNil, pick } = require('lodash/fp');
|
||||
module.exports = {
|
||||
async send(ctx) {
|
||||
let options = ctx.request.body;
|
||||
|
||||
try {
|
||||
await strapi
|
||||
.plugin('email')
|
||||
|
||||
35
packages/core/email/server/routes/admin.js
Normal file
35
packages/core/email/server/routes/admin.js
Normal file
@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
type: 'admin',
|
||||
routes: [
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/',
|
||||
handler: 'email.send',
|
||||
config: {},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/test',
|
||||
handler: 'email.test',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['plugin::email.settings.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/settings',
|
||||
handler: 'email.getSettings',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['plugin::email.settings.read'] } },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
12
packages/core/email/server/routes/content-api.js
Normal file
12
packages/core/email/server/routes/content-api.js
Normal file
@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
type: 'content-api',
|
||||
routes: [
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/',
|
||||
handler: 'email.send',
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -1,49 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/',
|
||||
handler: 'Email.send',
|
||||
config: {
|
||||
policies: [],
|
||||
description: 'Send an email',
|
||||
tag: {
|
||||
plugin: 'email',
|
||||
name: 'Email',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/test',
|
||||
handler: 'Email.test',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['plugin::email.settings.read'] } },
|
||||
],
|
||||
description: 'Send an test email',
|
||||
tag: {
|
||||
plugin: 'email',
|
||||
name: 'Email',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/settings',
|
||||
handler: 'Email.getSettings',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{ name: 'admin::hasPermissions', options: { actions: ['plugin::email.settings.read'] } },
|
||||
],
|
||||
description: 'Get the email settings',
|
||||
tag: {
|
||||
plugin: 'email',
|
||||
name: 'Email',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
module.exports = {
|
||||
admin: require('./admin'),
|
||||
'content-api': require('./content-api'),
|
||||
};
|
||||
|
||||
@ -1,25 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
const Koa = require('koa');
|
||||
const Router = require('koa-router');
|
||||
const _ = require('lodash');
|
||||
const { createLogger } = require('@strapi/logger');
|
||||
const { Database } = require('@strapi/database');
|
||||
|
||||
const loadConfiguration = require('./core/app-configuration');
|
||||
|
||||
const { createHTTPServer } = 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');
|
||||
const createEntityService = require('./services/entity-service');
|
||||
const entityValidator = require('./services/entity-validator');
|
||||
const createTelemetry = require('./services/metrics');
|
||||
const createContentAPI = require('./services/content-api');
|
||||
const createUpdateNotifier = require('./utils/update-notifier');
|
||||
const createStartupLogger = require('./utils/startup-logger');
|
||||
const ee = require('./utils/ee');
|
||||
@ -54,16 +53,15 @@ class Strapi {
|
||||
this.container.register('modules', modulesRegistry(this));
|
||||
this.container.register('plugins', pluginsRegistry(this));
|
||||
this.container.register('apis', apisRegistry(this));
|
||||
this.container.register('content-api', createContentAPI(this));
|
||||
|
||||
this.isLoaded = false;
|
||||
this.reload = this.reload();
|
||||
this.app = new Koa();
|
||||
this.router = new Router();
|
||||
this.server = createHTTPServer(this, this.app);
|
||||
this.server = createServer(this);
|
||||
|
||||
this.fs = createStrapiFs(this);
|
||||
this.eventHub = createEventHub();
|
||||
this.startupLogger = createStartupLogger(this);
|
||||
this.app.proxy = this.config.get('server.proxy');
|
||||
this.log = createLogger(this.config.get('logger', {}));
|
||||
|
||||
createUpdateNotifier(this).notify();
|
||||
@ -123,9 +121,6 @@ class Strapi {
|
||||
await this.load();
|
||||
}
|
||||
|
||||
this.app.use(this.router.routes()).use(this.router.allowedMethods());
|
||||
|
||||
// Launch server.
|
||||
await this.listen();
|
||||
|
||||
return this;
|
||||
@ -268,15 +263,6 @@ class Strapi {
|
||||
}
|
||||
|
||||
async load() {
|
||||
this.app.use(async (ctx, next) => {
|
||||
if (ctx.request.url === '/_health' && ['HEAD', 'GET'].includes(ctx.request.method)) {
|
||||
ctx.set('strapi', 'You are so French!');
|
||||
ctx.status = 204;
|
||||
} else {
|
||||
await next();
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
this.loadPlugins(),
|
||||
this.loadAdmin(),
|
||||
@ -332,11 +318,12 @@ class Strapi {
|
||||
this.telemetry = createTelemetry(this);
|
||||
|
||||
// Initialize middlewares.
|
||||
await initializeMiddlewares.call(this);
|
||||
await initializeMiddlewares(this);
|
||||
|
||||
await this.runLifecyclesFunctions(LIFECYCLES.BOOTSTRAP);
|
||||
|
||||
this.isLoaded = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -403,7 +390,7 @@ class Strapi {
|
||||
await execLifecycle(this.config.get(configPath));
|
||||
|
||||
// admin
|
||||
await this.admin[lifecycleName]();
|
||||
await this.admin[lifecycleName](this);
|
||||
}
|
||||
|
||||
getModel(uid) {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
'use strict';
|
||||
const { green } = require('chalk');
|
||||
|
||||
// eslint-disable-next-line node/no-extraneous-require
|
||||
const strapiAdmin = require('@strapi/admin');
|
||||
const { getConfigUrls } = require('@strapi/utils');
|
||||
const loadConfiguration = require('../core/app-configuration');
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
// eslint-disable-next-line node/no-extraneous-require
|
||||
const strapiAdmin = require('@strapi/admin');
|
||||
const { getOr } = require('lodash/fp');
|
||||
const { getConfigUrls, getAbsoluteServerUrl } = require('@strapi/utils');
|
||||
|
||||
4
packages/core/strapi/lib/core/bootstrap.js
vendored
4
packages/core/strapi/lib/core/bootstrap.js
vendored
@ -11,6 +11,10 @@ module.exports = function(strapi) {
|
||||
});
|
||||
});
|
||||
|
||||
_.forEach(strapi.admin.middlewares, (middleware, middlewareName) => {
|
||||
strapi.middleware[middlewareName] = middleware;
|
||||
});
|
||||
|
||||
_.forEach(strapi.plugins, plugin => {
|
||||
_.forEach(plugin.middlewares, (middleware, middlewareName) => {
|
||||
strapi.middleware[middlewareName] = middleware;
|
||||
|
||||
@ -2,10 +2,8 @@
|
||||
|
||||
// Dependencies.
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const _ = require('lodash');
|
||||
const glob = require('../../load/glob');
|
||||
const findPackagePath = require('../../load/package-path');
|
||||
|
||||
/**
|
||||
* Load middlewares
|
||||
@ -23,12 +21,6 @@ module.exports = async function(strapi) {
|
||||
await loaders.loadInternalMiddlewares(middlewares);
|
||||
// local middleware
|
||||
await loaders.loadLocalMiddlewares(appPath, middlewares);
|
||||
// plugins middlewares
|
||||
await loaders.loadPluginsMiddlewares(strapi.plugins, middlewares);
|
||||
// local plugin middlewares
|
||||
await loaders.loadLocalPluginsMiddlewares(appPath, middlewares);
|
||||
// load admin middlewares
|
||||
await loaders.loadAdminMiddlewares(middlewares);
|
||||
|
||||
return middlewares;
|
||||
};
|
||||
@ -55,45 +47,6 @@ const createLoaders = strapi => {
|
||||
const loadLocalMiddlewares = (appPath, middlewares) =>
|
||||
loadMiddlewaresInDir(path.resolve(appPath, 'middlewares'), middlewares);
|
||||
|
||||
const loadPluginsMiddlewares = async (plugins, middlewares) => {
|
||||
for (const pluginName in plugins) {
|
||||
const pluginMiddlewares = plugins[pluginName].middlewares;
|
||||
for (const middlewareName in pluginMiddlewares) {
|
||||
middlewares[middlewareName] = {
|
||||
loaded: false,
|
||||
...pluginMiddlewares[middlewareName],
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const loadLocalPluginsMiddlewares = async (appPath, middlewares) => {
|
||||
const pluginsDir = path.resolve(appPath, 'plugins');
|
||||
if (!fs.existsSync(pluginsDir)) return;
|
||||
|
||||
const pluginsNames = await fs.readdir(pluginsDir);
|
||||
|
||||
for (let pluginFolder of pluginsNames) {
|
||||
// ignore files
|
||||
const stat = await fs.stat(path.resolve(pluginsDir, pluginFolder));
|
||||
if (!stat.isDirectory()) continue;
|
||||
|
||||
const dir = path.resolve(pluginsDir, pluginFolder, 'middlewares');
|
||||
await loadMiddlewaresInDir(dir, middlewares);
|
||||
}
|
||||
};
|
||||
|
||||
const loadAdminMiddlewares = async middlewares => {
|
||||
const middlewaresDir = 'middlewares';
|
||||
const dir = path.resolve(findPackagePath(`@strapi/admin`), middlewaresDir);
|
||||
await loadMiddlewaresInDir(dir, middlewares);
|
||||
|
||||
// load ee admin middlewares if they exist
|
||||
if (process.env.STRAPI_DISABLE_EE !== 'true' && strapi.EE) {
|
||||
await loadMiddlewaresInDir(`${dir}/../ee/${middlewaresDir}`, middlewares);
|
||||
}
|
||||
};
|
||||
|
||||
const loadMiddlewareDependencies = async (packages, middlewares) => {
|
||||
for (let packageName of packages) {
|
||||
const baseDir = path.dirname(require.resolve(`@strapi/middleware-${packageName}`));
|
||||
@ -128,9 +81,6 @@ const createLoaders = strapi => {
|
||||
return {
|
||||
loadInternalMiddlewares,
|
||||
loadLocalMiddlewares,
|
||||
loadPluginsMiddlewares,
|
||||
loadLocalPluginsMiddlewares,
|
||||
loadMiddlewareDependencies,
|
||||
loadAdminMiddlewares,
|
||||
};
|
||||
};
|
||||
|
||||
@ -76,6 +76,7 @@ const getEnabledPlugins = async strapi => {
|
||||
const userPluginsConfig = existsSync(userPluginConfigPath)
|
||||
? loadConfigFile(userPluginConfigPath)
|
||||
: {};
|
||||
|
||||
_.forEach(userPluginsConfig, (declaration, pluginName) => {
|
||||
validatePluginName(pluginName);
|
||||
declaredPlugins[pluginName] = toDetailedDeclaration(declaration);
|
||||
@ -86,6 +87,7 @@ const getEnabledPlugins = async strapi => {
|
||||
p => !declaredPluginsResolves.includes(p.pathToPlugin),
|
||||
installedPlugins
|
||||
);
|
||||
|
||||
const enabledPlugins = pipe(
|
||||
defaultsDeep(declaredPlugins),
|
||||
defaultsDeep(installedPluginsNotAlreadyUsed),
|
||||
|
||||
@ -87,6 +87,7 @@ const applyUserConfig = plugins => {
|
||||
|
||||
const loadPlugins = async strapi => {
|
||||
const plugins = {};
|
||||
|
||||
const enabledPlugins = await getEnabledPlugins(strapi);
|
||||
|
||||
for (const pluginName in enabledPlugins) {
|
||||
@ -94,6 +95,7 @@ const loadPlugins = async strapi => {
|
||||
const pluginServer = loadConfigFile(join(enabledPlugin.pathToPlugin, 'strapi-server.js'));
|
||||
plugins[pluginName] = defaultsDeep(defaultPlugin, pluginServer);
|
||||
}
|
||||
|
||||
// TODO: validate plugin format
|
||||
applyUserConfig(plugins);
|
||||
await applyUserExtension(plugins);
|
||||
|
||||
@ -17,8 +17,8 @@ const pluginsRegistry = strapi => {
|
||||
throw new Error(`Plugin ${name} has already been registered.`);
|
||||
}
|
||||
|
||||
const moduleInstance = strapi.container.get('modules').add(`plugin::${name}`, pluginConfig);
|
||||
plugins[name] = moduleInstance;
|
||||
const pluginModule = strapi.container.get('modules').add(`plugin::${name}`, pluginConfig);
|
||||
plugins[name] = pluginModule;
|
||||
|
||||
return plugins[name];
|
||||
},
|
||||
|
||||
@ -65,18 +65,18 @@ module.exports = strapi => {
|
||||
*/
|
||||
|
||||
initialize() {
|
||||
this.delegator = delegate(strapi.app.context, 'response');
|
||||
this.delegator = delegate(strapi.server.app.context, 'response');
|
||||
this.createResponses();
|
||||
|
||||
strapi.errors = Boom;
|
||||
strapi.app.use(async (ctx, next) => {
|
||||
strapi.server.use(async (ctx, next) => {
|
||||
try {
|
||||
// App logic.
|
||||
await next();
|
||||
} catch (error) {
|
||||
// emit error if configured
|
||||
if (strapi.config.get('server.emitErrors', false)) {
|
||||
strapi.app.emit('error', error, ctx);
|
||||
strapi.server.app.emit('error', error, ctx);
|
||||
}
|
||||
|
||||
// Log error.
|
||||
@ -92,10 +92,10 @@ module.exports = strapi => {
|
||||
}
|
||||
});
|
||||
|
||||
strapi.app.use(async (ctx, next) => {
|
||||
strapi.server.use(async (ctx, next) => {
|
||||
await next();
|
||||
// Empty body is considered as `notFound` response.
|
||||
if (_.isNil(ctx.body) && _.isNil(ctx.status)) {
|
||||
if (_.isNil(ctx.body) && (_.isNil(ctx.status) || ctx.status === 404)) {
|
||||
ctx.notFound();
|
||||
}
|
||||
});
|
||||
@ -104,7 +104,7 @@ module.exports = strapi => {
|
||||
// Custom function to avoid ctx.body repeat
|
||||
createResponses() {
|
||||
boomMethods.forEach(method => {
|
||||
strapi.app.response[method] = function(msg, ...rest) {
|
||||
strapi.server.app.response[method] = function(msg, ...rest) {
|
||||
const boomError = Boom[method](msg, ...rest) || {};
|
||||
|
||||
const { status, body } = formatBoomPayload(boomError);
|
||||
@ -119,17 +119,17 @@ module.exports = strapi => {
|
||||
this.delegator.method(method);
|
||||
});
|
||||
|
||||
strapi.app.response.send = function(data, status = 200) {
|
||||
strapi.server.app.response.send = function(data, status = 200) {
|
||||
this.status = status;
|
||||
this.body = data;
|
||||
};
|
||||
|
||||
strapi.app.response.created = function(data) {
|
||||
strapi.server.app.response.created = function(data) {
|
||||
this.status = 201;
|
||||
this.body = data;
|
||||
};
|
||||
|
||||
strapi.app.response.deleted = function(data) {
|
||||
strapi.server.app.response.deleted = function(data) {
|
||||
if (_.isNil(data)) {
|
||||
this.status = 204;
|
||||
} else {
|
||||
|
||||
@ -30,7 +30,7 @@ module.exports = strapi => {
|
||||
keepHeadersOnError,
|
||||
} = Object.assign({}, defaults, strapi.config.get('middleware.settings.cors'));
|
||||
|
||||
strapi.app.use(
|
||||
strapi.server.use(
|
||||
cors({
|
||||
origin: async function(ctx) {
|
||||
let originList;
|
||||
|
||||
@ -22,7 +22,7 @@ module.exports = strapi => {
|
||||
const { dir } = strapi;
|
||||
const { maxAge, path: faviconPath } = strapi.config.middleware.settings.favicon;
|
||||
|
||||
strapi.app.use(
|
||||
strapi.server.use(
|
||||
favicon(resolve(dir, faviconPath), {
|
||||
maxAge,
|
||||
})
|
||||
|
||||
@ -13,7 +13,7 @@ module.exports = strapi => {
|
||||
|
||||
initialize() {
|
||||
const { options = {} } = strapi.config.middleware.settings.gzip;
|
||||
strapi.app.use(compress(options));
|
||||
strapi.server.use(compress(options));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@ -4,6 +4,6 @@ const helmet = require('koa-helmet');
|
||||
|
||||
module.exports = strapi => ({
|
||||
initialize() {
|
||||
strapi.app.use(helmet(strapi.config.middleware.settings.helmet));
|
||||
strapi.server.use(helmet(strapi.config.middleware.settings.helmet));
|
||||
},
|
||||
});
|
||||
|
||||
@ -15,13 +15,13 @@ const requiredMiddlewares = [
|
||||
'favicon',
|
||||
];
|
||||
|
||||
module.exports = async function() {
|
||||
module.exports = async function(strapi) {
|
||||
/** Utils */
|
||||
const middlewareConfig = this.config.middleware;
|
||||
const middlewareConfig = strapi.config.middleware;
|
||||
|
||||
// check if a middleware exists
|
||||
const middlewareExists = key => {
|
||||
return !isUndefined(this.middleware[key]);
|
||||
return !isUndefined(strapi.middleware[key]);
|
||||
};
|
||||
|
||||
// check if a middleware is enabled
|
||||
@ -33,26 +33,26 @@ module.exports = async function() {
|
||||
};
|
||||
|
||||
// list of enabled middlewares
|
||||
const enabledMiddlewares = Object.keys(this.middleware).filter(middlewareEnabled);
|
||||
const enabledMiddlewares = Object.keys(strapi.middleware).filter(middlewareEnabled);
|
||||
|
||||
// Method to initialize middlewares and emit an event.
|
||||
const initialize = middlewareKey => {
|
||||
if (this.middleware[middlewareKey].loaded === true) return;
|
||||
if (strapi.middleware[middlewareKey].loaded === true) return;
|
||||
|
||||
const module = this.middleware[middlewareKey].load;
|
||||
const module = strapi.middleware[middlewareKey].load;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeout = setTimeout(
|
||||
() => reject(`(middleware: ${middlewareKey}) is taking too long to load.`),
|
||||
middlewareConfig.timeout || 1000
|
||||
);
|
||||
this.middleware[middlewareKey] = merge(this.middleware[middlewareKey], module);
|
||||
strapi.middleware[middlewareKey] = merge(strapi.middleware[middlewareKey], module);
|
||||
|
||||
Promise.resolve()
|
||||
.then(() => module.initialize())
|
||||
.then(() => module.initialize(strapi))
|
||||
.then(() => {
|
||||
clearTimeout(timeout);
|
||||
this.middleware[middlewareKey].loaded = true;
|
||||
strapi.middleware[middlewareKey].loaded = true;
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
@ -72,9 +72,9 @@ module.exports = async function() {
|
||||
// Run beforeInitialize of every middleware
|
||||
await Promise.all(
|
||||
enabledMiddlewares.map(key => {
|
||||
const { beforeInitialize } = this.middleware[key].load;
|
||||
const { beforeInitialize } = strapi.middleware[key].load;
|
||||
if (typeof beforeInitialize === 'function') {
|
||||
return beforeInitialize();
|
||||
return beforeInitialize(strapi);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
@ -14,7 +14,7 @@ module.exports = strapi => {
|
||||
initialize() {
|
||||
const { whiteList, blackList } = strapi.config.middleware.settings.ip;
|
||||
|
||||
strapi.app.use(
|
||||
strapi.server.use(
|
||||
ip({
|
||||
whitelist: whiteList,
|
||||
blacklist: blackList,
|
||||
|
||||
@ -19,14 +19,14 @@ module.exports = strapi => {
|
||||
*/
|
||||
|
||||
initialize() {
|
||||
locale(strapi.app);
|
||||
locale(strapi.server.app);
|
||||
|
||||
const { defaultLocale, modes, cookieName } = strapi.config.middleware.settings.language;
|
||||
|
||||
const directory = resolve(strapi.config.appPath, strapi.config.paths.config, 'locales');
|
||||
|
||||
strapi.app.use(
|
||||
i18n(strapi.app, {
|
||||
strapi.server.use(
|
||||
i18n(strapi.server.app, {
|
||||
directory,
|
||||
locales: strapi.config.get('middleware.settings.language.locales', []),
|
||||
defaultLocale,
|
||||
|
||||
@ -23,9 +23,9 @@ module.exports = strapi => {
|
||||
* Initialize the hook
|
||||
*/
|
||||
initialize() {
|
||||
strapi.app.context.log = strapi.log;
|
||||
strapi.server.app.context.log = strapi.log;
|
||||
|
||||
strapi.app.use(async (ctx, next) => {
|
||||
strapi.server.use(async (ctx, next) => {
|
||||
const start = Date.now();
|
||||
await next();
|
||||
const delta = Math.ceil(Date.now() - start);
|
||||
|
||||
@ -37,7 +37,7 @@ module.exports = strapi => {
|
||||
* Initialize the hook
|
||||
*/
|
||||
initialize() {
|
||||
strapi.app.use(async (ctx, next) => {
|
||||
strapi.server.use(async (ctx, next) => {
|
||||
// disable for graphql
|
||||
// TODO: find a better way later
|
||||
if (ctx.url === '/graphql') {
|
||||
@ -66,7 +66,10 @@ module.exports = strapi => {
|
||||
}
|
||||
});
|
||||
|
||||
addQsParser(strapi.app, strapi.config.get('middleware.settings.parser.queryStringParser'));
|
||||
addQsParser(
|
||||
strapi.server.app,
|
||||
strapi.config.get('middleware.settings.parser.queryStringParser')
|
||||
);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
module.exports = strapi => {
|
||||
return {
|
||||
initialize() {
|
||||
strapi.app.use(async (ctx, next) => {
|
||||
strapi.server.use(async (ctx, next) => {
|
||||
await next();
|
||||
|
||||
ctx.set(
|
||||
|
||||
@ -33,6 +33,7 @@ module.exports = strapi => {
|
||||
const serveIndexPage = async (ctx, next) => {
|
||||
// defer rendering of strapi index page
|
||||
await next();
|
||||
|
||||
if (ctx.body != null || ctx.status !== 404) return;
|
||||
|
||||
ctx.url = 'index.html';
|
||||
@ -61,38 +62,64 @@ module.exports = strapi => {
|
||||
ctx.body = body;
|
||||
};
|
||||
|
||||
strapi.router.get('/', serveIndexPage);
|
||||
strapi.router.get('/index.html', serveIndexPage);
|
||||
strapi.router.get(
|
||||
'/assets/images/(.*)',
|
||||
serveStatic(path.resolve(__dirname, 'assets/images'), { maxage: maxAge, defer: true })
|
||||
);
|
||||
strapi.server.routes([
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/',
|
||||
handler: serveIndexPage,
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/index.html',
|
||||
handler: serveIndexPage,
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/assets/images/(.*)',
|
||||
handler: serveStatic(path.resolve(__dirname, 'assets/images'), {
|
||||
maxage: maxAge,
|
||||
defer: true,
|
||||
}),
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/(.*)',
|
||||
handler: koaStatic(staticDir, {
|
||||
maxage: maxAge,
|
||||
defer: true,
|
||||
}),
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
// serve files in public folder unless a sub router renders something else
|
||||
strapi.router.get(
|
||||
'/(.*)',
|
||||
koaStatic(staticDir, {
|
||||
maxage: maxAge,
|
||||
defer: true,
|
||||
})
|
||||
);
|
||||
|
||||
if (!strapi.config.serveAdminPanel) return;
|
||||
|
||||
const buildDir = path.resolve(strapi.dir, 'build');
|
||||
const serveAdmin = ctx => {
|
||||
const serveAdmin = async (ctx, next) => {
|
||||
await next();
|
||||
|
||||
if (ctx.method !== 'HEAD' && ctx.method !== 'GET') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.body != null || ctx.status !== 404) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.type = 'html';
|
||||
ctx.body = fs.createReadStream(path.join(buildDir + '/index.html'));
|
||||
};
|
||||
|
||||
strapi.router.get(
|
||||
`${strapi.config.admin.path}/*`,
|
||||
serveStatic(buildDir, { maxage: maxAge, defer: false, index: 'index.html' })
|
||||
);
|
||||
|
||||
strapi.router.get(`${strapi.config.admin.path}`, serveAdmin);
|
||||
strapi.router.get(`${strapi.config.admin.path}/*`, serveAdmin);
|
||||
strapi.server.routes([
|
||||
{
|
||||
method: 'GET',
|
||||
path: `${strapi.config.admin.path}/:path*`,
|
||||
handler: [
|
||||
serveAdmin,
|
||||
serveStatic(buildDir, { maxage: maxAge, defer: false, index: 'index.html' }),
|
||||
],
|
||||
},
|
||||
]);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@ -11,7 +11,7 @@ module.exports = strapi => {
|
||||
*/
|
||||
|
||||
initialize() {
|
||||
strapi.app.use(async (ctx, next) => {
|
||||
strapi.server.use(async (ctx, next) => {
|
||||
const start = Date.now();
|
||||
|
||||
await next();
|
||||
|
||||
@ -5,7 +5,7 @@ const _ = require('lodash');
|
||||
module.exports = strapi => {
|
||||
return {
|
||||
initialize() {
|
||||
strapi.app.use(async (ctx, next) => {
|
||||
strapi.server.use(async (ctx, next) => {
|
||||
await next();
|
||||
|
||||
const responseFn = strapi.config.get(['functions', 'responses', ctx.status]);
|
||||
|
||||
@ -1,42 +1,67 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies
|
||||
*/
|
||||
|
||||
// Public node modules.
|
||||
const _ = require('lodash');
|
||||
const Router = require('koa-router');
|
||||
const createEndpointComposer = require('./utils/compose-endpoint');
|
||||
const { toLower } = require('lodash/fp');
|
||||
|
||||
const createRouteScopeGenerator = namespace => route => {
|
||||
const prefix = namespace.endsWith('::') ? namespace : `${namespace}.`;
|
||||
|
||||
if (typeof route.handler === 'string') {
|
||||
const [controller, action] = route.handler.split('.');
|
||||
|
||||
_.defaultsDeep(route.config, {
|
||||
auth: {
|
||||
scope: `${prefix}${controller}.${toLower(action)}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = strapi => {
|
||||
const composeEndpoint = createEndpointComposer(strapi);
|
||||
|
||||
const registerAdminRoutes = () => {
|
||||
const router = new Router({ prefix: '/admin' });
|
||||
const generateRouteScope = createRouteScopeGenerator(`admin::`);
|
||||
|
||||
for (const route of strapi.admin.routes) {
|
||||
composeEndpoint(route, { pluginName: 'admin', router });
|
||||
}
|
||||
strapi.admin.routes.forEach(route => {
|
||||
generateRouteScope(route);
|
||||
route.info = { pluginName: 'admin' };
|
||||
});
|
||||
|
||||
strapi.app.use(router.routes()).use(router.allowedMethods());
|
||||
strapi.server.routes({
|
||||
type: 'admin',
|
||||
prefix: '/admin',
|
||||
routes: strapi.admin.routes,
|
||||
});
|
||||
};
|
||||
|
||||
const registerPluginRoutes = () => {
|
||||
for (const pluginName in strapi.plugins) {
|
||||
const plugin = strapi.plugins[pluginName];
|
||||
|
||||
const router = new Router({ prefix: `/${pluginName}` });
|
||||
const generateRouteScope = createRouteScopeGenerator(`plugin::${pluginName}`);
|
||||
|
||||
for (const route of plugin.routes || []) {
|
||||
const hasPrefix = _.has(route.config, 'prefix');
|
||||
composeEndpoint(route, {
|
||||
pluginName,
|
||||
router: hasPrefix ? strapi.router : router,
|
||||
if (Array.isArray(plugin.routes)) {
|
||||
plugin.routes.forEach(route => {
|
||||
generateRouteScope(route);
|
||||
route.info = { pluginName };
|
||||
});
|
||||
|
||||
strapi.server.routes({
|
||||
type: 'admin',
|
||||
prefix: `/${pluginName}`,
|
||||
routes: plugin.routes,
|
||||
});
|
||||
} else {
|
||||
_.forEach(plugin.routes, router => {
|
||||
router.type = router.type || 'admin';
|
||||
router.prefix = `/${pluginName}`;
|
||||
router.routes.forEach(route => {
|
||||
generateRouteScope(route);
|
||||
route.info = { pluginName };
|
||||
});
|
||||
|
||||
strapi.server.routes(router);
|
||||
});
|
||||
}
|
||||
|
||||
strapi.app.use(router.routes()).use(router.allowedMethods());
|
||||
}
|
||||
};
|
||||
|
||||
@ -44,28 +69,26 @@ module.exports = strapi => {
|
||||
for (const apiName in strapi.api) {
|
||||
const api = strapi.api[apiName];
|
||||
|
||||
_.forEach(api.routes, routeInfo => {
|
||||
// nested router
|
||||
if (_.has(routeInfo, 'routes')) {
|
||||
const router = new Router({ prefix: routeInfo.prefix });
|
||||
const generateRouteScope = createRouteScopeGenerator(`api::${apiName}`);
|
||||
|
||||
for (const route of routeInfo.routes || []) {
|
||||
composeEndpoint(route, { apiName, router });
|
||||
}
|
||||
_.forEach(api.routes, router => {
|
||||
// TODO: remove once auth setup
|
||||
// pass meta down to compose endpoint
|
||||
router.type = 'content-api';
|
||||
router.routes.forEach(route => {
|
||||
generateRouteScope(route);
|
||||
route.info = { apiName };
|
||||
});
|
||||
|
||||
strapi.router.use(router.routes()).use(router.allowedMethods());
|
||||
} else {
|
||||
composeEndpoint(routeInfo, { apiName, router: strapi.router });
|
||||
}
|
||||
return strapi.server.routes(router);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
initialize() {
|
||||
strapi.router.prefix(strapi.config.get('middleware.settings.router.prefix', ''));
|
||||
registerAPIRoutes();
|
||||
registerAdminRoutes();
|
||||
registerAPIRoutes();
|
||||
registerPluginRoutes();
|
||||
},
|
||||
};
|
||||
|
||||
@ -1,169 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const compose = require('koa-compose');
|
||||
const { yup, policy: policyUtils } = 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();
|
||||
}
|
||||
|
||||
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) || !_.isFunction(action)) {
|
||||
return strapi.log.warn(
|
||||
`Ignored attempt to bind route '${routeConfig.method} ${routeConfig.path}' to unknown controller/action.`
|
||||
);
|
||||
}
|
||||
|
||||
router[method](endpoint, compose([...policies, ...middlewares, 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 createRouteChecker = strapi => {
|
||||
return (value, { pluginName, apiName }) => {
|
||||
const method = getMethod(value);
|
||||
const endpoint = getEndpoint(value);
|
||||
|
||||
// Define controller and action names.
|
||||
const [controllerName, actionName] = _.trim(value.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}"`
|
||||
);
|
||||
}
|
||||
|
||||
const action = controller[actionName].bind(controller);
|
||||
|
||||
const { bodyPolicy } = policyUtils;
|
||||
|
||||
const globalPolicy = policyUtils.globalPolicy({
|
||||
controller: controllerKey,
|
||||
action: actionName,
|
||||
method,
|
||||
endpoint,
|
||||
plugin: pluginName,
|
||||
});
|
||||
|
||||
const policyOption = _.get(value, 'config.policies', []);
|
||||
|
||||
const routePolicies = policyOption.map(policyConfig => {
|
||||
return policyUtils.get(policyConfig, { pluginName, apiName });
|
||||
});
|
||||
|
||||
// Init policies array.
|
||||
const policies = [globalPolicy, ...routePolicies, bodyPolicy];
|
||||
|
||||
return {
|
||||
method,
|
||||
endpoint,
|
||||
policies,
|
||||
action,
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -27,9 +27,12 @@ describe('Session middleware', () => {
|
||||
);
|
||||
|
||||
const mockStrapi = {
|
||||
app: {
|
||||
server: {
|
||||
app: {
|
||||
use: jest.fn(),
|
||||
context: {},
|
||||
},
|
||||
use: jest.fn(),
|
||||
context: {},
|
||||
},
|
||||
config: {
|
||||
appPath: __dirname,
|
||||
|
||||
@ -93,7 +93,7 @@ module.exports = strapi => {
|
||||
|
||||
return {
|
||||
initialize() {
|
||||
strapi.app.keys = strapi.config.get('middleware.settings.session.secretKeys');
|
||||
strapi.server.app.keys = strapi.config.get('middleware.settings.session.secretKeys');
|
||||
|
||||
if (
|
||||
_.has(strapi.config.middleware.settings.session, 'client') &&
|
||||
@ -112,8 +112,8 @@ module.exports = strapi => {
|
||||
strapi.config.middleware.settings.session
|
||||
);
|
||||
|
||||
strapi.app.use(session(options, strapi.app));
|
||||
strapi.app.use((ctx, next) => {
|
||||
strapi.server.use(session(options, strapi.server.app));
|
||||
strapi.server.use((ctx, next) => {
|
||||
ctx.state = ctx.state || {};
|
||||
ctx.state.session = ctx.session || {};
|
||||
|
||||
@ -127,8 +127,8 @@ module.exports = strapi => {
|
||||
) {
|
||||
const options = _.assign(strapi.config.middleware.settings.session);
|
||||
|
||||
strapi.app.use(session(options, strapi.app));
|
||||
strapi.app.use((ctx, next) => {
|
||||
strapi.server.use(session(options, strapi.server.app));
|
||||
strapi.server.use((ctx, next) => {
|
||||
ctx.state = ctx.state || {};
|
||||
ctx.state.session = ctx.session || {};
|
||||
|
||||
|
||||
71
packages/core/strapi/lib/services/content-api/index.js
Normal file
71
packages/core/strapi/lib/services/content-api/index.js
Normal file
@ -0,0 +1,71 @@
|
||||
'use strict';
|
||||
|
||||
const { strict: assert } = require('assert');
|
||||
const { has } = require('lodash/fp');
|
||||
|
||||
class UnauthorizedError extends Error {}
|
||||
class ForbiddenError extends Error {}
|
||||
|
||||
const INVALID_STRATEGY_MSG =
|
||||
'Invalid auth strategy. Expecting an object with properties {name: string, authenticate: function, verify: function}';
|
||||
|
||||
const validStrategy = strategy => {
|
||||
assert(has('name', strategy), INVALID_STRATEGY_MSG);
|
||||
assert(typeof strategy.name === 'string', INVALID_STRATEGY_MSG);
|
||||
|
||||
assert(has('authenticate', strategy), INVALID_STRATEGY_MSG);
|
||||
assert(typeof strategy.authenticate === 'function', INVALID_STRATEGY_MSG);
|
||||
|
||||
assert(has('verify', strategy), INVALID_STRATEGY_MSG);
|
||||
assert(typeof strategy.verify === 'function', INVALID_STRATEGY_MSG);
|
||||
};
|
||||
|
||||
const createAuthentication = () => {
|
||||
const strategies = [];
|
||||
|
||||
return {
|
||||
register(strategy) {
|
||||
validStrategy(strategy);
|
||||
strategies.push(strategy);
|
||||
|
||||
return () => {
|
||||
strategies.splice(strategies.indexOf(strategy), 1);
|
||||
};
|
||||
},
|
||||
async authenticate(ctx, next) {
|
||||
for (const strategy of strategies) {
|
||||
const result = await strategy.authenticate(ctx);
|
||||
|
||||
const { authenticated = false, credentials } = result || {};
|
||||
|
||||
if (authenticated) {
|
||||
ctx.state.auth = { strategy, credentials };
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
return next();
|
||||
},
|
||||
async verify(auth, config = {}) {
|
||||
if (config.public) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!auth) {
|
||||
throw new UnauthorizedError();
|
||||
}
|
||||
|
||||
return await auth.strategy.verify(auth, config);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = () => {
|
||||
return {
|
||||
auth: createAuthentication(),
|
||||
errors: {
|
||||
UnauthorizedError,
|
||||
ForbiddenError,
|
||||
},
|
||||
};
|
||||
};
|
||||
@ -21,7 +21,7 @@ describe('metrics', () => {
|
||||
strapi: '0.0.0',
|
||||
},
|
||||
},
|
||||
app: {
|
||||
server: {
|
||||
use,
|
||||
},
|
||||
});
|
||||
@ -43,7 +43,7 @@ describe('metrics', () => {
|
||||
strapi: '0.0.0',
|
||||
},
|
||||
},
|
||||
app: {
|
||||
server: {
|
||||
use,
|
||||
},
|
||||
});
|
||||
@ -63,7 +63,7 @@ describe('metrics', () => {
|
||||
strapi: '0.0.0',
|
||||
},
|
||||
},
|
||||
app: {
|
||||
server: {
|
||||
use() {},
|
||||
},
|
||||
});
|
||||
@ -96,7 +96,7 @@ describe('metrics', () => {
|
||||
strapi: '0.0.0',
|
||||
},
|
||||
},
|
||||
app: {
|
||||
server: {
|
||||
use() {},
|
||||
},
|
||||
});
|
||||
|
||||
@ -34,7 +34,7 @@ const createTelemetryInstance = strapi => {
|
||||
const pingCron = scheduleJob('0 0 12 * * *', () => sendEvent('ping'));
|
||||
crons.push(pingCron);
|
||||
|
||||
strapi.app.use(createMiddleware({ sendEvent }));
|
||||
strapi.server.use(createMiddleware({ sendEvent }));
|
||||
}
|
||||
|
||||
if (strapi.EE === true && ee.isEE === true) {
|
||||
|
||||
13
packages/core/strapi/lib/services/server/admin-api.js
Normal file
13
packages/core/strapi/lib/services/server/admin-api.js
Normal file
@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const { createAPI } = require('./api');
|
||||
|
||||
const createAdminAPI = strapi => {
|
||||
const opts = {
|
||||
prefix: '', // '/admin';
|
||||
};
|
||||
|
||||
return createAPI(strapi, opts);
|
||||
};
|
||||
|
||||
module.exports = { createAdminAPI };
|
||||
32
packages/core/strapi/lib/services/server/api.js
Normal file
32
packages/core/strapi/lib/services/server/api.js
Normal file
@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
const Router = require('@koa/router');
|
||||
|
||||
const { createRouteManager } = require('./routing');
|
||||
|
||||
const createAPI = (strapi, opts = {}) => {
|
||||
const { prefix, defaultPolicies } = opts;
|
||||
|
||||
const api = new Router({ prefix });
|
||||
|
||||
const routeManager = createRouteManager(strapi, { defaultPolicies });
|
||||
|
||||
return {
|
||||
use(fn) {
|
||||
api.use(fn);
|
||||
return this;
|
||||
},
|
||||
|
||||
routes(routes) {
|
||||
routeManager.addRoutes(routes, api);
|
||||
return this;
|
||||
},
|
||||
|
||||
mount(router) {
|
||||
router.use(api.routes(), api.allowedMethods());
|
||||
return this;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = { createAPI };
|
||||
69
packages/core/strapi/lib/services/server/compose-endpoint.js
Normal file
69
packages/core/strapi/lib/services/server/compose-endpoint.js
Normal file
@ -0,0 +1,69 @@
|
||||
'use strict';
|
||||
|
||||
const { toLower, castArray, trim } = require('lodash/fp');
|
||||
|
||||
const compose = require('koa-compose');
|
||||
const { resolveMiddlewares } = require('./middleware');
|
||||
const { resolvePolicies } = require('./policy');
|
||||
|
||||
const getMethod = route => trim(toLower(route.method));
|
||||
const getPath = route => trim(route.path);
|
||||
|
||||
const routeInfoMiddleware = route => (ctx, next) => {
|
||||
ctx.state.route = route;
|
||||
return next();
|
||||
};
|
||||
|
||||
module.exports = strapi => {
|
||||
return (route, { pluginName, router, apiName }) => {
|
||||
try {
|
||||
const method = getMethod(route);
|
||||
const path = getPath(route);
|
||||
|
||||
const middlewares = resolveMiddlewares(route);
|
||||
const policies = resolvePolicies(route, { pluginName, apiName });
|
||||
|
||||
const action = getAction(route, { pluginName, apiName }, strapi);
|
||||
|
||||
const routeHandler = compose([
|
||||
routeInfoMiddleware(route),
|
||||
...policies,
|
||||
...middlewares,
|
||||
...castArray(action),
|
||||
]);
|
||||
|
||||
router[method](path, routeHandler);
|
||||
} catch (error) {
|
||||
throw new Error(`Error creating endpoint ${route.method} ${route.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);
|
||||
};
|
||||
52
packages/core/strapi/lib/services/server/content-api.js
Normal file
52
packages/core/strapi/lib/services/server/content-api.js
Normal file
@ -0,0 +1,52 @@
|
||||
'use strict';
|
||||
|
||||
const { prop } = require('lodash/fp');
|
||||
const { createAPI } = require('./api');
|
||||
|
||||
const getAuthConfig = prop('config.auth');
|
||||
|
||||
const createAuthPolicy = strapi => async (ctx, next) => {
|
||||
const { auth, route } = ctx.state;
|
||||
|
||||
if (!route) {
|
||||
return ctx.unauthorized();
|
||||
}
|
||||
|
||||
try {
|
||||
await strapi.container.get('content-api').auth.verify(auth, getAuthConfig(route));
|
||||
|
||||
return next();
|
||||
} catch (error) {
|
||||
const { errors } = strapi.container.get('content-api');
|
||||
|
||||
if (error instanceof errors.UnauthorizedError) {
|
||||
return ctx.unauthorized();
|
||||
}
|
||||
|
||||
if (error instanceof errors.ForbiddenError) {
|
||||
return ctx.forbidden();
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const createContentAPI = strapi => {
|
||||
const opts = {
|
||||
prefix: strapi.config.get('api.prefix', '/api'),
|
||||
defaultPolicies: [createAuthPolicy(strapi)],
|
||||
};
|
||||
|
||||
const api = createAPI(strapi, opts);
|
||||
|
||||
// implement auth providers
|
||||
api.use((ctx, next) => {
|
||||
return strapi.container.get('content-api').auth.authenticate(ctx, next);
|
||||
});
|
||||
|
||||
return api;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createContentAPI,
|
||||
};
|
||||
112
packages/core/strapi/lib/services/server/index.js
Normal file
112
packages/core/strapi/lib/services/server/index.js
Normal file
@ -0,0 +1,112 @@
|
||||
'use strict';
|
||||
|
||||
const Koa = require('koa');
|
||||
const Router = require('@koa/router');
|
||||
|
||||
const { createHTTPServer } = require('./http-server');
|
||||
const { createRouteManager } = require('./routing');
|
||||
const { createAdminAPI } = require('./admin-api');
|
||||
const { createContentAPI } = require('./content-api');
|
||||
|
||||
const healthCheck = async (ctx, next) => {
|
||||
if (ctx.request.url === '/_health' && ['HEAD', 'GET'].includes(ctx.request.method)) {
|
||||
ctx.set('strapi', 'You are so French!');
|
||||
ctx.status = 204;
|
||||
} else {
|
||||
await next();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef Server
|
||||
*
|
||||
* @property {Koa} app
|
||||
* @property {http.Server} app
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Strapi} strapi
|
||||
* @returns {Server}
|
||||
*/
|
||||
const createServer = strapi => {
|
||||
const app = new Koa({
|
||||
proxy: strapi.config.get('server.proxy'),
|
||||
});
|
||||
|
||||
const router = new Router({
|
||||
// FIXME: this prefix can break the admin if not specified in the admin url
|
||||
prefix: strapi.config.get('middleware.settings.router.prefix', ''),
|
||||
});
|
||||
|
||||
const routeManager = createRouteManager(strapi);
|
||||
|
||||
const httpServer = createHTTPServer(strapi, app);
|
||||
|
||||
const apis = {
|
||||
'content-api': createContentAPI(strapi),
|
||||
admin: createAdminAPI(strapi),
|
||||
};
|
||||
|
||||
// init health check
|
||||
app.use(healthCheck);
|
||||
|
||||
const state = {
|
||||
mounted: false,
|
||||
};
|
||||
|
||||
return {
|
||||
app,
|
||||
router,
|
||||
httpServer,
|
||||
|
||||
api(name) {
|
||||
return apis[name];
|
||||
},
|
||||
|
||||
use(...args) {
|
||||
app.use(...args);
|
||||
return this;
|
||||
},
|
||||
|
||||
routes(routes) {
|
||||
if (routes.type) {
|
||||
const api = apis[routes.type];
|
||||
if (!api) {
|
||||
throw new Error(`API ${routes.type} not found. Possible APIs are ${Object.keys(apis)}`);
|
||||
}
|
||||
|
||||
apis[routes.type].routes(routes);
|
||||
return this;
|
||||
}
|
||||
|
||||
routeManager.addRoutes(routes, router);
|
||||
return this;
|
||||
},
|
||||
|
||||
mount() {
|
||||
state.mounted = true;
|
||||
|
||||
Object.values(apis).forEach(api => api.mount(router));
|
||||
app.use(router.routes()).use(router.allowedMethods());
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
listen(...args) {
|
||||
if (!state.mounted) {
|
||||
this.mount();
|
||||
}
|
||||
|
||||
return httpServer.listen(...args);
|
||||
},
|
||||
|
||||
async destroy() {
|
||||
await httpServer.destroy();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createServer,
|
||||
};
|
||||
28
packages/core/strapi/lib/services/server/middleware.js
Normal file
28
packages/core/strapi/lib/services/server/middleware.js
Normal file
@ -0,0 +1,28 @@
|
||||
'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;
|
||||
}
|
||||
|
||||
// TODO: this won't work until we have the new middleware formats
|
||||
const middleware = strapi.middleware(middlewareConfig);
|
||||
|
||||
if (!middleware) {
|
||||
throw new Error(`Middleware ${middlewareConfig} not found.`);
|
||||
}
|
||||
|
||||
return middleware;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
resolveMiddlewares,
|
||||
};
|
||||
19
packages/core/strapi/lib/services/server/policy.js
Normal file
19
packages/core/strapi/lib/services/server/policy.js
Normal 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,
|
||||
};
|
||||
111
packages/core/strapi/lib/services/server/routing.js
Normal file
111
packages/core/strapi/lib/services/server/routing.js
Normal file
@ -0,0 +1,111 @@
|
||||
'use strict';
|
||||
|
||||
const Router = require('@koa/router');
|
||||
const _ = require('lodash');
|
||||
const { has } = require('lodash/fp');
|
||||
const { yup } = require('@strapi/utils');
|
||||
|
||||
const createEndpointComposer = require('./compose-endpoint');
|
||||
|
||||
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) {
|
||||
throw new Error('Invalid route config', error.message);
|
||||
}
|
||||
};
|
||||
|
||||
const createRouteManager = (strapi, opts = {}) => {
|
||||
const composeEndpoint = createEndpointComposer(strapi);
|
||||
|
||||
const createRoute = (route, router) => {
|
||||
validateRouteConfig(route);
|
||||
|
||||
if (opts.defaultPolicies) {
|
||||
if (has('config.policies', route)) {
|
||||
route.config.policies.unshift(...opts.defaultPolicies);
|
||||
} else {
|
||||
_.set(route, 'config.policies', [...opts.defaultPolicies]);
|
||||
}
|
||||
}
|
||||
|
||||
composeEndpoint(route, { ...route.info, router });
|
||||
};
|
||||
|
||||
const addRoutes = (routes, router) => {
|
||||
if (Array.isArray(routes)) {
|
||||
routes.forEach(route => createRoute(route, router));
|
||||
} else if (routes.routes) {
|
||||
const subRouter = new Router({ prefix: routes.prefix });
|
||||
|
||||
routes.routes.forEach(route => {
|
||||
const hasPrefix = has('prefix', route.config);
|
||||
createRoute(route, hasPrefix ? router : subRouter);
|
||||
});
|
||||
|
||||
return router.use(subRouter.routes(), subRouter.allowedMethods());
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
addRoutes,
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
validateRouteConfig,
|
||||
createRouteManager,
|
||||
};
|
||||
@ -14,10 +14,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@koa/cors": "^3.0.0",
|
||||
"@koa/router": "10.1.1",
|
||||
"@strapi/admin": "3.6.8",
|
||||
"@strapi/database": "3.6.8",
|
||||
"@strapi/generators": "3.6.8",
|
||||
"@strapi/generate-new": "3.6.8",
|
||||
"@strapi/generators": "3.6.8",
|
||||
"@strapi/logger": "3.6.8",
|
||||
"@strapi/plugin-content-manager": "3.6.8",
|
||||
"@strapi/plugin-content-type-builder": "3.6.8",
|
||||
@ -52,7 +53,7 @@
|
||||
"koa-i18n": "^2.1.0",
|
||||
"koa-ip": "^2.0.0",
|
||||
"koa-locale": "~1.3.0",
|
||||
"koa-router": "^7.4.0",
|
||||
"koa-mount": "4.0.0",
|
||||
"koa-session": "^6.2.0",
|
||||
"koa-static": "^5.0.0",
|
||||
"lodash": "4.17.21",
|
||||
|
||||
@ -4,7 +4,7 @@ const _ = require('lodash');
|
||||
|
||||
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
|
||||
const { createTestBuilder } = require('../../../../../test/helpers/builder');
|
||||
const { createAuthRequest } = require('../../../../../test/helpers/request');
|
||||
const { createContentAPIRequest } = require('../../../../../test/helpers/request');
|
||||
|
||||
const builder = createTestBuilder();
|
||||
let strapi;
|
||||
@ -56,7 +56,7 @@ describe('Core API - Basic + compo', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@ -4,7 +4,7 @@ const _ = require('lodash');
|
||||
|
||||
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
|
||||
const { createTestBuilder } = require('../../../../../test/helpers/builder');
|
||||
const { createAuthRequest } = require('../../../../../test/helpers/request');
|
||||
const { createContentAPIRequest } = require('../../../../../test/helpers/request');
|
||||
|
||||
const builder = createTestBuilder();
|
||||
let strapi;
|
||||
@ -55,7 +55,7 @@ describe('Core API - Basic + compo', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@ -4,7 +4,7 @@ const _ = require('lodash');
|
||||
|
||||
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
|
||||
const { createTestBuilder } = require('../../../../../test/helpers/builder');
|
||||
const { createAuthRequest } = require('../../../../../test/helpers/request');
|
||||
const { createContentAPIRequest } = require('../../../../../test/helpers/request');
|
||||
|
||||
const builder = createTestBuilder();
|
||||
let strapi;
|
||||
@ -57,7 +57,7 @@ describe('Core API - Basic + compo + draftAndPublish', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@ -4,7 +4,7 @@ const _ = require('lodash');
|
||||
|
||||
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
|
||||
const { createTestBuilder } = require('../../../../../test/helpers/builder');
|
||||
const { createAuthRequest } = require('../../../../../test/helpers/request');
|
||||
const { createContentAPIRequest } = require('../../../../../test/helpers/request');
|
||||
|
||||
const builder = createTestBuilder();
|
||||
let strapi;
|
||||
@ -56,7 +56,7 @@ describe('Core API - Basic + compo + draftAndPublish', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@ -4,7 +4,7 @@ const _ = require('lodash');
|
||||
|
||||
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
|
||||
const { createTestBuilder } = require('../../../../../test/helpers/builder');
|
||||
const { createAuthRequest } = require('../../../../../test/helpers/request');
|
||||
const { createContentAPIRequest } = require('../../../../../test/helpers/request');
|
||||
|
||||
const builder = createTestBuilder();
|
||||
let strapi;
|
||||
@ -54,7 +54,7 @@ describe('Core API - Basic + draftAndPublish', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@ -4,7 +4,7 @@ const _ = require('lodash');
|
||||
|
||||
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
|
||||
const { createTestBuilder } = require('../../../../../test/helpers/builder');
|
||||
const { createAuthRequest } = require('../../../../../test/helpers/request');
|
||||
const { createContentAPIRequest } = require('../../../../../test/helpers/request');
|
||||
|
||||
const builder = createTestBuilder();
|
||||
let strapi;
|
||||
@ -55,7 +55,7 @@ describe('Core API - Basic + dz', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
|
||||
const { createTestBuilder } = require('../../../../../test/helpers/builder');
|
||||
const { createAuthRequest } = require('../../../../../test/helpers/request');
|
||||
const { createContentAPIRequest } = require('../../../../../test/helpers/request');
|
||||
|
||||
const builder = createTestBuilder();
|
||||
let strapi;
|
||||
@ -48,7 +48,7 @@ describe('Core API - Basic', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
const { createTestBuilder } = require('../../../../../test/helpers/builder');
|
||||
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
|
||||
const { createAuthRequest } = require('../../../../../test/helpers/request');
|
||||
const { createContentAPIRequest } = require('../../../../../test/helpers/request');
|
||||
|
||||
let strapi;
|
||||
let rq;
|
||||
@ -40,8 +40,8 @@ describe('Non repeatable and Not required component', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq.setURLPrefix('/withcomponents');
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
rq.setURLPrefix('/api/withcomponents');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
const { createTestBuilder } = require('../../../../../test/helpers/builder');
|
||||
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
|
||||
const { createAuthRequest } = require('../../../../../test/helpers/request');
|
||||
const { createContentAPIRequest } = require('../../../../../test/helpers/request');
|
||||
|
||||
let strapi;
|
||||
let rq;
|
||||
@ -38,8 +38,8 @@ describe('Non repeatable and Not required component', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq.setURLPrefix('/withcomponents');
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
rq.setURLPrefix('/api/withcomponents');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
const { createTestBuilder } = require('../../../../../test/helpers/builder');
|
||||
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
|
||||
const { createAuthRequest } = require('../../../../../test/helpers/request');
|
||||
const { createContentAPIRequest } = require('../../../../../test/helpers/request');
|
||||
|
||||
let strapi;
|
||||
let rq;
|
||||
@ -40,8 +40,8 @@ describe('Non repeatable and Not required component', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq.setURLPrefix('/withcomponents');
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
rq.setURLPrefix('/api/withcomponents');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
const { createTestBuilder } = require('../../../../../test/helpers/builder');
|
||||
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
|
||||
const { createAuthRequest } = require('../../../../../test/helpers/request');
|
||||
const { createContentAPIRequest } = require('../../../../../test/helpers/request');
|
||||
|
||||
let strapi;
|
||||
let rq;
|
||||
@ -38,8 +38,8 @@ describe('Non repeatable and Not required component', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq.setURLPrefix('/withcomponents');
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
rq.setURLPrefix('/api/withcomponents');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
const { createTestBuilder } = require('../../../../../test/helpers/builder');
|
||||
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
|
||||
const { createAuthRequest } = require('../../../../../test/helpers/request');
|
||||
const { createContentAPIRequest } = require('../../../../../test/helpers/request');
|
||||
|
||||
let strapi;
|
||||
let rq;
|
||||
@ -38,8 +38,8 @@ describe('Non repeatable and Not required component', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq.setURLPrefix('/withcomponents');
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
rq.setURLPrefix('/api/withcomponents');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
const { createTestBuilder } = require('../../../../../test/helpers/builder');
|
||||
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
|
||||
const { createAuthRequest } = require('../../../../../test/helpers/request');
|
||||
const { createContentAPIRequest } = require('../../../../../test/helpers/request');
|
||||
|
||||
let strapi;
|
||||
let rq;
|
||||
@ -38,8 +38,8 @@ describe('Non repeatable and required component', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq.setURLPrefix('/withcomponents');
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
rq.setURLPrefix('/api/withcomponents');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@ -4,7 +4,10 @@
|
||||
const _ = require('lodash');
|
||||
const { createStrapiInstance } = require('../../../../test/helpers/strapi');
|
||||
const { createTestBuilder } = require('../../../../test/helpers/builder');
|
||||
const { createAuthRequest, transformToRESTResource } = require('../../../../test/helpers/request');
|
||||
const {
|
||||
createContentAPIRequest,
|
||||
transformToRESTResource,
|
||||
} = require('../../../../test/helpers/request');
|
||||
|
||||
const builder = createTestBuilder();
|
||||
const data = {
|
||||
@ -89,7 +92,7 @@ describe('Deep Filtering API', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
|
||||
Object.assign(
|
||||
data,
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
const { createTestBuilder } = require('../../../../../test/helpers/builder');
|
||||
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
|
||||
const { createAuthRequest } = require('../../../../../test/helpers/request');
|
||||
const { createContentAPIRequest } = require('../../../../../test/helpers/request');
|
||||
|
||||
let strapi;
|
||||
let rq;
|
||||
@ -87,8 +87,8 @@ describe('Not required dynamiczone', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq.setURLPrefix('/withdynamiczones');
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
rq.setURLPrefix('/api/withdynamiczones');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@ -5,7 +5,7 @@ const path = require('path');
|
||||
|
||||
const { createTestBuilder } = require('../../../../../test/helpers/builder');
|
||||
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
|
||||
const { createAuthRequest } = require('../../../../../test/helpers/request');
|
||||
const { createContentAPIRequest } = require('../../../../../test/helpers/request');
|
||||
|
||||
let strapi;
|
||||
let rq;
|
||||
@ -77,10 +77,10 @@ describe('Not required dynamiczone', () => {
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
|
||||
baseRq = await createAuthRequest({ strapi });
|
||||
baseRq = await createContentAPIRequest({ strapi });
|
||||
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq.setURLPrefix('/withdynamiczonemedias');
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
rq.setURLPrefix('/api/withdynamiczonemedias');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
// Helpers.
|
||||
const { createStrapiInstance } = require('../../../../test/helpers/strapi');
|
||||
const { createAuthRequest } = require('../../../../test/helpers/request');
|
||||
const { createContentAPIRequest } = require('../../../../test/helpers/request');
|
||||
const { createTestBuilder } = require('../../../../test/helpers/builder');
|
||||
const modelsUtils = require('../../../../test/helpers/models');
|
||||
|
||||
@ -29,7 +29,7 @@ describe('Create Strapi API End to End', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@ -4,7 +4,10 @@
|
||||
const _ = require('lodash');
|
||||
const { createStrapiInstance } = require('../../../../test/helpers/strapi');
|
||||
const { createTestBuilder } = require('../../../../test/helpers/builder');
|
||||
const { createAuthRequest, transformToRESTResource } = require('../../../../test/helpers/request');
|
||||
const {
|
||||
createContentAPIRequest,
|
||||
transformToRESTResource,
|
||||
} = require('../../../../test/helpers/request');
|
||||
|
||||
const builder = createTestBuilder();
|
||||
let strapi;
|
||||
@ -89,7 +92,7 @@ describe('Filtering API', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
|
||||
Object.assign(
|
||||
data,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { createStrapiInstance } = require('../../../../test/helpers/strapi');
|
||||
const { createAuthRequest } = require('../../../../test/helpers/request');
|
||||
const { createContentAPIRequest } = require('../../../../test/helpers/request');
|
||||
const { createTestBuilder } = require('../../../../test/helpers/builder');
|
||||
|
||||
const builder = createTestBuilder();
|
||||
@ -152,7 +152,7 @@ describe('Publication State', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
|
||||
Object.assign(data, builder.sanitizedFixtures(strapi));
|
||||
});
|
||||
|
||||
@ -3,7 +3,10 @@
|
||||
// Test an API with all the possible filed types and simple filtering (no deep filtering, no relations)
|
||||
const { createStrapiInstance } = require('../../../../test/helpers/strapi');
|
||||
const { createTestBuilder } = require('../../../../test/helpers/builder');
|
||||
const { createAuthRequest, transformToRESTResource } = require('../../../../test/helpers/request');
|
||||
const {
|
||||
createContentAPIRequest,
|
||||
transformToRESTResource,
|
||||
} = require('../../../../test/helpers/request');
|
||||
|
||||
const builder = createTestBuilder();
|
||||
let rq;
|
||||
@ -127,7 +130,7 @@ describe('Search query', () => {
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
|
||||
data.bed = builder.sanitizedFixturesFor(bedModel.name, strapi);
|
||||
});
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
// Helpers.
|
||||
const { createStrapiInstance } = require('../../../../test/helpers/strapi');
|
||||
const { createAuthRequest } = require('../../../../test/helpers/request');
|
||||
const { createContentAPIRequest } = require('../../../../test/helpers/request');
|
||||
const { createTestBuilder } = require('../../../../test/helpers/builder');
|
||||
|
||||
const builder = createTestBuilder();
|
||||
@ -26,7 +26,8 @@ describe('Content Manager single types', () => {
|
||||
await builder.addContentType(model).build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
|
||||
rq = await createContentAPIRequest({ strapi });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const uploadController = require('./upload');
|
||||
const upload = require('./upload');
|
||||
|
||||
module.exports = {
|
||||
upload: uploadController,
|
||||
upload,
|
||||
};
|
||||
|
||||
@ -38,18 +38,11 @@ module.exports = {
|
||||
getSettings: resolveControllerMethod('getSettings'),
|
||||
|
||||
async upload(ctx) {
|
||||
const isUploadDisabled = _.get(strapi.plugins, 'upload.config.enabled', true) === false;
|
||||
|
||||
if (isUploadDisabled) {
|
||||
throw strapi.errors.badRequest(null, {
|
||||
errors: [{ id: 'Upload.status.disabled', message: 'File upload is disabled' }],
|
||||
});
|
||||
}
|
||||
|
||||
const {
|
||||
query: { id },
|
||||
request: { files: { files } = {} },
|
||||
} = ctx;
|
||||
|
||||
const controller = resolveController(ctx);
|
||||
|
||||
if (id && (_.isEmpty(files) || files.size === 0)) {
|
||||
|
||||
@ -7,7 +7,7 @@ const { getService } = require('../../utils');
|
||||
|
||||
const sanitize = (data, options = {}) => {
|
||||
return sanitizeEntity(data, {
|
||||
model: strapi.getModel('file', 'upload'),
|
||||
model: strapi.getModel('plugin::upload.file'),
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
const { resolve } = require('path');
|
||||
const range = require('koa-range');
|
||||
const koaStatic = require('koa-static');
|
||||
const _ = require('lodash');
|
||||
|
||||
module.exports = {
|
||||
defaults: { upload: { enabled: true } },
|
||||
@ -15,7 +14,7 @@ module.exports = {
|
||||
);
|
||||
const staticDir = resolve(strapi.dir, configPublicPath);
|
||||
|
||||
strapi.app.on('error', err => {
|
||||
strapi.server.app.on('error', err => {
|
||||
if (err.code === 'EPIPE') {
|
||||
// when serving audio or video the browsers sometimes close the connection to go to range requests instead.
|
||||
// This causes koa to emit a write EPIPE error. We can ignore it.
|
||||
@ -23,16 +22,18 @@ module.exports = {
|
||||
return;
|
||||
}
|
||||
|
||||
strapi.app.onerror(err);
|
||||
strapi.server.app.onerror(err);
|
||||
});
|
||||
|
||||
const localServerConfig =
|
||||
_.get(strapi, 'plugins.upload.config.providerOptions.localServer') || {};
|
||||
strapi.router.get(
|
||||
'/uploads/(.*)',
|
||||
range,
|
||||
koaStatic(staticDir, { defer: true, ...localServerConfig })
|
||||
);
|
||||
const localServerConfig = strapi.config.get('plugin.upload.providerOptions.localeServer', {});
|
||||
|
||||
strapi.server.routes([
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/uploads/(.*)',
|
||||
handler: [range, koaStatic(staticDir, { defer: true, ...localServerConfig })],
|
||||
},
|
||||
]);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
119
packages/core/upload/server/routes/admin.js
Normal file
119
packages/core/upload/server/routes/admin.js
Normal file
@ -0,0 +1,119 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
type: 'admin',
|
||||
routes: [
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/settings',
|
||||
handler: 'upload.getSettings',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: {
|
||||
actions: ['plugin::upload.settings.read'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/settings',
|
||||
handler: 'upload.updateSettings',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: {
|
||||
actions: ['plugin::upload.settings.read'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/',
|
||||
handler: 'upload.upload',
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/files/count',
|
||||
handler: 'upload.count',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: {
|
||||
actions: ['plugin::upload.read'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/files',
|
||||
handler: 'upload.find',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: {
|
||||
actions: ['plugin::upload.read'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/files/:id',
|
||||
handler: 'upload.findOne',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: {
|
||||
actions: ['plugin::upload.read'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/search/:id',
|
||||
handler: 'upload.search',
|
||||
config: {
|
||||
policies: ['admin::isAuthenticatedAdmin'],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/files/:id',
|
||||
handler: 'upload.destroy',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
options: {
|
||||
actions: ['plugin::upload.assets.update'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
32
packages/core/upload/server/routes/content-api.js
Normal file
32
packages/core/upload/server/routes/content-api.js
Normal file
@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
type: 'content-api',
|
||||
routes: [
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/',
|
||||
handler: 'upload.upload',
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/files/count',
|
||||
handler: 'upload.count',
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/files',
|
||||
handler: 'upload.find',
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/files/:id',
|
||||
handler: 'upload.findOne',
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/files/:id',
|
||||
handler: 'upload.destroy',
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -1,98 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/settings',
|
||||
handler: 'upload.getSettings',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: '/settings',
|
||||
handler: 'upload.updateSettings',
|
||||
config: {
|
||||
policies: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/',
|
||||
handler: 'upload.upload',
|
||||
config: {
|
||||
policies: [],
|
||||
description: 'upload a file',
|
||||
tag: {
|
||||
plugin: 'upload',
|
||||
name: 'File',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/files/count',
|
||||
handler: 'upload.count',
|
||||
config: {
|
||||
policies: [],
|
||||
description: 'Retrieve the total number of uploaded files',
|
||||
tag: {
|
||||
plugin: 'upload',
|
||||
name: 'File',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/files',
|
||||
handler: 'upload.find',
|
||||
config: {
|
||||
policies: [],
|
||||
description: 'Retrieve all file documents',
|
||||
tag: {
|
||||
plugin: 'upload',
|
||||
name: 'File',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/files/:id',
|
||||
handler: 'upload.findOne',
|
||||
config: {
|
||||
policies: [],
|
||||
description: 'Retrieve a single file depending on its id',
|
||||
tag: {
|
||||
plugin: 'upload',
|
||||
name: 'File',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/search/:id',
|
||||
handler: 'upload.search',
|
||||
config: {
|
||||
policies: [],
|
||||
description: 'Search for an uploaded file',
|
||||
tag: {
|
||||
plugin: 'upload',
|
||||
name: 'File',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'DELETE',
|
||||
path: '/files/:id',
|
||||
handler: 'upload.destroy',
|
||||
config: {
|
||||
policies: [],
|
||||
description: 'Delete an uploaded file',
|
||||
tag: {
|
||||
plugin: 'upload',
|
||||
name: 'File',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
module.exports = {
|
||||
admin: require('./admin'),
|
||||
'content-api': require('./content-api'),
|
||||
};
|
||||
|
||||
@ -53,7 +53,7 @@ const combineFilters = params => {
|
||||
|
||||
module.exports = ({ strapi }) => ({
|
||||
emitEvent(event, data) {
|
||||
const modelDef = strapi.getModel('file', 'upload');
|
||||
const modelDef = strapi.getModel('plugin::upload.file');
|
||||
strapi.eventHub.emit(event, { media: sanitizeEntity(data, { model: modelDef }) });
|
||||
},
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user