mirror of
https://github.com/strapi/strapi.git
synced 2025-12-25 22:23:10 +00:00
Merge branch 'master' into fix-filter-doc
This commit is contained in:
commit
df48e85ff5
@ -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).
|
||||
|
||||
@ -9,13 +9,16 @@ The most advanced open-source Content Management Framework to build powerful API
|
||||
[](https://www.npmjs.org/package/strapi)
|
||||
[](https://travis-ci.org/strapi/strapi)
|
||||
[](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.
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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: {
|
||||
...
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
13
docs/3.x.x/en/tutorials/README.md
Normal file
13
docs/3.x.x/en/tutorials/README.md
Normal 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)
|
||||
@ -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...');
|
||||
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
{
|
||||
"name": "Default Application",
|
||||
"description": "This API is going to be awesome!",
|
||||
"favicon": {
|
||||
"path": "favicon.ico",
|
||||
"maxAge": 86400000
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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...');
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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'),
|
||||
|
||||
27
packages/strapi-plugin-settings-manager/config/functions/bootstrap.js
vendored
Normal file
27
packages/strapi-plugin-settings-manager/config/functions/bootstrap.js
vendored
Normal 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();
|
||||
};
|
||||
@ -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 });
|
||||
|
||||
|
||||
@ -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`);
|
||||
|
||||
@ -3,10 +3,7 @@ coverage
|
||||
build
|
||||
node_modules
|
||||
jwt.json
|
||||
grant.json
|
||||
actions.json
|
||||
email.json
|
||||
advanced.json
|
||||
|
||||
# Cruft
|
||||
.DS_Store
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"advanced": {
|
||||
"unique_email": true,
|
||||
"allow_register": true
|
||||
}
|
||||
}
|
||||
@ -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 don’t 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(() => {
|
||||
|
||||
@ -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.');
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
});
|
||||
|
||||
@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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...');
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
};
|
||||
|
||||
174
packages/strapi/lib/core/store.js
Normal file
174
packages/strapi/lib/core/store.js
Normal 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();
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user