Merge branch 'fix/deploy' of github.com:strapi/strapi into fix/deploy

This commit is contained in:
Aurelsicoko 2018-01-16 10:39:32 +01:00
commit 4edcff2627
29 changed files with 478 additions and 81 deletions

View File

@ -1,4 +1,4 @@
# Authentification
# Authentication
## Register a new user.
@ -53,7 +53,7 @@ $.ajax({
});
```
## Use your token to be identify as user.
## Use your token to be identified as a user.
By default, each API request is identified as `guest` role (see permissions of `guest`'s role in your admin dashboard). To make a request as a user, you have to set the `Authorization` token in your request headers. You receive a 401 error if you are not authorized to make this request or if your authorization header is not correct.

View File

@ -46,7 +46,7 @@ import LanguageProvider from 'containers/LanguageProvider';
import App from 'containers/App';
import { showNotification } from 'containers/NotificationProvider/actions';
import { pluginLoaded, updatePlugin, setHasUserPlugin } from 'containers/App/actions';
import { pluginLoaded, updatePlugin, unsetHasUserPlugin } from 'containers/App/actions';
import auth from 'utils/auth';
import configureStore from './store';
import { translationMessages, languages } from './i18n';
@ -111,7 +111,7 @@ if (window.location.port !== '4000') {
})
.then(plugins => {
if (findIndex(plugins, ['id', 'users-permissions']) === -1) {
store.dispatch(setHasUserPlugin());
store.dispatch(unsetHasUserPlugin());
}
(plugins || []).forEach(plugin => {
@ -146,7 +146,7 @@ if (window.location.port !== '4000') {
console.log(err);
});
} else if (findIndex(plugins, ['id', 'users-permissions']) === -1) {
store.dispatch(setHasUserPlugin());
store.dispatch(unsetHasUserPlugin());
}
// const isPluginAllowedToRegister = (plugin) => true;

View File

@ -9,7 +9,7 @@ import {
UPDATE_PLUGIN,
PLUGIN_LOADED,
PLUGIN_DELETED,
SET_HAS_USERS_PLUGIN,
UNSET_HAS_USERS_PLUGIN,
} from './constants';
export function loadPlugin(newPlugin) {
@ -42,8 +42,8 @@ export function pluginDeleted(plugin) {
};
}
export function setHasUserPlugin() {
export function unsetHasUserPlugin() {
return {
type: SET_HAS_USERS_PLUGIN,
type: UNSET_HAS_USERS_PLUGIN,
};
}

View File

@ -4,7 +4,7 @@
*
*/
export const SET_HAS_USERS_PLUGIN = 'app/App/SET_HAS_USERS_PLUGIN';
export const UNSET_HAS_USERS_PLUGIN = 'app/App/UNSET_HAS_USERS_PLUGIN';
export const LOAD_PLUGIN = 'app/App/LOAD_PLUGIN';
export const UPDATE_PLUGIN = 'app/App/UPDATE_PLUGIN';
export const PLUGIN_LOADED = 'app/App/PLUGIN_LOADED';

View File

@ -3,7 +3,7 @@ import {
UPDATE_PLUGIN,
PLUGIN_DELETED,
PLUGIN_LOADED,
SET_HAS_USERS_PLUGIN,
UNSET_HAS_USERS_PLUGIN,
} from './constants';
const initialState = fromJS({
@ -19,7 +19,7 @@ function appReducer(state = initialState, action) {
return state.setIn(['plugins', action.pluginId, action.updatedKey], fromJS(action.updatedValue));
case PLUGIN_DELETED:
return state.deleteIn(['plugins', action.plugin]);
case SET_HAS_USERS_PLUGIN:
case UNSET_HAS_USERS_PLUGIN:
return state.set('hasUserPlugin', false);
default:
return state;

View File

@ -457,7 +457,7 @@ module.exports = function(strapi) {
result.value = parseFloat(value);
break;
default:
result = undefined;
return undefined;
}
return result;
@ -622,7 +622,7 @@ module.exports = function(strapi) {
switch (association.nature) {
case 'oneToOne':
case 'oneToMany':
return this.manageRelations(model, params)
return this.manageRelations(model, params);
case 'manyToMany':
return Model.forge({
[Model.primaryKey]: parseFloat(params[Model.primaryKey])
@ -645,7 +645,7 @@ module.exports = function(strapi) {
switch (association.nature) {
case 'oneToOne':
case 'oneToMany':
return this.manageRelations(model, params)
return this.manageRelations(model, params);
case 'manyToMany':
return Model.forge({
[Model.primaryKey]: parseFloat(params[Model.primaryKey])

View File

@ -0,0 +1,31 @@
'use strict';
// Node.js core.
const execSync = require('child_process').execSync;
const path = require('path');
// Logger.
const logger = require('strapi-utils').logger;
module.exports = (scope, success, error) => {
const knex = require(path.resolve(`${scope.rootPath}/node_modules/knex`))({
client: scope.client.module,
connection: Object.assign(scope.database, {
user: scope.database.username
})
});
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}`);
logger.info('Copying the dashboard...');
success();
})
.catch(() => {
logger.warn('Database connection has failed! Make sure your database is running.');
error();
});
};

View File

@ -1,17 +0,0 @@
{
"defaultConnection": "default",
"connections": {
"default": {
"connector": "strapi-mongoose",
"settings": {
"client": "mongo",
"host": "localhost",
"port": 27017,
"database": "development",
"username": "",
"password": ""
},
"options": {}
}
}
}

View File

@ -0,0 +1,12 @@
'use strict';
module.exports = scope => {
// Finally, return the JSON.
return {
defaultConnection: 'default',
connections: {
default: scope.database
}
};
};

View File

@ -40,7 +40,8 @@ module.exports = scope => {
'dependencies': {
'lodash': '4.x.x',
'strapi': getDependencyVersion(cliPkg, 'strapi'),
'strapi-mongoose': getDependencyVersion(cliPkg, 'strapi')
[scope.client.connector]: getDependencyVersion(cliPkg, 'strapi'),
[scope.client.module]: scope.client.version
},
'author': {
'name': scope.author || 'A Strapi developer',

View File

@ -35,8 +35,8 @@ module.exports = (scope, cb) => {
const availableDependencies = [];
const dependencies = _.get(packageJSON, 'dependencies');
const strapiDependencies = Object.keys(dependencies).filter(key => key.indexOf('strapi') !== -1);
const othersDependencies = Object.keys(dependencies).filter(key => key.indexOf('strapi') === -1);
const strapiDependencies = Object.keys(dependencies).filter(key => key.indexOf('strapi') !== -1 && key.indexOf('strapi-bookshelf') === -1);
const othersDependencies = Object.keys(dependencies).filter(key => key.indexOf('strapi') === -1 || key.indexOf('strapi-bookshelf') !== -1);
// Verify if the dependencies are available into the global
_.forEach(strapiDependencies, (key) => {

View File

@ -6,10 +6,13 @@
// Node.js core.
const path = require('path');
const exec = require('child_process').exec;
const execSync = require('child_process').execSync;
// Public node modules.
const _ = require('lodash');
const fs = require('fs-extra');
const inquirer = require('inquirer');
// Logger.
const logger = require('strapi-utils').logger;
@ -45,8 +48,174 @@ module.exports = (scope, cb) => {
// ...
}
logger.info('Copying the dashboard...');
logger.info('Let\s configurate the connection to your database:');
// Trigger callback with no error to proceed.
return cb.success();
scope.database = {};
const connectionValidation = () => {
const databaseChoices = [
{
name: 'MongoDB (highly recommended)',
value: {
database: 'mongo',
connector: 'strapi-mongoose'
}
},
{
name: 'Postgres',
value: {
database: 'postgres',
connector: 'strapi-bookshelf',
module: 'pg'
}
},
{
name: 'MySQL',
value: {
database: 'mysql',
connector: 'strapi-bookshelf',
module: 'mysql'
}
},
{
name: 'Sqlite3',
value: {
database: 'sqlite3',
connector: 'strapi-bookshelf',
module: 'sqlite3'
}
},
{
name: 'Redis',
value: {
database: 'redis',
connector: 'strapi-redis'
}
}
];
inquirer
.prompt([
{
type: 'list',
prefix: '',
name: 'client',
message: 'Choose your main database:',
choices: databaseChoices,
default: () => {
if (scope.client) {
return _.findIndex(databaseChoices, { value: _.omit(scope.client, ['version'])});
}
}
}
])
.then(answers => {
scope.client = answers.client;
_.assign(scope.database, {
connector: answers.client.connector,
settings: {
client: answers.client.database
},
options: {}
});
const asyncFn = [
new Promise(resolve => {
inquirer
.prompt([
{
type: 'input',
prefix: '',
name: 'name',
message: 'Database name:',
default: _.get(scope.database, 'database', 'strapi')
},
{
type: 'input',
prefix: '',
name: 'host',
message: 'Host:',
default: _.get(scope.database, 'host', 'localhost')
},
{
type: 'input',
prefix: '',
name: 'port',
message: 'Port:',
default: (answers) => {
if (_.get(scope.database, 'port')) {
return scope.database.port;
}
const ports = {
mongo: 27017,
postgres: 5432,
mysql: 3306,
sqlite3: 1433,
redis: 6379
};
return ports[scope.client.database];
}
},
{
type: 'input',
prefix: '',
name: 'username',
message: 'Username:',
default: _.get(scope.database, 'username', undefined)
},
{
type: 'input',
prefix: '',
name: 'password',
message: 'Password:',
default: _.get(scope.database, 'password', undefined)
}
])
.then(answers => {
scope.database.settings.host = answers.host;
scope.database.settings.port = answers.port;
scope.database.settings.database = answers.name;
scope.database.settings.username = answers.username;
scope.database.settings.password = answers.password;
logger.info('Testing database connection...');
resolve();
});
}),
new Promise(resolve => {
let cmd = `npm install --prefix ${scope.rootPath} ${scope.client.connector}@alpha`;
if (scope.client.module) {
cmd += ` ${scope.client.module}`;
}
exec(cmd, () => {
if (scope.client.module) {
const lock = require(`${scope.rootPath}/node_modules/${scope.client.module}/package.json`);
scope.client.version = lock.version;
}
resolve();
});
})
];
Promise.all(asyncFn)
.then(() => {
try {
require(path.resolve(`${scope.rootPath}/node_modules/${scope.client.connector}/lib/utils/connectivity.js`))(scope, cb.success, connectionValidation);
} catch(err) {
execSync(`rm -r ${scope.rootPath}`);
logger.info('Copying the dashboard...');
cb.success();
}
});
});
};
connectionValidation();
};

View File

@ -9,6 +9,7 @@ const path = require('path');
// Local dependencies.
const packageJSON = require('../json/package.json.js');
const database = require('../json/database.json.js');
/**
* Copy required files for the generated application
@ -29,6 +30,10 @@ module.exports = {
jsonfile: packageJSON
},
'config/environments/development/database.json': {
jsonfile: database
},
// Copy dot files.
'.editorconfig': {
copy: 'editorconfig'

View File

@ -16,6 +16,7 @@
"enpeem": "^2.2.0",
"fs-extra": "^4.0.0",
"get-installed-path": "^3.0.1",
"inquirer": "^4.0.2",
"lodash": "^4.17.4",
"strapi-utils": "3.0.0-alpha.7.3",
"uuid": "^3.1.0"

View File

@ -18,11 +18,15 @@ function parseJSON(response) {
*
* @return {object|undefined} Returns either the response, or throws an error
*/
function checkStatus(response) {
function checkStatus(response, checkToken = true) {
if (response.status >= 200 && response.status < 300) {
return response;
}
if (response.status === 401 && auth.getToken() && checkToken) {
return checkTokenValidity(response);
}
return parseJSON(response).then(responseFormatted => {
const error = new Error(response.statusText);
error.response = response;
@ -31,6 +35,29 @@ function checkStatus(response) {
});
}
function checkTokenValidity(response) {
const options = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${auth.getToken()}`,
},
};
if (auth.getToken()) {
return fetch(`${strapi.backendURL}/user/me`, options)
.then(resp => {
if (response.status === 401) {
window.location = `${strapi.remoteURL}/plugins/users-permissions/auth/login`;
auth.clearAppStorage();
}
return checkStatus(response, false);
});
}
}
/**
* Format query params
*

View File

@ -0,0 +1,29 @@
'use strict';
// Node.js core.
const execSync = require('child_process').execSync;
const path = require('path');
// Logger.
const logger = require('strapi-utils').logger;
module.exports = (scope, success, error) => {
const Mongoose = require(path.resolve(`${scope.rootPath}/node_modules/mongoose`));
Mongoose.connect(`mongodb://${ (scope.database.username && scope.database.password) ? `${scope.database.username}:${scope.database.password}@` : '' }${scope.database.host}:${scope.database.port}/${scope.database.database}`, function (err) {
if (err) {
logger.warn('Database connection has failed! Make sure your database is running.');
return error();
}
logger.info('The app has been connected to the database successfully!');
Mongoose.connection.close();
execSync(`rm -r ${scope.rootPath}`);
logger.info('Copying the dashboard...');
success();
});
};

View File

@ -118,7 +118,7 @@ Plugin.propTypes = {
description: PropTypes.string,
information: PropTypes.shape({
logo: PropTypes.string.isRequired,
}).isRequired,
}),
}),
pluginSelected: PropTypes.string.isRequired,
};

View File

@ -27,5 +27,7 @@ module.exports = cb => {
}
}
strapi.plugins['users-permissions'].services.userspermissions.updatePermissions(cb);
strapi.plugins['users-permissions'].services.userspermissions.syncSchema(() => {
strapi.plugins['users-permissions'].services.userspermissions.updatePermissions(cb);
});
};

View File

@ -12,9 +12,6 @@ module.exports = {
email: {
className: 'col-md-6'
},
username: {
className: 'col-md-6'
},
provider: {
className: 'd-none'
},

View File

@ -144,6 +144,15 @@
"prefix": ""
}
},
{
"method": "GET",
"path": "/user/me",
"handler": "User.me",
"config": {
"policies": [],
"prefix": ""
}
},
{
"method": "GET",
"path": "/user/:_id",

View File

@ -28,7 +28,25 @@ module.exports = {
},
/**
}
* Retrieve authenticated user.
*
* @return {Object|Array}
*/
me: async (ctx) => {
const user = ctx.state.user;
if (!user) {
return ctx.badRequest(null, [{ messages: [{ id: 'No authorization header was found' }] }]);
}
const data = _.omit(user.toJSON ? user.toJSON() : user, ['password', 'resetPasswordToken']);
// Send 200 `ok`
ctx.send(data);
},
/**
* Retrieve a user record.
*
* @return {Object}

View File

@ -39,7 +39,9 @@ module.exports = {
issue: (payload) => {
return jwt.sign(
_.clone(payload.toJSON ? payload.toJSON() : payload),
process.env.JWT_SECRET || _.get(strapi.plugins['users-permissions'], 'config.jwtSecret') || 'oursecret'
process.env.JWT_SECRET || _.get(strapi.plugins['users-permissions'], 'config.jwtSecret') || 'oursecret', {
expiresIn: '30d'
}
);
},

View File

@ -169,9 +169,10 @@ module.exports = {
const isCallback = actionName === 'callback' && controllerName === 'auth' && pluginName === 'users-permissions' && roleId === '1';
const isRegister = actionName === 'register' && controllerName === 'auth' && pluginName === 'users-permissions' && roleId === '1';
const isPassword = actionName === 'forgotPassword' && controllerName === 'auth' && pluginName === 'users-permissions' && roleId === '1';
const isNewPassword = actionName === 'changePassword-password' && controllerName === 'auth' && pluginName === 'users-permissions' && roleId === '1';
const isNewPassword = actionName === 'changePassword' && controllerName === 'auth' && pluginName === 'users-permissions' && roleId === '1';
const isInit = actionName === 'init' && controllerName === 'userspermissions';
const enabled = isCallback || isRegister || roleId === '0' || isInit || isPassword || isNewPassword;
const isMe = actionName === 'me' && controllerName === 'user' && pluginName === 'users-permissions';
const enabled = isCallback || isRegister || roleId === '0' || isInit || isPassword || isNewPassword || isMe;
_.set(data, [roleId, 'permissions', pluginName, 'controllers', controllerName, actionName], { enabled, policy: '' })
}
@ -257,4 +258,91 @@ module.exports = {
strapi.log.error(err);
}
},
syncSchema: (cb) => {
const Model = strapi.plugins['users-permissions'].models.user;
if (Model.orm !== 'bookshelf') {
return cb();
}
const tableName = Model.collectionName;
new Promise((resolve, reject) => {
strapi.connections[Model.connection].schema.hasTable(tableName)
.then(exist => {
if (!exist) {
strapi.log.warn(`
TABLE \`${tableName}\` DOESN'T EXIST
1 EXECUTE THE FOLLOWING SQL QUERY
CREATE TABLE "${tableName}" (
id integer NOT NULL,
username text,
email text,
role text,
"resetPasswordToken" text,
password text,
updated_at timestamp with time zone,
created_at timestamp with time zone
);
2 RESTART YOUR SERVER
`);
strapi.stop();
}
resolve();
});
})
.then(() => {
const attributes = _.cloneDeep(Model.attributes);
attributes.id = {
type: 'integer'
};
attributes.updated_at = attributes.created_at = {
type: 'timestamp with time zone'
};
let commands = '';
const columnExist = (description, attribute) => {
return new Promise((resolve, reject) => {
strapi.connections[Model.connection].schema.hasColumn(tableName, attribute)
.then(exist => {
if (!exist) {
if (description.type === 'string') {
description.type = 'text';
}
commands += `\r\nALTER TABLE "${tableName}" ADD "${attribute}" ${description.type};`;
}
resolve();
});
});
};
const testsColumns = Object.entries(attributes).map(([attribute, description]) => columnExist(description, attribute));
Promise.all(testsColumns)
.then(() => {
if (!_.isEmpty(commands)) {
strapi.log.warn(`
TABLE \`${tableName}\` HAS MISSING COLUMNS
1 EXECUTE THE FOLLOWING SQL QUERIES
${commands}
2 RESTART YOUR SERVER
`);
strapi.stop();
}
cb();
});
});
}
};

View File

@ -0,0 +1,35 @@
'use strict';
// Node.js core.
const execSync = require('child_process').execSync;
const path = require('path');
// Logger.
const logger = require('strapi-utils').logger;
module.exports = (scope, success, error) => {
const Redis = require(`${scope.rootPath}/node_modules/ioredis`);
const redis = new Redis({
port: scope.database.port,
host: scope.database.host,
password: scope.database.password,
db: scope.database.database
});
redis.connect((err) => {
redis.disconnect();
if (err) {
logger.warn('Database connection has failed! Make sure your database is running.');
return error();
}
logger.info('The app has been connected to the database successfully!');
execSync(`rm -r ${scope.rootPath}`);
logger.info('Copying the dashboard...');
success();
});
};

View File

@ -16,12 +16,10 @@ module.exports = strapi => {
initialize: function(cb) {
strapi.app.use(
strapi.koaMiddlewares.convert(
strapi.koaMiddlewares.ip({
whiteList: strapi.config.middleware.settings.ip.whiteList,
blackList: strapi.config.middleware.settings.ip.blackList
})
)
strapi.koaMiddlewares.ip({
whiteList: strapi.config.middleware.settings.ip.whiteList,
blackList: strapi.config.middleware.settings.ip.blackList
})
);
cb();

View File

@ -22,20 +22,18 @@ module.exports = strapi => {
strapi.koaMiddlewares.locale(strapi.app);
strapi.app.use(
strapi.koaMiddlewares.convert(
strapi.koaMiddlewares.i18n(strapi.app, {
directory: path.resolve(
strapi.config.appPath,
strapi.config.paths.config,
'locales'
),
locales: Object.keys(get(strapi.config, 'locales', {})),
defaultLocale: strapi.config.middleware.settings.language.defaultLocale,
modes: strapi.config.middleware.settings.language.modes,
cookieName: strapi.config.middleware.settings.language.cookieName,
extension: '.json'
})
)
strapi.koaMiddlewares.i18n(strapi.app, {
directory: path.resolve(
strapi.config.appPath,
strapi.config.paths.config,
'locales'
),
locales: Object.keys(get(strapi.config, 'locales', {})),
defaultLocale: strapi.config.middleware.settings.language.defaultLocale,
modes: strapi.config.middleware.settings.language.modes,
cookieName: strapi.config.middleware.settings.language.cookieName,
extension: '.json'
})
);
cb();

View File

@ -11,11 +11,7 @@ module.exports = strapi => {
*/
initialize: function(cb) {
strapi.app.use(
strapi.koaMiddlewares.convert(
strapi.koaMiddlewares.body(strapi.config.middleware.settings.parser)
)
);
strapi.app.use(strapi.koaMiddlewares.body(strapi.config.middleware.settings.parser));
cb();
}

View File

@ -45,9 +45,7 @@ module.exports = strapi => {
);
strapi.app.use(
strapi.koaMiddlewares.convert(
strapi.koaMiddlewares.session(options, strapi.app)
)
strapi.koaMiddlewares.session(options, strapi.app)
);
strapi.app.use((ctx, next) => {
ctx.state = ctx.state || {};
@ -71,9 +69,7 @@ module.exports = strapi => {
);
strapi.app.use(
strapi.koaMiddlewares.convert(
strapi.koaMiddlewares.session(options, strapi.app)
)
strapi.koaMiddlewares.session(options, strapi.app)
);
strapi.app.use((ctx, next) => {
ctx.state = ctx.state || {};

View File

@ -37,17 +37,17 @@
"glob": "^7.1.2",
"kcors": "^2.2.0",
"koa": "^2.1.0",
"koa-body": "^2.3.0",
"koa-body": "^2.5.0",
"koa-compose": "^4.0.0",
"koa-compress": "^2.0.0",
"koa-convert": "^1.2.0",
"koa-favicon": "^2.0.0",
"koa-i18n": "^2.1.0",
"koa-ip": "^0.1.0",
"koa-ip": "^1.0.0",
"koa-locale": "~1.3.0",
"koa-lusca": "~2.2.0",
"koa-router-joi": "^1.0.1",
"koa-session": "^5.5.0",
"koa-session": "^5.5.1",
"koa-static": "^4.0.1",
"lodash": "^4.16.5",
"node-fetch": "^1.7.3",