Merge branch 'master' into fix-filter-doc

This commit is contained in:
Jim LAURIE 2018-02-13 11:28:24 +01:00 committed by GitHub
commit df48e85ff5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 552 additions and 218 deletions

View File

@ -28,6 +28,10 @@
We've been working on a major update to Strapi for several months now, rewriting the core framework and the administration panel. Performances has been increased, Developer eXperience has been improved and a brand new plugins
ecosystem has been introduced. **Both versions are available, we still recommend you to use v1 for production usage.**.
<a href="https://heroku.com/deploy?template=https://github.com/strapi/strapi-heroku-app">
<img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy">
</a>
#### Alpha
The alpha has support for the latest version of Node.js (v8) and npm (v5).

View File

@ -9,13 +9,16 @@ The most advanced open-source Content Management Framework to build powerful API
[![npm downloads](https://img.shields.io/npm/dm/strapi.svg)](https://www.npmjs.org/package/strapi)
[![Build status](https://travis-ci.org/strapi/strapi.svg?branch=master)](https://travis-ci.org/strapi/strapi)
[![Slack status](http://strapi-slack.herokuapp.com/badge.svg)](http://slack.strapi.io)
<a href="https://heroku.com/deploy?template=https://github.com/strapi/strapi-heroku-app">
<img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy" height="20px">
</a>
{% endcenter %}
## v3@alpha.8 is available!
## v3@alpha.9 is available!
We've been working on a major update for Strapi during the past months, rewriting the core framework and the dashboard.
This documentation is only related to Strapi v3@alpha.8 ([v1 documentation is still available](http://strapi.io/documentation/1.x.x)).
This documentation is only related to Strapi v3@alpha.9 ([v1 documentation is still available](http://strapi.io/documentation/1.x.x)).
**[Get Started](getting-started/installation.md)**<br />
Learn how to install Strapi and start developing your API.

View File

@ -49,7 +49,7 @@
* [Table of contents](api-reference/reference.md)
### Tutorials
* Coming soon
* [Table of contents](tutorials/README.md)
### Migration
* [Migrating from v1 to v3](migration/migration-guide.md)

View File

@ -9,8 +9,6 @@ Contains the main configurations relative to your project.
**Path —** `./config/application.json`.
```json
{
"name": "Default Application",
"description": "This API is going to be awesome!",
"favicon": {
"path": "favicon.ico",
"maxAge": 86400000
@ -22,8 +20,6 @@ Contains the main configurations relative to your project.
}
```
- `name` (string): Application's name.
- `description` (string): Application's description.
- `favicon`
- `path` (string): Path to the favicon file. Default value: `favicon.ico`.
- `maxAge` (integer): Cache-control max-age directive in ms. Default value: `86400000`.
@ -351,20 +347,76 @@ The syntax is inspired by the [template literals ES2015 specifications](https://
In any JSON configurations files in your project, you can inject dynamic values like this:
**Path —** `./config/application.json`.
**Path —** `./config/environments/production/database.json`.
```json
{
"name": "${process.env.APP_NAME}",
"description": "${process.env.APP_DESCRIPTION}",
"favicon": {
"path": "favicon.ico",
"maxAge": 86400000
},
"public": {
"path": "./public",
"maxAge": 60000
"defaultConnection": "default",
"connections": {
"default": {
"connector": "strapi-mongoose",
"settings": {
"client": "mongo",
"uri": "${process.env.DATABASE_URI || ''}",
"host": "${process.env.DATABASE_HOST || '127.0.0.1'}",
"port": "${process.env.DATABASE_PORT || 27017}",
"database": "${process.env.DATABASE_NAME || 'production'}",
"username": "${process.env.DATABASE_USERNAME || ''}",
"password": "${process.env.DATABASE_PASSWORD || ''}"
},
"options": {}
}
}
}
```
> Note: You can't execute functions inside the curly braces. Only strings are allowed.
***
## Database configuration
Configuration files are not multi server friendly. So we create a data store for config you will want to update in production.
#### Usage
## Get settings:
- `environment` (string): Sets the environment you want to store the data in. By default it's current environment (can be an empty string if your config is environment agnostic).
- `type` (string): Sets if your config is for an `api`, `plugin` or `core`. By default it's `core`.
- `name` (string): You have to set the plugin or api name if `type` is `api` or `plugin`.
- `key` (string, required): The name of the key you want to store.
```
// strapi.store(object).get(object);
// create reusable plugin store variable
const pluginStore = strapi.store({
environment: strapi.config.environment,
type: 'plugin',
name: 'users-permissions'
});
await pluginStore.get({key: 'grant'});
```
## Set settings:
- `value` (any, required): The value you want to store.
```
// strapi.store(object).set(object);
// create reusable plugin store variable
const pluginStore = strapi.store({
environment: strapi.config.environment,
type: 'plugin',
name: 'users-permissions'
});
await pluginStore.set({
key: 'grant',
value: {
...
}
});
```

View File

@ -0,0 +1,13 @@
## Tutorials
After having realized the [Getting Started guide](https://strapi.io/getting-started), it is time to go further with Strapi. Official and community tutorials are here to help you:
### Development
- [Building a static blog using Gatsby and Strapi (official)](https://hackernoon.com/building-a-static-blog-using-gatsby-and-strapi-8b5acfc82ad8)
### Deployment
- [Using mLab with Strapi (official)](https://medium.com/@strapi/using-mlab-with-strapi-e3f968a9c530)
- [How to deploy a Strapi API on Ubuntu 16.04 (official)](https://medium.com/@strapi/how-to-deploy-a-strapi-api-on-ubuntu-16-04-17f8fbbf5c5b)
- [Deploying a Strapi API on Heroku (official)](https://medium.com/@strapi/deploying-a-strapi-api-on-heroku-9c8b7809675c)

View File

@ -8,7 +8,7 @@ const path = require('path');
const logger = require('strapi-utils').logger;
module.exports = (scope, success, error) => {
const knex = require(path.resolve(`${scope.rootPath}/node_modules/knex`))({
const knex = require(path.resolve(`${scope.tmpPath}/node_modules/knex`))({
client: scope.client.module,
connection: Object.assign({}, scope.database.settings, {
user: scope.database.settings.username
@ -18,7 +18,7 @@ module.exports = (scope, success, error) => {
knex.raw('select 1+1 as result').then(() => {
logger.info('The app has been connected to the database successfully');
knex.destroy();
execSync(`rm -r ${scope.rootPath}`);
execSync(`rm -r ${scope.tmpPath}`);
logger.info('Copying the dashboard...');

View File

@ -1,6 +1,4 @@
{
"name": "Default Application",
"description": "This API is going to be awesome!",
"favicon": {
"path": "favicon.ico",
"maxAge": 86400000

View File

@ -41,11 +41,12 @@ module.exports = (scope, cb) => {
// Make changes to the rootPath where the Strapi project will be created.
scope.rootPath = path.resolve(process.cwd(), scope.name || '');
scope.tmpPath = path.resolve(process.cwd(), 'tmp');
// Ensure we aren't going to inadvertently delete any files.
try {
const files = fs.readdirSync(scope.rootPath);
if (files.length) {
if (files.length > 1) {
return logger.error('`$ strapi new` can only be called in an empty directory.');
}
} catch (err) {
@ -212,14 +213,14 @@ module.exports = (scope, cb) => {
});
}),
new Promise(resolve => {
let cmd = `npm install --prefix "${scope.rootPath}" ${scope.client.connector}@alpha`;
let cmd = `npm install --prefix "${scope.tmpPath}" ${scope.client.connector}@alpha`;
if (scope.client.module) {
cmd += ` ${scope.client.module}`;
}
exec(cmd, () => {
if (scope.client.module) {
const lock = require(path.join(`${scope.rootPath}`,`/node_modules/`,`${scope.client.module}/package.json`));
const lock = require(path.join(`${scope.tmpPath}`,`/node_modules/`,`${scope.client.module}/package.json`));
scope.client.version = lock.version;
}
@ -231,9 +232,9 @@ module.exports = (scope, cb) => {
Promise.all(asyncFn)
.then(() => {
try {
require(path.join(`${scope.rootPath}`,`/node_modules/`,`${scope.client.connector}/lib/utils/connectivity.js`))(scope, cb.success, connectionValidation);
require(path.join(`${scope.tmpPath}`,`/node_modules/`,`${scope.client.connector}/lib/utils/connectivity.js`))(scope, cb.success, connectionValidation);
} catch(err) {
shell.rm('-r', scope.rootPath);
shell.rm('-r', scope.tmpPath);
logger.info('Copying the dashboard...');
cb.success();
}

View File

@ -8,7 +8,7 @@ const path = require('path');
const logger = require('strapi-utils').logger;
module.exports = (scope, success, error) => {
const Mongoose = require(path.resolve(`${scope.rootPath}/node_modules/mongoose`));
const Mongoose = require(path.resolve(`${scope.tmpPath}/node_modules/mongoose`));
const { username, password } = scope.database.settings
const connectOptions = {}
@ -28,7 +28,7 @@ module.exports = (scope, success, error) => {
Mongoose.connection.close();
execSync(`rm -r ${scope.rootPath}`);
execSync(`rm -r ${scope.tmpPath}`);
logger.info('Copying the dashboard...');

View File

@ -22,8 +22,11 @@ module.exports = {
'associations'
]);
const models = _.mapValues(strapi.models, pickData);
delete models['core_store'];
ctx.body = {
models: _.mapValues(strapi.models, pickData),
models,
plugins: Object.keys(strapi.plugins).reduce((acc, current) => {
acc[current] = {
models: _.mapValues(strapi.plugins[current].models, pickData)

View File

@ -10,6 +10,10 @@ module.exports = {
const models = [];
_.forEach(strapi.models, (model, name) => {
if (name === 'core_store') {
return true;
}
models.push({
icon: 'fa-cube',
name: _.get(model, 'info.name', 'model.name.missing'),

View File

@ -0,0 +1,27 @@
'use strict';
/**
* An asynchronous bootstrap function that runs before
* your application gets started.
*
* This gives you an opportunity to set up your data model,
* run jobs, or perform some special logic.
*/
module.exports = async cb => {
const pluginStore = strapi.store({
environment: '',
type: 'core'
});
if (!await pluginStore.get({key: 'application'})) {
const value = {
name: 'Default Application',
description: 'This API is going to be awesome!'
};
await pluginStore.set({key: 'application', value});
}
cb();
};

View File

@ -59,7 +59,7 @@ module.exports = {
if (env && _.isEmpty(_.find(Service.getEnvironments(), { name: env }))) return ctx.badRequest(null, [{ messages: [{ id: 'request.error.environment.unknown' }] }]);
_.has(Service, slug) ? ctx.send(Service[slug](env)) : ctx.badRequest(null, [{ messages: [{ id: 'request.error.config' }] }]);
_.has(Service, slug) ? ctx.send(await Service[slug](env)) : ctx.badRequest(null, [{ messages: [{ id: 'request.error.config' }] }]);
},
update: async ctx => {
@ -71,7 +71,7 @@ module.exports = {
let model;
if (_.has(Service, slug)) {
model = Service[slug](env);
model = await Service[slug](env);
} else {
return ctx.badRequest(null, [{ messages: [{ id: 'request.error.config' }] }]);
}
@ -87,7 +87,7 @@ module.exports = {
strapi.reload.isWatching = false;
const updateErrors = Service.updateSettings(params, items, env);
const updateErrors = await Service.updateSettings(params, items, env);
!_.isEmpty(updateErrors) ? ctx.badRequest(null, Service.formatErrors(updateErrors)) : ctx.send({ ok: true });

View File

@ -56,47 +56,57 @@ module.exports = {
]
},
application: () => ({
name: 'form.application.name',
description: 'form.application.description',
sections: [
{
name: '',
items: [
{
name: 'form.application.item.name',
target: 'application.name',
type: 'string',
value: _.get(strapi.config, 'name', null),
validations : {
maxLength: 255,
required: true
application: async () => {
const application = await strapi.store({
environment: '',
type: 'core',
key: 'application'
}).get();
return {
name: 'form.application.name',
description: 'form.application.description',
sections: [
{
name: '',
items: [
{
name: 'form.application.item.name',
target: 'application.name',
source: 'db',
type: 'string',
value: _.get(application, 'name', null),
validations : {
maxLength: 255,
required: true
}
},
{
name: 'form.application.item.description',
target: 'application.description',
source: 'db',
type: 'string',
value: _.get(application, 'description', null),
validations : {
maxLength: 255,
required: true
}
},
{
name: 'form.application.item.version',
target: 'package.version',
type: 'string',
value: _.get(strapi.config, 'info.version', null),
validations : {
regex: '^(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)$',
required: true
}
}
},
{
name: 'form.application.item.description',
target: 'application.description',
type: 'string',
value: _.get(strapi.config, 'description', null),
validations : {
maxLength: 255,
required: true
}
},
{
name: 'form.application.item.version',
target: 'package.version',
type: 'string',
value: _.get(strapi.config, 'info.version', null),
validations : {
regex: '^(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)$',
required: true
}
}
]
}
]
}),
]
}
]
}
},
request: env => ({
name: 'form.request.name',
@ -806,15 +816,37 @@ module.exports = {
return [params, errors];
},
updateSettings: (params, items, env = '') => {
updateSettings: async (params, items, env = '') => {
const appPath = strapi.config.appPath;
const errors = [];
_.forEach(items, ({ target }) => {
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
await asyncForEach(items, async ({ target, source }) => {
if (_.has(params, target)) {
let input = _.get(params, target, null);
const [file, ...objPath] = target.split('.');
if (source === 'db') {
const store = strapi.store({
environment: env,
type: 'core',
key: file
});
const data = await store.get();
_.set(data, objPath, input);
await store.set({value: data});
return;
}
if (target === 'language.defaultLocale') input = _.lowerCase(input).replace(/ /g, '_');
const filePath = (file === 'package') ? path.join(appPath, 'package.json') : path.join(appPath, 'config', `${env ? `environments/${env}` : ''}`, `${_.replace(file, '.', '/')}.json`);

View File

@ -3,10 +3,7 @@ coverage
build
node_modules
jwt.json
grant.json
actions.json
email.json
advanced.json
# Cruft
.DS_Store

View File

@ -70,7 +70,7 @@ export function* submitData(action) {
const body = yield select(makeSelectModifiedData());
const opts = { method: 'PUT', body };
yield call(request, `/users-permissions/${action.endPoint}`, opts, true);
yield call(request, `/users-permissions/${action.endPoint}`, opts);
yield put(submitSucceeded());
} catch(error) {
strapi.notification.error('notification.error');

View File

@ -1,6 +0,0 @@
{
"advanced": {
"unique_email": true,
"allow_register": true
}
}

View File

@ -13,7 +13,7 @@ const _ = require('lodash');
const fs = require('fs');
const uuid = require('uuid/v4');
module.exports = cb => {
module.exports = async cb => {
if (!_.get(strapi.plugins['users-permissions'], 'config.jwtSecret')) {
try {
const jwtSecret = uuid();
@ -28,108 +28,90 @@ module.exports = cb => {
}
}
if (!_.get(strapi.plugins['users-permissions'], 'config.grant')) {
try {
const grant = {
email: {
enabled: true,
icon: 'envelope'
},
facebook: {
enabled: false,
icon: 'facebook-official',
key: '',
secret: '',
callback: '/auth/facebook/callback',
scope: ['email']
},
google: {
enabled: false,
icon: 'google',
key: '',
secret: '',
callback: '/auth/google/callback',
scope: ['email']
},
github: {
enabled: false,
icon: 'github',
key: '',
secret: '',
redirect_uri: '/auth/github/callback',
scope: [
'user',
'user:email'
]
},
twitter: {
enabled: false,
icon: 'twitter',
key: '',
secret: '',
callback: '/auth/twitter/callback'
}
};
const pluginStore = strapi.store({
environment: strapi.config.environment,
type: 'plugin',
name: 'users-permissions'
});
fs.writeFileSync(path.join(strapi.config.appPath, 'plugins', 'users-permissions', 'config', 'grant.json'), JSON.stringify({
grant
}, null, 2), 'utf8');
if (!await pluginStore.get({key: 'grant'})) {
const value = {
email: {
enabled: true,
icon: 'envelope'
},
facebook: {
enabled: false,
icon: 'facebook-official',
key: '',
secret: '',
callback: '/auth/facebook/callback',
scope: ['email']
},
google: {
enabled: false,
icon: 'google',
key: '',
secret: '',
callback: '/auth/google/callback',
scope: ['email']
},
github: {
enabled: false,
icon: 'github',
key: '',
secret: '',
redirect_uri: '/auth/github/callback',
scope: [
'user',
'user:email'
]
},
twitter: {
enabled: false,
icon: 'twitter',
key: '',
secret: '',
callback: '/auth/twitter/callback'
}
};
_.set(strapi.plugins['users-permissions'], 'config.grant', grant);
} catch(err) {
strapi.log.error(err);
}
await pluginStore.set({key: 'grant', value});
}
if (!_.get(strapi.plugins['users-permissions'], 'config.email')) {
try {
const email = {
'reset_password': {
display: 'Email.template.reset_password',
icon: 'refresh',
options: {
from: {
name: 'Administration Panel',
email: 'no-reply@strapi.io'
},
response_email: '',
object: '­Reset password 🔑 ',
message: `<p>We heard that you lost your password. Sorry about that!</p>
if (!await pluginStore.get({key: 'email'})) {
const value = {
'reset_password': {
display: 'Email.template.reset_password',
icon: 'refresh',
options: {
from: {
name: 'Administration Panel',
email: 'no-reply@strapi.io'
},
response_email: '',
object: '­Reset password 🔑 ',
message: `<p>We heard that you lost your password. Sorry about that!</p>
<p>But dont worry! You can use the following link to reset your password:</p>
<p><%= URL %>?code=<%= TOKEN %></p>
<p>Thanks.</p>`
}
}
};
}
};
fs.writeFileSync(path.join(strapi.config.appPath, 'plugins', 'users-permissions', 'config', 'email.json'), JSON.stringify({
email
}, null, 2), 'utf8');
_.set(strapi.plugins['users-permissions'], 'config.email', email);
} catch(err) {
strapi.log.error(err);
}
await pluginStore.set({key: 'email', value});
}
if (!_.get(strapi.plugins['users-permissions'], 'config.advanced')) {
try {
const advanced = {
unique_email: true,
allow_register: true
};
if (!await pluginStore.get({key: 'advanced'})) {
const value = {
unique_email: true,
allow_register: true
};
fs.writeFileSync(path.join(strapi.config.appPath, 'plugins', 'users-permissions', 'config', 'advanced.json'), JSON.stringify({
advanced
}, null, 2), 'utf8');
_.set(strapi.plugins['users-permissions'], 'config.advanced', advanced);
} catch(err) {
strapi.log.error(err);
}
await pluginStore.set({key: 'advanced', value});
}
strapi.plugins['users-permissions'].services.userspermissions.syncSchema(() => {

View File

@ -15,8 +15,13 @@ module.exports = {
const provider = ctx.params.provider || 'local';
const params = ctx.request.body;
const store = await strapi.store({
type: 'plugin',
name: 'users-permissions'
});
if (provider === 'local') {
if (!_.get(strapi.plugins['users-permissions'].config.grant['email'], 'enabled') && !ctx.request.admin) {
if (!_.get(await store.get({key: 'grant'}), 'email.enabled') && !ctx.request.admin) {
return ctx.badRequest(null, 'This provider is disabled.');
}
@ -69,7 +74,7 @@ module.exports = {
});
}
} else {
if (!_.get(strapi.plugins['users-permissions'].config.grant[provider], 'enabled')) {
if (!_.get(await store.get({key: 'grant'}), [provider, 'enabled'])) {
return ctx.badRequest(null, 'This provider is disabled.');
}
@ -122,7 +127,13 @@ module.exports = {
},
connect: async (ctx, next) => {
_.defaultsDeep(strapi.plugins['users-permissions'].config.grant, {
const grantConfig = await strapi.store({
type: 'plugin',
name: 'users-permissions',
key: 'grant'
}).get();
_.defaultsDeep(grantConfig, {
server: {
protocol: 'http',
host: `${strapi.config.currentEnvironment.server.host}:${strapi.config.currentEnvironment.server.port}`
@ -130,14 +141,14 @@ module.exports = {
});
const provider = ctx.request.url.split('/')[2];
const config = strapi.plugins['users-permissions'].config.grant[provider];
const config = grantConfig[provider];
if (!_.get(config, 'enabled')) {
return ctx.badRequest(null, 'This provider is disabled.');
}
const Grant = require('grant-koa');
const grant = new Grant(strapi.plugins['users-permissions'].config.grant);
const grant = new Grant(grantConfig);
return strapi.koaMiddlewares.compose(grant.middleware)(ctx, next);
},
@ -192,7 +203,11 @@ module.exports = {
},
register: async (ctx) => {
if (!strapi.plugins['users-permissions'].config.advanced.allow_register) {
if (!(await strapi.store({
type: 'plugin',
name: 'users-permissions',
key: 'advanced'
}).get()).allow_register) {
return ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: 'Auth.advanced.allow_register' }] }] : 'Register action is currently disabled.');
}

View File

@ -70,7 +70,11 @@ module.exports = {
*/
create: async (ctx) => {
if (strapi.plugins['users-permissions'].config.advanced.unique_email && ctx.request.body.email) {
if ((await strapi.store({
type: 'plugin',
name: 'users-permissions',
key: 'advanced'
}).get()).unique_email && ctx.request.body.email) {
const user = await strapi.query('user', 'users-permissions').findOne({ email: ctx.request.body.email });
if (user) {
@ -96,7 +100,13 @@ module.exports = {
update: async (ctx, next) => {
try {
if (strapi.plugins['users-permissions'].config.advanced.unique_email && ctx.request.body.email) {
const advancedConfigs = await strapi.store({
type: 'plugin',
name: 'users-permissions',
key: 'advanced'
}).get();
if (advancedConfigs.unique_email && ctx.request.body.email) {
const users = await strapi.plugins['users-permissions'].services.user.fetchAll({ email: ctx.request.body.email });
if (users && _.find(users, user => (user.id || user._id).toString() !== ctx.params.id)) {
@ -114,7 +124,7 @@ module.exports = {
delete ctx.request.body.role;
}
if (ctx.request.body.email && strapi.plugins['users-permissions'].config.advanced.unique_email) {
if (ctx.request.body.email && advancedConfigs.unique_email) {
const user = await strapi.query('user', 'users-permissions').findOne({
email: ctx.request.body.email
});

View File

@ -171,7 +171,11 @@ module.exports = {
},
getEmailTemplate: async (ctx) => {
ctx.send(strapi.plugins['users-permissions'].config.email);
ctx.send(await strapi.store({
type: 'plugin',
name: 'users-permissions',
key: 'email'
}).get());
},
updateEmailTemplate: async (ctx) => {
@ -179,19 +183,21 @@ module.exports = {
return ctx.badRequest(null, [{ messages: [{ id: 'Cannot be empty' }] }]);
}
strapi.reload.isWatching = false;
fs.writeFileSync(path.join(strapi.config.appPath, 'plugins', 'users-permissions', 'config', 'email.json'), JSON.stringify({
email: ctx.request.body
}, null, 2), 'utf8');
await strapi.store({
type: 'plugin',
name: 'users-permissions',
key: 'email'
}).set({value: ctx.request.body})
ctx.send({ ok: true });
strapi.reload();
},
getAdvancedSettings: async (ctx) => {
ctx.send(strapi.plugins['users-permissions'].config.advanced);
ctx.send(await strapi.store({
type: 'plugin',
name: 'users-permissions',
key: 'advanced'
}).get());
},
updateAdvancedSettings: async (ctx) => {
@ -199,19 +205,21 @@ module.exports = {
return ctx.badRequest(null, [{ messages: [{ id: 'Cannot be empty' }] }]);
}
strapi.reload.isWatching = false;
fs.writeFileSync(path.join(strapi.config.appPath, 'plugins', 'users-permissions', 'config', 'advanced.json'), JSON.stringify({
advanced: ctx.request.body
}, null, 2), 'utf8');
await strapi.store({
type: 'plugin',
name: 'users-permissions',
key: 'advanced'
}).set({value: ctx.request.body})
ctx.send({ ok: true });
strapi.reload();
},
getProviders: async (ctx) => {
ctx.send(strapi.plugins['users-permissions'].config.grant);
ctx.send(await strapi.store({
type: 'plugin',
name: 'users-permissions',
key: 'grant'
}).get());
},
updateProviders: async (ctx) => {
@ -219,15 +227,12 @@ module.exports = {
return ctx.badRequest(null, [{ messages: [{ id: 'Cannot be empty' }] }]);
}
strapi.reload.isWatching = false;
fs.writeFileSync(path.join(strapi.config.appPath, 'plugins', 'users-permissions', 'config', 'grant.json'), JSON.stringify({
grant: ctx.request.body
}, null, 2), 'utf8');
await strapi.store({
type: 'plugin',
name: 'users-permissions',
key: 'grant'
}).set({value: ctx.request.body})
ctx.send({ ok: true });
strapi.reload();
}
};

View File

@ -49,7 +49,13 @@ exports.connect = (provider, query) => {
email: profile.email
});
if (_.isEmpty(_.find(users, {provider})) && !strapi.plugins['users-permissions'].config.advanced.allow_register) {
const advanced = await strapi.store({
type: 'plugin',
name: 'users-permissions',
key: 'advanced'
}).get();
if (_.isEmpty(_.find(users, {provider})) && !advanced.allow_register) {
return resolve([null, [{ messages: [{ id: 'Auth.advanced.allow_register' }] }], 'Register action is actualy not available.']);
}
@ -57,7 +63,7 @@ exports.connect = (provider, query) => {
return resolve([user, null]);
}
if (!_.isEmpty(_.find(users, user => user.provider !== provider)) && strapi.plugins['users-permissions'].config.advanced.unique_email) {
if (!_.isEmpty(_.find(users, user => user.provider !== provider)) && advanced.unique_email) {
return resolve([null, [{ messages: [{ id: 'Auth.form.error.email.taken' }] }], 'Email is already taken.']);
}
@ -87,9 +93,15 @@ exports.connect = (provider, query) => {
* @param {Function} callback
*/
const getProfile = (provider, query, callback) => {
const getProfile = async (provider, query, callback) => {
const access_token = query.access_token || query.code || query.oauth_token;
const grant = await strapi.store({
type: 'plugin',
name: 'users-permissions',
key: 'grant'
}).get();
switch (provider) {
case 'facebook':
const facebook = new Purest({
@ -136,8 +148,8 @@ const getProfile = (provider, query, callback) => {
request.post({
url: 'https://github.com/login/oauth/access_token',
form: {
client_id: strapi.plugins['users-permissions'].config.grant.github.key,
client_secret: strapi.plugins['users-permissions'].config.grant.github.secret,
client_id: grant.github.key,
client_secret: grant.github.secret,
code: access_token
}
}, (err, res, body) => {
@ -156,8 +168,8 @@ const getProfile = (provider, query, callback) => {
case 'twitter':
const twitter = new Purest({
provider: 'twitter',
key: strapi.plugins['users-permissions'].config.grant.twitter.key,
secret: strapi.plugins['users-permissions'].config.grant.twitter.secret
key: grant.twitter.key,
secret: grant.twitter.secret
});
twitter.query().get('account/verify_credentials').auth(access_token, query.access_secret).qs({screen_name: query['raw[screen_name]'], include_email: 'true'}).request((err, res, body) => {

View File

@ -8,7 +8,7 @@ const path = require('path');
const logger = require('strapi-utils').logger;
module.exports = (scope, success, error) => {
const Redis = require(`${scope.rootPath}/node_modules/ioredis`);
const Redis = require(`${scope.tmpPath}/node_modules/ioredis`);
const redis = new Redis({
port: scope.database.settings.port,
host: scope.database.settings.host,
@ -26,7 +26,7 @@ module.exports = (scope, success, error) => {
logger.info('The app has been connected to the database successfully!');
execSync(`rm -r ${scope.rootPath}`);
execSync(`rm -r ${scope.tmpPath}`);
logger.info('Copying the dashboard...');

View File

@ -8,7 +8,7 @@ const path = require('path');
const cluster = require('cluster');
const { includes, get, assign, forEach } = require('lodash');
const { logger, models } = require('strapi-utils');
const { nestedConfigurations, appConfigurations, apis, middlewares, hooks, plugins, admin } = require('./core');
const { nestedConfigurations, appConfigurations, apis, middlewares, hooks, plugins, admin, store } = require('./core');
const initializeMiddlewares = require('./middlewares');
const initializeHooks = require('./hooks');
const { EventEmitter } = require('events');
@ -179,12 +179,18 @@ class Strapi extends EventEmitter {
// Populate AST with configurations.
await appConfigurations.call(this);
// Init core store manager
await store.pre.call(this);
// Initialize hooks and middlewares.
await Promise.all([
initializeMiddlewares.call(this),
initializeHooks.call(this)
]);
// Core store post middleware and hooks init validation.
await store.post.call(this);
// Harmonize plugins configuration.
await plugins.call(this);
}

View File

@ -6,6 +6,7 @@ const middlewares = require('./middlewares');
const hooks = require('./hooks');
const plugins = require('./plugins');
const admin = require('./admin');
const store = require('./store');
module.exports = {
nestedConfigurations: nested,
@ -14,5 +15,6 @@ module.exports = {
middlewares,
hooks,
plugins,
admin
admin,
store
};

View File

@ -0,0 +1,174 @@
'use strict';
module.exports = {
pre: function () {
return new Promise((resolve, reject) => {
this.models['core_store'] = {
connection: 'default',
info: {
name: 'core_store',
description: ''
},
attributes: {
key: {
type: 'string'
},
value: {
type: 'string'
},
type: {
type: 'string'
},
environment: {
type: 'string'
},
tag: {
type: 'string'
}
},
globalId: 'StrapiConfigs',
collectionName: 'core_store'
};
this.store = (source = {}) => {
const get = async (params = {}) => {
Object.assign(source, params);
const {
key,
environment = strapi.config.environment,
type = 'core',
name = '',
tag = ''
} = source;
const prefix = `${type}${name ? `_${name}` : ''}`;
const findAction = strapi.models['core_store'].orm === 'mongoose' ? 'findOne' : 'forge';
const where = {
key: `${prefix}_${key}`,
environment,
tag
};
const data = strapi.models['core_store'].orm === 'mongoose'
? await strapi.models['core_store'].findOne(where)
: await strapi.models['core_store'].forge(where).fetch().then(config => {
if (config) {
return config.toJSON();
}
});
if (!data) {
return null;
}
if (data.type === 'object' || data.type === 'array' || data.type === 'boolean') {
try {
return JSON.parse(data.value);
} catch (err) {
return new Date(data.value);
}
} else if (data.type === 'number') {
return parseFloat(data.value);
} else {
return null;
}
};
const set = async (params = {}) => {
Object.assign(source, params);
const {
key,
value,
environment = strapi.config.environment,
type,
name,
tag = ''
} = source;
const prefix = `${type}${name ? `_${name}` : ''}`;
const where = {
key: `${prefix}_${key}`,
environment,
tag
};
let data = strapi.models['core_store'].orm === 'mongoose'
? await strapi.models['core_store'].findOne(where)
: await strapi.models['core_store'].forge(where).fetch().then(config => {
if (config) {
return config.toJSON();
}
});
if (data) {
Object.assign(data, {
value: JSON.stringify(value) || value.toString(),
type: (typeof value).toString()
});
strapi.models['core_store'].orm === 'mongoose'
? await strapi.models['core_store'].update({ _id: data._id }, data, { strict: false })
: await strapi.models['core_store'].forge({ id: data.id }).save(data, { patch: true });
} else {
Object.assign(where, {
value: JSON.stringify(value) || value.toString(),
type: (typeof value).toString(),
tag
});
strapi.models['core_store'].orm === 'mongoose'
? await strapi.models['core_store'].create(where)
: await strapi.models['core_store'].forge().save(where);
}
};
return {
get,
set
}
}
resolve();
});
},
post: function () {
return new Promise(async (resolve, reject) => {
const Model = this.models['core_store'];
if (Model.orm !== 'bookshelf') {
return resolve();
}
const hasTable = await this.connections[Model.connection].schema.hasTable(Model.tableName || Model.collectionName);
if (!hasTable) {
const quote = Model.client === 'pg' ? '"' : '`';
console.log(`
TABLE \`core_store\` DOESN'T EXIST
CREATE TABLE ${quote}${Model.tableName || Model.collectionName}${quote} (
id ${Model.client === 'pg' ? 'SERIAL' : 'INT AUTO_INCREMENT'} NOT NULL PRIMARY KEY,
key text,
value text,
environment text,
type text,
tag text
);
ALTER TABLE ${quote}${Model.tableName || Model.collectionName}${quote} ADD COLUMN ${quote}parent${quote} integer, ADD FOREIGN KEY (${quote}parent${quote}) REFERENCES ${quote}${Model.tableName || Model.collectionName}${quote}(${quote}id${quote});
`);
// Stop the server.
return this.stop();
}
resolve();
});
}
};

View File

@ -70,7 +70,7 @@ module.exports = strapi => {
};
// Only pick successful JSON requests.
if ([200, 201, 202].includes(ctx.status) && ctx.type === 'application/json') {
if ([200, 201, 202].includes(ctx.status) && ctx.type === 'application/json' && !ctx.request.admin) {
ctx.body = mask(ctx.body);
}
});