Merge branch 'master' into fix/802

This commit is contained in:
Jim LAURIE 2018-04-27 16:40:57 +02:00 committed by GitHub
commit 49ced583f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 1525 additions and 1376 deletions

View File

@ -15,10 +15,10 @@ The most advanced open-source Content Management Framework to build powerful API
{% endcenter %} {% endcenter %}
## v3@alpha.11 is available! ## v3@alpha.12 is available!
We've been working on a major update for Strapi during the past months, rewriting the core framework and the dashboard. 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.11 ([v1 documentation is still available](http://strapi.io/documentation/1.x.x)). This documentation is only related to Strapi v3@alpha.12 ([v1 documentation is still available](http://strapi.io/documentation/1.x.x)).
**[Get Started](getting-started/installation.md)**<br /> **[Get Started](getting-started/installation.md)**<br />
Learn how to install Strapi and start developing your API. Learn how to install Strapi and start developing your API.

View File

@ -12,7 +12,7 @@ The entire logic of the admin panel is located in a single folder named `./admin
└─── admin └─── admin
| └─── build // Webpack generated build of the front-end | └─── build // Webpack generated build of the front-end
| └─── src // Front-end directory | └─── src // Front-end directory
| └─── app.js // Entry point of the Reacr application | └─── app.js // Entry point of the React application
| └─── assets // Assets directory containing images,... | └─── assets // Assets directory containing images,...
| └─── components // Admin's React components directory | └─── components // Admin's React components directory
| └─── containers // Admin's high level components directory | └─── containers // Admin's high level components directory

View File

@ -17,8 +17,10 @@ defineMessages(messages);
function LeftMenuFooter({ version }) { // eslint-disable-line react/prefer-stateless-function function LeftMenuFooter({ version }) { // eslint-disable-line react/prefer-stateless-function
return ( return (
<div className={styles.leftMenuFooter}> <div className={styles.leftMenuFooter}>
<div>
<FormattedMessage {...messages.poweredBy} /> <FormattedMessage {...messages.poweredBy} />
<a href="https://strapi.io" target="_blank">v{version}</a> <a href="https://strapi.io" target="_blank">v{version}</a>
</div>
<LocaleToggle /> <LocaleToggle />
</div> </div>
); );

View File

@ -4,6 +4,8 @@
.leftMenuFooter { /* stylelint-disable */ .leftMenuFooter { /* stylelint-disable */
position: absolute; position: absolute;
width: 100%; width: 100%;
display: flex;
justify-content: space-between;
bottom: 0; bottom: 0;
height: 3rem; height: 3rem;
padding-left: 15px; padding-left: 15px;

File diff suppressed because one or more lines are too long

View File

@ -4,6 +4,7 @@
* *
*/ */
import { import {
GET_CURR_ENV_SUCCEEDED,
GET_GA_STATUS, GET_GA_STATUS,
GET_GA_STATUS_SUCCEEDED, GET_GA_STATUS_SUCCEEDED,
GET_LAYOUT, GET_LAYOUT,
@ -11,6 +12,13 @@ import {
GET_STRAPI_VERSION_SUCCEEDED, GET_STRAPI_VERSION_SUCCEEDED,
} from './constants'; } from './constants';
export function getCurrEnvSucceeded(currentEnvironment) {
return {
type: GET_CURR_ENV_SUCCEEDED,
currentEnvironment,
};
}
export function getGaStatus() { export function getGaStatus() {
return { return {
type: GET_GA_STATUS, type: GET_GA_STATUS,

View File

@ -4,6 +4,7 @@
* *
*/ */
export const GET_CURR_ENV_SUCCEEDED = 'app/Admin/GET_CURR_ENV_SUCCEEDED';
export const GET_GA_STATUS = 'app/Admin/GET_GA_STATUS'; export const GET_GA_STATUS = 'app/Admin/GET_GA_STATUS';
export const GET_GA_STATUS_SUCCEEDED = 'app/Admin/GET_GA_STATUS_SUCCEEDED'; export const GET_GA_STATUS_SUCCEEDED = 'app/Admin/GET_GA_STATUS_SUCCEEDED';
export const GET_LAYOUT = 'app/Admin/GET_LAYOUT'; export const GET_LAYOUT = 'app/Admin/GET_LAYOUT';

View File

@ -59,6 +59,8 @@ import selectAdminPage from './selectors';
import styles from './styles.scss'; import styles from './styles.scss';
const PLUGINS_TO_BLOCK_PRODUCTION = ['content-type-builder', 'settings-manager'];
export class AdminPage extends React.Component { // eslint-disable-line react/prefer-stateless-function export class AdminPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
state = { hasAlreadyRegistereOtherPlugins: false }; state = { hasAlreadyRegistereOtherPlugins: false };
@ -136,6 +138,20 @@ export class AdminPage extends React.Component { // eslint-disable-line react/pr
showLeftMenu = () => !includes(this.props.location.pathname, 'users-permissions/auth/'); showLeftMenu = () => !includes(this.props.location.pathname, 'users-permissions/auth/');
retrievePlugins = () => {
const { adminPage: { currentEnvironment }, plugins } = this.props;
if (currentEnvironment === 'production') {
let pluginsToDisplay = plugins;
PLUGINS_TO_BLOCK_PRODUCTION.map(plugin =>
pluginsToDisplay = pluginsToDisplay.delete(plugin));
return pluginsToDisplay;
}
return plugins;
}
render() { render() {
const { adminPage } = this.props; const { adminPage } = this.props;
const header = this.showLeftMenu() ? <Header /> : ''; const header = this.showLeftMenu() ? <Header /> : '';
@ -145,7 +161,7 @@ export class AdminPage extends React.Component { // eslint-disable-line react/pr
<div className={styles.adminPage}> <div className={styles.adminPage}>
{this.showLeftMenu() && ( {this.showLeftMenu() && (
<LeftMenu <LeftMenu
plugins={this.props.plugins} plugins={this.retrievePlugins()}
layout={adminPage.layout} layout={adminPage.layout}
version={adminPage.strapiVersion} version={adminPage.strapiVersion}
/> />

View File

@ -7,6 +7,7 @@
import { fromJS, Map } from 'immutable'; import { fromJS, Map } from 'immutable';
import { import {
GET_CURR_ENV_SUCCEEDED,
GET_GA_STATUS_SUCCEEDED, GET_GA_STATUS_SUCCEEDED,
GET_LAYOUT_SUCCEEDED, GET_LAYOUT_SUCCEEDED,
GET_STRAPI_VERSION_SUCCEEDED, GET_STRAPI_VERSION_SUCCEEDED,
@ -14,12 +15,15 @@ import {
const initialState = fromJS({ const initialState = fromJS({
allowGa: true, allowGa: true,
currentEnvironment: 'development',
layout: Map({}), layout: Map({}),
strapiVersion: '3', strapiVersion: '3',
}); });
function adminPageReducer(state = initialState, action) { function adminPageReducer(state = initialState, action) {
switch (action.type) { switch (action.type) {
case GET_CURR_ENV_SUCCEEDED:
return state.update('currentEnvironment', () => action.currentEnvironment);
case GET_GA_STATUS_SUCCEEDED: case GET_GA_STATUS_SUCCEEDED:
return state.update('allowGa', () => action.allowGa); return state.update('allowGa', () => action.allowGa);
case GET_LAYOUT_SUCCEEDED: case GET_LAYOUT_SUCCEEDED:

View File

@ -2,6 +2,7 @@ import { fork, call, put, takeLatest } from 'redux-saga/effects';
import request from 'utils/request'; import request from 'utils/request';
import { import {
getCurrEnvSucceeded,
getGaStatusSucceeded, getGaStatusSucceeded,
getLayoutSucceeded, getLayoutSucceeded,
getStrapiVersionSucceeded, getStrapiVersionSucceeded,
@ -10,11 +11,13 @@ import { GET_GA_STATUS, GET_LAYOUT } from './constants';
function* getGaStatus() { function* getGaStatus() {
try { try {
const [allowGa, strapiVersion] = yield [ const [allowGa, strapiVersion, currentEnvironment] = yield [
call(request, '/admin/gaConfig', { method: 'GET' }), call(request, '/admin/gaConfig', { method: 'GET' }),
call(request, '/admin/strapiVersion', { method: 'GET' }), call(request, '/admin/strapiVersion', { method: 'GET' }),
call(request, '/admin/currentEnvironment', { method: 'GET' }),
]; ];
yield put(getCurrEnvSucceeded(currentEnvironment.currentEnvironment));
yield put(getGaStatusSucceeded(allowGa)); yield put(getGaStatusSucceeded(allowGa));
yield put(getStrapiVersionSucceeded(strapiVersion.strapiVersion)); yield put(getStrapiVersionSucceeded(strapiVersion.strapiVersion));
} catch(err) { } catch(err) {

View File

@ -10,17 +10,20 @@
"analyze:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf stats.json", "analyze:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf stats.json",
"preanalyze": "npm run analyze:clean", "preanalyze": "npm run analyze:clean",
"analyze": "node ./node_modules/strapi-helper-plugin/lib/internals/scripts/analyze.js", "analyze": "node ./node_modules/strapi-helper-plugin/lib/internals/scripts/analyze.js",
"prebuild": "APP_PATH=$APP_PATH node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production IS_ADMIN=true node ./node_modules/strapi-helper-plugin/lib/internals/scripts/loadAdminConfigurations.js",
"build:dev": "npm run build:dll && node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development IS_ADMIN=true node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config ./node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress", "build:dev": "npm run build:dll && node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development IS_ADMIN=true node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config ./node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress",
"build": "APP_PATH=$APP_PATH npm run build:dll && node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production IS_ADMIN=true node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config ./node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress", "build": "APP_PATH=$APP_PATH npm run build:dll && node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production IS_ADMIN=true node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config ./node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress",
"build:dll": "APP_PATH=$APP_PATH node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production IS_ADMIN=true node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config ./node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.dll.babel.js --color -p --progress", "build:dll": "APP_PATH=$APP_PATH node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production IS_ADMIN=true node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config ./node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.dll.babel.js --color -p --progress",
"build:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf admin/build", "build:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf admin/build",
"prestart": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development PORT=4000 IS_ADMIN=true node ./node_modules/strapi-helper-plugin/lib/internals/scripts/loadAdminConfigurations.js",
"start": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development PORT=4000 IS_ADMIN=true node ./node_modules/strapi-helper-plugin/lib/server", "start": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development PORT=4000 IS_ADMIN=true node ./node_modules/strapi-helper-plugin/lib/server",
"generate": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/plop --plopfile ./node_modules/strapi-helper-plugin/lib/internals/generators/index.js", "generate": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/plop --plopfile ./node_modules/strapi-helper-plugin/lib/internals/generators/index.js",
"lint": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/eslint --ignore-path ./admin/.gitignore --ignore-pattern build --config ./node_modules/strapi-helper-plugin/lib/internals/eslint/.eslintrc.json admin", "lint": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/eslint --ignore-path ./admin/.gitignore --ignore-pattern build --config ./node_modules/strapi-helper-plugin/lib/internals/eslint/.eslintrc.json admin",
"prettier": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/prettier --single-quote --trailing-comma es5 --write \"{admin,__{tests,mocks}__}/**/*.js\"", "prettier": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/prettier --single-quote --trailing-comma es5 --write \"{admin,__{tests,mocks}__}/**/*.js\"",
"test": "npm run lint", "test": "npm run lint",
"prepublishOnly": "npm run build", "prepublishOnly": "npm run build",
"setup": "node ./scripts/setup.js" "setup": "node ./scripts/setup.js",
"presetup": "node ./scripts/preSetup.js"
}, },
"dependencies": { "dependencies": {
"react-ga": "^2.4.1", "react-ga": "^2.4.1",

View File

@ -0,0 +1,43 @@
const shell = require('shelljs');
const path = require('path');
const _ = require('lodash');
shell.echo('');
shell.echo('🕓 The setup process can take few minutes.');
shell.echo('');
shell.echo('🔸 Administration Panel');
shell.echo('📦 Installing packages...');
const pwd = shell.pwd();
const silent = process.env.npm_config_debug !== 'true';
const isDevelopmentMode = path.resolve(pwd.stdout).indexOf('strapi-admin') !== -1;
const appPath = isDevelopmentMode ? path.resolve(process.env.PWD, '..') : path.resolve(pwd.stdout, '..');
// We just install the admin's dependencies here
// Remove package-lock.json.
shell.rm('-rf', path.resolve(appPath, 'package-lock.json'));
shell.rm('-rf', path.resolve(appPath, 'admin', 'package-lock.json'));
// Install the project dependencies.
shell.exec(`cd "${appPath}" && npm install --ignore-scripts`, {
silent
});
// Install the administration dependencies.
shell.exec(`cd "${path.resolve(appPath, 'admin')}" && npm install`, {
silent
});
if (isDevelopmentMode) {
shell.exec(`cd "${path.resolve(appPath, 'admin')}" && npm link strapi-helper-plugin && npm link strapi-utils`, {
silent
});
} else {
shell.exec(`cd "${path.resolve(appPath, 'admin', 'node_modules', 'strapi-helper-plugin')}" && npm install`, {
silent
});
}
shell.echo('Packaged installed successfully');

View File

@ -1,44 +1,15 @@
const fs = require('fs');
const shell = require('shelljs'); const shell = require('shelljs');
const path = require('path'); const path = require('path');
const _ = require('lodash'); const _ = require('lodash');
shell.echo('');
shell.echo('🕓 The setup process can take few minutes.');
shell.echo('');
shell.echo('🔸 Administration Panel');
shell.echo('📦 Installing packages...');
const pwd = shell.pwd(); const pwd = shell.pwd();
const silent = process.env.npm_config_debug !== 'true'; const silent = process.env.npm_config_debug !== 'true';
const isDevelopmentMode = path.resolve(pwd.stdout).indexOf('strapi-admin') !== -1; const isDevelopmentMode = path.resolve(pwd.stdout).indexOf('strapi-admin') !== -1;
const appPath = isDevelopmentMode ? path.resolve(process.env.PWD, '..') : path.resolve(pwd.stdout, '..'); const appPath = isDevelopmentMode ? path.resolve(process.env.PWD, '..') : path.resolve(pwd.stdout, '..');
// Remove package-lock.json. shell.echo('🏗 Building the admin...');
shell.rm('-rf', path.resolve(appPath, 'package-lock.json'));
shell.rm('-rf', path.resolve(appPath, 'admin', 'package-lock.json'));
// Install the project dependencies.
shell.exec(`cd "${appPath}" && npm install --ignore-scripts`, {
silent
});
// Install the administration dependencies.
shell.exec(`cd "${path.resolve(appPath, 'admin')}" && npm install`, {
silent
});
if (isDevelopmentMode) {
shell.exec(`cd "${path.resolve(appPath, 'admin')}" && npm link strapi-helper-plugin && npm link strapi-utils`, {
silent
});
} else {
shell.exec(`cd "${path.resolve(appPath, 'admin', 'node_modules', 'strapi-helper-plugin')}" && npm install`, {
silent
});
}
shell.echo('🏗 Building...');
const build = shell.exec(`cd "${path.resolve(appPath, 'admin')}" && APP_PATH="${appPath}" npm run build`, { const build = shell.exec(`cd "${path.resolve(appPath, 'admin')}" && APP_PATH="${appPath}" npm run build`, {
silent silent
@ -55,7 +26,21 @@ shell.echo('');
if (process.env.npm_config_plugins === 'true') { if (process.env.npm_config_plugins === 'true') {
const plugins = path.resolve(appPath, 'plugins'); const plugins = path.resolve(appPath, 'plugins');
shell.ls('* -d', plugins).forEach(function (plugin) { // TODO: build plugins in async
shell.ls('* -d', plugins)
.filter(x => {
let hasAdminFolder;
try {
fs.accessSync(path.resolve(appPath, 'plugins', x, 'admin', 'src', 'containers', 'App'));
hasAdminFolder = true;
} catch(err) {
hasAdminFolder = false;
}
return hasAdminFolder;
})
.forEach(function (plugin) {
shell.echo(`🔸 Plugin - ${_.upperFirst(plugin)}`); shell.echo(`🔸 Plugin - ${_.upperFirst(plugin)}`);
shell.echo('📦 Installing packages...'); shell.echo('📦 Installing packages...');
shell.exec(`cd "${path.resolve(plugins, plugin)}" && npm install`, { shell.exec(`cd "${path.resolve(plugins, plugin)}" && npm install`, {

View File

@ -40,9 +40,11 @@ module.exports = function(strapi) {
* Initialize the hook * Initialize the hook
*/ */
initialize: cb => { initialize: async cb => {
const connections = _.pickBy(strapi.config.connections, { connector: 'strapi-bookshelf' }); const connections = _.pickBy(strapi.config.connections, { connector: 'strapi-bookshelf' });
const databaseUpdate = [];
_.forEach(connections, (connection, connectionName) => { _.forEach(connections, (connection, connectionName) => {
// Apply defaults // Apply defaults
_.defaults(connection.settings, strapi.config.hook.settings.bookshelf); _.defaults(connection.settings, strapi.config.hook.settings.bookshelf);
@ -307,6 +309,233 @@ module.exports = function(strapi) {
// Push attributes to be aware of model schema. // Push attributes to be aware of model schema.
target[model]._attributes = definition.attributes; target[model]._attributes = definition.attributes;
databaseUpdate.push(new Promise(async (resolve) => {
// Equilize database tables
const handler = async (table, attributes) => {
const tableExist = await ORM.knex.schema.hasTable(table);
const getType = (attribute, name) => {
let type;
if (!attribute.type) {
// Add integer value if there is a relation
const relation = definition.associations.find((association) => {
return association.alias === name;
});
switch (relation.nature) {
case 'oneToOne':
case 'manyToOne':
type = definition.client === 'pg' ? 'integer' : 'int';
break;
default:
return null;
}
} else {
switch (attribute.type) {
case 'text':
case 'json':
type = 'text';
break;
case 'string':
case 'password':
case 'email':
type = 'varchar(255)';
break;
case 'integer':
case 'biginteger':
type = definition.client === 'pg' ? 'integer' : 'int';
break;
case 'float':
case 'decimal':
type = attribute.type;
break;
case 'date':
case 'time':
case 'datetime':
case 'timestamp':
type = definition.client === 'pg' ? 'timestamp with time zone' : 'timestamp DEFAULT CURRENT_TIMESTAMP';
break;
case 'timestampUpdate':
type = definition.client === 'pg' ? 'timestamp with time zone' : 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP';
break;
case 'boolean':
type = 'boolean';
break;
default:
}
}
return type;
};
// Apply field type of attributes definition
const generateColumns = (attrs, start) => {
return Object.keys(attrs).reduce((acc, attr) => {
const attribute = attributes[attr];
const type = getType(attribute, attr);
if (type) {
acc.push(`${quote}${attr}${quote} ${type}`);
}
return acc;
}, start);
};
if (!tableExist) {
const columns = generateColumns(attributes, [`id ${definition.client === 'pg' ? 'SERIAL' : 'INT AUTO_INCREMENT'} NOT NULL PRIMARY KEY`]).join(',\n\r');
// Create table
await ORM.knex.raw(`
CREATE TABLE ${quote}${table}${quote} (
${columns}
)
`);
} else {
const columns = Object.keys(attributes);
// Fetch existing column
const columnsExist = await Promise.all(columns.map(attribute =>
ORM.knex.schema.hasColumn(table, attribute)
));
const columnsToAdd = {};
// Get columns to add
columnsExist.forEach((columnExist, index) => {
const attribute = attributes[columns[index]];
if (!columnExist) {
columnsToAdd[columns[index]] = attribute;
}
});
// Generate and execute query to add missing column
if (Object.keys(columnsToAdd).length > 0) {
const columns = generateColumns(columnsToAdd, []);
const queries = columns.reduce((acc, attribute) => {
acc.push(`ALTER TABLE ${quote}${table}${quote} ADD ${attribute};`)
return acc;
}, []).join('\n\r');
await ORM.knex.raw(queries);
}
// Execute query to update column type
await Promise.all(columns.map(attribute =>
new Promise(async (resolve) => {
const type = getType(attributes[attribute], attribute);
if (type) {
const changeType = definition.client === 'pg'
? `ALTER COLUMN ${quote}${attribute}${quote} TYPE ${type} USING ${quote}${attribute}${quote}::${type}`
: `CHANGE ${quote}${attribute}${quote} ${quote}${attribute}${quote} ${type} `;
const changeRequired = definition.client === 'pg'
? `ALTER COLUMN ${quote}${attribute}${quote} ${attributes[attribute].required ? 'SET' : 'DROP'} NOT NULL`
: `CHANGE ${quote}${attribute}${quote} ${quote}${attribute}${quote} ${type} ${attributes[attribute].required ? 'NOT' : ''} NULL`;
await ORM.knex.raw(`ALTER TABLE ${quote}${table}${quote} ${changeType}`);
await ORM.knex.raw(`ALTER TABLE ${quote}${table}${quote} ${changeRequired}`);
}
resolve();
})
));
}
};
const quote = definition.client === 'pg' ? '"' : '`';
// Add created_at and updated_at field if timestamp option is true
if (loadedModel.hasTimestamps) {
definition.attributes['created_at'] = {
type: 'timestamp'
};
definition.attributes['updated_at'] = {
type: 'timestampUpdate'
};
}
// Equilize tables
await handler(loadedModel.tableName, definition.attributes);
// Equilize polymorphic releations
const morphRelations = definition.associations.find((association) => {
return association.nature.toLowerCase().includes('morphto');
});
if (morphRelations) {
const attributes = {
[`${loadedModel.tableName}_id`]: {
type: 'integer'
},
[`${morphRelations.alias}_id`]: {
type: 'integer'
},
[`${morphRelations.alias}_type`]: {
type: 'text'
},
[definition.attributes[morphRelations.alias].filter]: {
type: 'text'
}
};
await handler(`${loadedModel.tableName}_morph`, attributes);
}
// Equilize many to many releations
const manyRelations = definition.associations.find((association) => {
return association.nature === 'manyToMany';
});
if (manyRelations && manyRelations.dominant) {
const collection = manyRelations.plugin ?
strapi.plugins[manyRelations.plugin].models[manyRelations.collection]:
strapi.models[manyRelations.collection];
const attributes = {
[`${pluralize.singular(manyRelations.collection)}_id`]: {
type: 'integer'
},
[`${pluralize.singular(definition.globalId.toLowerCase())}_id`]: {
type: 'integer'
}
};
const table = _.get(manyRelations, 'collectionName') ||
_.map(
_.sortBy(
[
collection.attributes[
manyRelations.via
],
manyRelations
],
'collection'
),
table => {
return _.snakeCase(
pluralize.plural(table.collection) +
' ' +
pluralize.plural(table.via)
);
}
).join('__');
await handler(table, attributes);
}
// Remove from attributes (auto handled by bookshlef and not displayed on ctb)
if (loadedModel.hasTimestamps) {
delete definition.attributes['created_at'];
delete definition.attributes['updated_at'];
}
resolve();
}));
} catch (err) { } catch (err) {
strapi.log.error('Impossible to register the `' + model + '` model.'); strapi.log.error('Impossible to register the `' + model + '` model.');
strapi.log.error(err); strapi.log.error(err);
@ -602,6 +831,8 @@ module.exports = function(strapi) {
}); });
}); });
await Promise.all(databaseUpdate);
cb(); cb();
}, },

View File

@ -4,6 +4,9 @@
const execSync = require('child_process').execSync; const execSync = require('child_process').execSync;
const path = require('path'); const path = require('path');
// Public node modules
const inquirer = require('inquirer');
// Logger. // Logger.
const logger = require('strapi-utils').logger; const logger = require('strapi-utils').logger;
@ -17,15 +20,45 @@ module.exports = (scope, success, error) => {
knex.raw('select 1+1 as result').then(() => { knex.raw('select 1+1 as result').then(() => {
logger.info('The app has been connected to the database successfully'); logger.info('The app has been connected to the database successfully');
knex.raw(scope.client.database === 'postgres' ? "SELECT tablename FROM pg_tables WHERE schemaname='public'" : 'SELECT * FROM information_schema.tables').then((tables) => {
knex.destroy(); knex.destroy();
const next = () => {
execSync(`rm -r "${scope.tmpPath}"`); execSync(`rm -r "${scope.tmpPath}"`);
logger.info('Copying the dashboard...'); logger.info('Copying the dashboard...');
success(); success();
};
if (tables.rows.length !== 0) {
logger.warn('It seems that your database is not empty. Be aware that Strapi is going to automatically creates tables & columns, and might update columns which can corrupt data or cause data loss.');
inquirer.prompt([{
type: 'confirm',
prefix: '',
name: 'confirm',
message: `Are you sure you want to continue with the ${scope.database.settings.database} database:`,
}])
.then(({confirm}) => {
if (confirm) {
next();
} else {
error();
}
});
} else {
next();
}
});
}) })
.catch(() => { .catch((err) => {
logger.warn('Database connection has failed! Make sure your database is running.'); if (err.sql) {
logger.warn('Server connection has failed! Make sure your database server is running.');
} else {
logger.warn(`Database connection has failed! Make sure your "${scope.database.settings.database}" database exist.`);
}
error(); error();
}); });
}; };

View File

@ -17,6 +17,7 @@
"main": "./lib", "main": "./lib",
"dependencies": { "dependencies": {
"bookshelf": "^0.12.1", "bookshelf": "^0.12.1",
"inquirer": "^5.2.0",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"pluralize": "^6.0.0", "pluralize": "^6.0.0",
"strapi-knex": "3.0.0-alpha.12", "strapi-knex": "3.0.0-alpha.12",

View File

@ -10,11 +10,12 @@
"port": "${process.env.DATABASE_PORT || 27017}", "port": "${process.env.DATABASE_PORT || 27017}",
"database": "${process.env.DATABASE_NAME || 'strapi-production'}", "database": "${process.env.DATABASE_NAME || 'strapi-production'}",
"username": "${process.env.DATABASE_USERNAME || ''}", "username": "${process.env.DATABASE_USERNAME || ''}",
"password": "${process.env.DATABASE_PASSWORD || ''}", "password": "${process.env.DATABASE_PASSWORD || ''}"
},
"options": {
"ssl": "${process.env.DATABASE_SSL || false}", "ssl": "${process.env.DATABASE_SSL || false}",
"authenticationDatabase": "${process.env.DATABASE_AUTHENTICATION_DATABASE || ''}" "authenticationDatabase": "${process.env.DATABASE_AUTHENTICATION_DATABASE || ''}"
}, }
"options": {}
} }
} }
} }

View File

@ -10,11 +10,12 @@
"port": "${process.env.DATABASE_PORT || 27017}", "port": "${process.env.DATABASE_PORT || 27017}",
"database": "${process.env.DATABASE_NAME || 'strapi-staging'}", "database": "${process.env.DATABASE_NAME || 'strapi-staging'}",
"username": "${process.env.DATABASE_USERNAME || ''}", "username": "${process.env.DATABASE_USERNAME || ''}",
"password": "${process.env.DATABASE_PASSWORD || ''}", "password": "${process.env.DATABASE_PASSWORD || ''}"
},
"options": {
"ssl": "${process.env.DATABASE_SSL || false}", "ssl": "${process.env.DATABASE_SSL || false}",
"authenticationDatabase": "${process.env.DATABASE_AUTHENTICATION_DATABASE || ''}" "authenticationDatabase": "${process.env.DATABASE_AUTHENTICATION_DATABASE || ''}"
}, }
"options": {}
} }
} }
} }

View File

@ -83,21 +83,6 @@ module.exports = (scope, cb) => {
connector: 'strapi-bookshelf', connector: 'strapi-bookshelf',
module: 'mysql' module: 'mysql'
} }
},
{
name: 'Sqlite3',
value: {
database: 'sqlite3',
connector: 'strapi-bookshelf',
module: 'sqlite3'
}
},
{
name: 'Redis',
value: {
database: 'redis',
connector: 'strapi-redis'
}
} }
]; ];
@ -169,9 +154,7 @@ module.exports = (scope, cb) => {
const ports = { const ports = {
mongo: 27017, mongo: 27017,
postgres: 5432, postgres: 5432,
mysql: 3306, mysql: 3306
sqlite3: 1433,
redis: 6379
}; };
return ports[scope.client.database]; return ports[scope.client.database];
@ -212,7 +195,6 @@ module.exports = (scope, cb) => {
} }
]) ])
.then(answers => { .then(answers => {
if (hasDatabaseConfig) { if (hasDatabaseConfig) {
answers = _.omit(scope.database.settings, ['client']) answers = _.omit(scope.database.settings, ['client'])
} }
@ -222,8 +204,8 @@ module.exports = (scope, cb) => {
scope.database.settings.database = answers.database; scope.database.settings.database = answers.database;
scope.database.settings.username = answers.username; scope.database.settings.username = answers.username;
scope.database.settings.password = answers.password; scope.database.settings.password = answers.password;
scope.database.settings.authenticationDatabase = answers.authenticationDatabase; scope.database.options.authenticationDatabase = answers.authenticationDatabase;
scope.database.settings.ssl = answers.ssl; scope.database.options.ssl = _.toString(answers.ssl) === 'true';
logger.info('Testing database connection...'); logger.info('Testing database connection...');

View File

@ -0,0 +1,22 @@
const path = require('path');
const shell = require('shelljs');
const pwd = shell.pwd();
const isDevelopmentMode = path.resolve(pwd.stdout).indexOf('strapi-admin') !== -1;
const appPath = isDevelopmentMode ? path.resolve(process.env.PWD, '..') : path.resolve(pwd.stdout, '..');
const isSetup = path.resolve(process.env.PWD, '..', '..') === path.resolve(process.env.INIT_CWD);
// Load the app configurations only when :
// - starting the app in dev mode
// - building the admin from an existing app (`npm run setup` at the root of the project)
if (!isSetup) {
const strapi = require(path.join(appPath, 'node_modules', 'strapi'));
strapi.config.appPath = appPath;
strapi.log.level = 'silent';
(async () => {
await strapi.load({
environment: process.env.NODE_ENV,
});
})();
}

View File

@ -12,6 +12,8 @@ const pkg = require(path.resolve(process.cwd(), 'package.json'));
const pluginId = pkg.name.replace(/^strapi-/i, ''); const pluginId = pkg.name.replace(/^strapi-/i, '');
const isAdmin = process.env.IS_ADMIN === 'true'; const isAdmin = process.env.IS_ADMIN === 'true';
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const appPath = (() => { const appPath = (() => {
if (process.env.APP_PATH) { if (process.env.APP_PATH) {
return process.env.APP_PATH; return process.env.APP_PATH;
@ -28,25 +30,6 @@ const adminPath = (() => {
return path.resolve(process.env.PWD); return path.resolve(process.env.PWD);
})(); })();
if (!isSetup) {
try {
// Load app' configurations to update `plugins.json` automatically.
const strapi = require(path.join(appPath, 'node_modules', 'strapi'));
strapi.config.appPath = appPath;
strapi.log.level = 'silent';
(async () => {
await strapi.load({
environment: process.env.NODE_ENV,
});
})();
} catch (e) {
console.log(e);
throw new Error(`You need to start the WebPack server from the /admin or /plugins/**/admin directories in a Strapi's project.`);
}
}
// Define remote and backend URLs. // Define remote and backend URLs.
const URLs = { const URLs = {
host: '/admin', host: '/admin',
@ -100,7 +83,9 @@ if (process.env.npm_lifecycle_event === 'start') {
plugins.exist = true; plugins.exist = true;
} }
// Read `plugins` directory and check if the plugin comes with an UI. // Read `plugins` directory and check if the plugin comes with an UI (it has an App container).
// If we don't do this check webpack expects the plugin to have a containers/App/reducer.js to create
// the plugin's store (redux).
plugins.src = isAdmin && !plugins.exist ? fs.readdirSync(path.resolve(appPath, 'plugins')).filter(x => { plugins.src = isAdmin && !plugins.exist ? fs.readdirSync(path.resolve(appPath, 'plugins')).filter(x => {
let hasAdminFolder; let hasAdminFolder;
@ -121,16 +106,39 @@ if (process.env.npm_lifecycle_event === 'start') {
}, {}); }, {});
} }
module.exports = (options) => ({ // Tell webpack to use a loader only for those files
const foldersToInclude = [path.join(adminPath, 'admin', 'src')]
.concat(plugins.src.reduce((acc, current) => {
acc.push(path.resolve(appPath, 'plugins', current, 'admin', 'src'), plugins.folders[current]);
return acc;
}, []))
.concat([path.join(adminPath, 'node_modules', 'strapi-helper-plugin', 'lib', 'src')]);
module.exports = (options) => {
// The disable option is only for production
// Config from https://github.com/facebook/create-react-app/blob/next/packages/react-scripts/config/webpack.config.prod.js
const extractSass = new ExtractTextPlugin({
filename: '[name].[contenthash].css',
disable: options.disableExtractTextPlugin || true,
});
return {
entry: options.entry, entry: options.entry,
output: Object.assign({ // Compile into js/build.js output: Object.assign({ // Compile into js/build.js
path: path.join(adminPath, 'admin', 'build'), path: path.join(adminPath, 'admin', 'build'),
}, options.output), // Merge with env dependent settings }, options.output), // Merge with env dependent settings
module: { module: {
loaders: [{ rules: [ // TODO: add eslint formatter
{
// "oneOf" will traverse all following loaders until one will
// match the requirements. When no loader matches it will fall
// back to the "file" loader at the end of the loader list.
oneOf: [
{
test: /\.js$/, // Transform all .js files required somewhere with Babel, test: /\.js$/, // Transform all .js files required somewhere with Babel,
use: { loader: require.resolve('babel-loader'),
loader: 'babel-loader', include: foldersToInclude,
options: { options: {
presets: options.babelPresets, presets: options.babelPresets,
env: { env: {
@ -155,61 +163,82 @@ module.exports = (options) => ({
}, },
}, },
}, },
include: [path.join(adminPath, 'admin', 'src')] // The notation here is somewhat confusing.
.concat(plugins.src.reduce((acc, current) => { // "postcss" loader applies autoprefixer to our CSS.
acc.push(path.resolve(appPath, 'plugins', current, 'admin', 'src'), plugins.folders[current]); // "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader normally turns CSS into JS modules injecting <style>,
return acc; // but unlike in development configuration, we do something different.
}, [])) // `ExtractTextPlugin` first applies the "postcss" and "css" loaders
.concat([path.join(adminPath, 'node_modules', 'strapi-helper-plugin', 'lib', 'src')]), // (second argument), then grabs the result CSS and puts it into a
}, { // separate file in our build process. This way we actually ship
// Transform our own .scss files // a single CSS file in production instead of JS code injecting <style>
test: /\.scss$/, // tags. If you use code splitting, however, any async bundles will still
use: [{ // use the "style" loader inside the async code so CSS from them won't be
loader: 'style-loader', // in the main CSS file.
}, { {
loader: 'css-loader', test: /\.css$/,
include: /node_modules/,
use: extractSass.extract({
fallback: require.resolve('style-loader'),
use: [
{
loader: require.resolve('css-loader'),
options: { options: {
localIdentName: `${pluginId}[local]__[path][name]__[hash:base64:5]`,
modules: true,
importLoaders: 1,
sourceMap: true,
minimize: process.env.NODE_ENV === 'production', minimize: process.env.NODE_ENV === 'production',
sourceMap: false,
}, },
}, { },
loader: 'postcss-loader', {
loader: require.resolve('postcss-loader'),
options: { options: {
config: { config: {
path: path.resolve(__dirname, '..', 'postcss', 'postcss.config.js'), path: path.resolve(__dirname, '..', 'postcss', 'postcss.config.js'),
}, },
}, },
}, {
loader: 'sass-loader',
}],
}, {
// Do not transform vendor's CSS with CSS-modules
// The point is that they remain in global scope.
// Since we require these CSS files in our JS or CSS files,
// they will be a part of our compilation either way.
// So, no need for ExtractTextPlugin here.
test: /\.css$/,
include: /node_modules/,
loaders: ['style-loader', {
loader: 'css-loader',
options: {
minimize: process.env.NODE_ENV === 'production',
sourceMap: true,
}, },
}], ],
}, { }),
test: /\.(eot|svg|ttf|woff|woff2)$/, },
loader: 'file-loader', {
}, { test: /\.scss$/,
include: foldersToInclude,
use: extractSass.extract({
use: [
{
loader: require.resolve('css-loader'),
options: {
localIdentName: `${pluginId}[local]__[path][name]__[hash:base64:5]`,
modules: true,
importLoaders: 1,
sourceMap: false,
minimize: process.env.NODE_ENV === 'production',
},
},
{
loader: require.resolve('postcss-loader'),
options: {
config: {
path: path.resolve(__dirname, '..', 'postcss', 'postcss.config.js'),
},
},
},
{
loader: 'sass-loader',
},
],
fallback: require.resolve('style-loader'),
}),
},
{
test: /\.(eot|svg|otf|ttf|woff|woff2)$/,
use: 'file-loader',
},
{
test: /\.(jpg|png|gif)$/, test: /\.(jpg|png|gif)$/,
loaders: [ loaders: [
'file-loader', require.resolve('file-loader'),
{ {
loader: 'image-webpack-loader', loader: require.resolve('image-webpack-loader'),
query: { query: {
mozjpeg: { mozjpeg: {
progressive: true, progressive: true,
@ -227,13 +256,22 @@ module.exports = (options) => ({
}, },
}, },
], ],
}, { },
{
test: /\.html$/, test: /\.html$/,
loader: 'html-loader', include: [path.join(adminPath, 'admin', 'src')],
}, { use: require.resolve('html-loader'),
},
{
test: /\.(mp4|webm)$/, test: /\.(mp4|webm)$/,
loader: 'url-loader?limit=10000', loader: require.resolve('url-loader'),
}], options: {
limit: 10000,
},
},
],
},
],
}, },
plugins: [ plugins: [
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
@ -253,6 +291,7 @@ module.exports = (options) => ({
}, },
}), }),
new webpack.NamedModulesPlugin(), new webpack.NamedModulesPlugin(),
extractSass,
].concat(options.plugins), ].concat(options.plugins),
resolve: { resolve: {
modules: [ modules: [
@ -283,4 +322,5 @@ module.exports = (options) => ({
}, },
devtool: options.devtool, devtool: options.devtool,
target: 'web', // Make web variables accessible to webpack, e.g. window, target: 'web', // Make web variables accessible to webpack, e.g. window,
}); };
};

View File

@ -49,6 +49,7 @@ if (process.env.npm_lifecycle_event === 'start') {
plugins.src = process.env.IS_ADMIN === 'true' && !plugins.exist ? fs.readdirSync(path.resolve(appPath, 'plugins')).filter(x => { plugins.src = process.env.IS_ADMIN === 'true' && !plugins.exist ? fs.readdirSync(path.resolve(appPath, 'plugins')).filter(x => {
let hasAdminFolder; let hasAdminFolder;
// Don't inject the plugins that don't have an admin into the app
try { try {
fs.accessSync(path.resolve(appPath, 'plugins', x, 'admin', 'src', 'containers', 'App')); fs.accessSync(path.resolve(appPath, 'plugins', x, 'admin', 'src', 'containers', 'App'));
hasAdminFolder = true; hasAdminFolder = true;

View File

@ -3,7 +3,6 @@ const path = require('path');
const _ = require('lodash'); const _ = require('lodash');
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const cssnext = require('postcss-cssnext'); const cssnext = require('postcss-cssnext');
const postcssFocus = require('postcss-focus'); const postcssFocus = require('postcss-focus');
const postcssReporter = require('postcss-reporter'); const postcssReporter = require('postcss-reporter');
@ -14,10 +13,6 @@ const CopyWebpackPlugin = require('copy-webpack-plugin');
const base = require('./webpack.base.babel'); const base = require('./webpack.base.babel');
// const pkg = require(path.resolve(process.cwd(), 'package.json'));
// const pluginId = pkg.name.replace(/^strapi-plugin-/i, '');
// const dllPlugin = pkg.dllPlugin;
const isAdmin = process.env.IS_ADMIN === 'true'; const isAdmin = process.env.IS_ADMIN === 'true';
const isSetup = path.resolve(process.env.PWD, '..', '..') === path.resolve(process.env.INIT_CWD); const isSetup = path.resolve(process.env.PWD, '..', '..') === path.resolve(process.env.INIT_CWD);
const appPath = (() => { const appPath = (() => {
@ -105,7 +100,6 @@ if (isAdmin) {
chunks: ['main'], chunks: ['main'],
inject: true, inject: true,
})); }));
plugins.push(new ExtractTextPlugin('[name].[contenthash].css'));
plugins.push(new AddAssetHtmlPlugin({ plugins.push(new AddAssetHtmlPlugin({
filepath: path.resolve(__dirname, 'dist/*.dll.js'), filepath: path.resolve(__dirname, 'dist/*.dll.js'),
})); }));
@ -180,4 +174,5 @@ module.exports = base({
}, },
devtool: 'cheap-module-source-map', devtool: 'cheap-module-source-map',
disableExtractTextPlugin: false,
}); });

View File

@ -7,7 +7,7 @@
import React from 'react'; import React from 'react';
import moment from 'moment'; import moment from 'moment';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
// import DateTimeStyle from 'react-datetime/css/react-datetime.css'; import DateTimeStyle from 'react-datetime/css/react-datetime.css'; /* eslint-disable-line no-unused-vars */
import DateTime from 'react-datetime'; import DateTime from 'react-datetime';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { isEmpty, isObject } from 'lodash'; import { isEmpty, isObject } from 'lodash';

View File

@ -590,22 +590,40 @@ class Wysiwyg extends React.Component {
'X-Forwarded-Host': 'strapi', 'X-Forwarded-Host': 'strapi',
}; };
let newEditorState = this.getEditorState();
const nextBlocks = getNextBlocksList(newEditorState, this.getSelection().getStartKey());
// Loop to update each block after the inserted li
nextBlocks.map((block, index) => {
// Update the current block
const nextBlockText = index === 0 ? `![Uploading ${files[0].name}]()` : nextBlocks.get(index - 1).getText();
const newBlock = createNewBlock(nextBlockText, 'unstyled', block.getKey());
// Update the contentState
const newContentState = this.createNewContentStateFromBlock( const newContentState = this.createNewContentStateFromBlock(
createNewBlock(`![Uploading ${files[0].name}]()`), newBlock,
newEditorState.getCurrentContent(),
); );
const newEditorState = EditorState.push(this.getEditorState(), newContentState); newEditorState = EditorState.push(newEditorState, newContentState);
this.setState({ editorState: newEditorState }); });
const offset = `![Uploading ${files[0].name}]()`.length;
const updatedSelection = updateSelection(this.getSelection(), nextBlocks, offset);
this.setState({ editorState: EditorState.acceptSelection(newEditorState, updatedSelection) });
return request('/upload', { method: 'POST', headers, body: formData }, false, false) return request('/upload', { method: 'POST', headers, body: formData }, false, false)
.then(response => { .then(response => {
const lastBlock = this.getEditorState() const nextBlockKey = newEditorState
.getCurrentContent() .getCurrentContent()
.getLastBlock(); .getKeyAfter(newEditorState.getSelection().getStartKey());
const content = `![text](${response[0].url})`;
const newContentState = this.createNewContentStateFromBlock( const newContentState = this.createNewContentStateFromBlock(
createNewBlock(`![text](${response[0].url})`, 'unstyled', lastBlock.getKey()), createNewBlock(content, 'unstyled', nextBlockKey),
); );
const newEditorState = EditorState.push(this.getEditorState(), newContentState);
this.setState({ editorState: EditorState.moveFocusToEnd(newEditorState) }); newEditorState = EditorState.push(newEditorState, newContentState);
const updatedSelection = updateSelection(this.getSelection(), nextBlocks, 2);
this.setState({ editorState: EditorState.acceptSelection(newEditorState, updatedSelection) });
this.sendData(newEditorState); this.sendData(newEditorState);
}) })
.catch(() => { .catch(() => {

View File

@ -43,19 +43,24 @@ module.exports = function (strapi) {
initialize: cb => { initialize: cb => {
_.forEach(_.pickBy(strapi.config.connections, {connector: 'strapi-mongoose'}), (connection, connectionName) => { _.forEach(_.pickBy(strapi.config.connections, {connector: 'strapi-mongoose'}), (connection, connectionName) => {
const instance = new Mongoose(); const instance = new Mongoose();
const { uri, host, port, username, password, database, authenticationDatabase, ssl } = _.defaults(connection.settings, strapi.config.hook.settings.mongoose); const { uri, host, port, username, password, database } = _.defaults(connection.settings, strapi.config.hook.settings.mongoose);
const { authenticationDatabase, ssl } = _.defaults(connection.options, strapi.config.hook.settings.mongoose);
// Connect to mongo database // Connect to mongo database
const connectOptions = {} const connectOptions = {}
if (!_.isEmpty(username)) { if (!_.isEmpty(username)) {
connectOptions.user = username connectOptions.user = username;
if (!_.isEmpty(password)) { if (!_.isEmpty(password)) {
connectOptions.pass = password connectOptions.pass = password;
} }
} }
if (!_.isEmpty(authenticationDatabase)) { if (!_.isEmpty(authenticationDatabase)) {
connectOptions.authSource = authenticationDatabase; connectOptions.authSource = authenticationDatabase;
} }
connectOptions.ssl = ssl === true || ssl === 'true'; connectOptions.ssl = ssl === true || ssl === 'true';
instance.connect(uri || `mongodb://${host}:${port}/${database}`, connectOptions); instance.connect(uri || `mongodb://${host}:${port}/${database}`, connectOptions);

View File

@ -10,18 +10,27 @@ const logger = require('strapi-utils').logger;
module.exports = (scope, success, error) => { module.exports = (scope, success, error) => {
const Mongoose = require(path.resolve(`${scope.tmpPath}/node_modules/mongoose`)); const Mongoose = require(path.resolve(`${scope.tmpPath}/node_modules/mongoose`));
const { username, password, authenticationDatabase, ssl } = scope.database.settings const { username, password } = scope.database.settings;
const connectOptions = {} const { authenticationDatabase, ssl } = scope.database.options;
const connectOptions = {};
if (username) { if (username) {
connectOptions.user = username connectOptions.user = username;
if (password) { if (password) {
connectOptions.pass = password connectOptions.pass = password;
} }
} }
if (authenticationDatabase) { if (authenticationDatabase) {
connectOptions.authSource = authenticationDatabase; connectOptions.authSource = authenticationDatabase;
} }
connectOptions.ssl = ssl ? true : false
connectOptions.ssl = ssl ? true : false;
console.log(connectOptions);
Mongoose.connect(`mongodb://${scope.database.settings.host}:${scope.database.settings.port}/${scope.database.settings.database}`, connectOptions, function (err) { Mongoose.connect(`mongodb://${scope.database.settings.host}:${scope.database.settings.port}/${scope.database.settings.database}`, connectOptions, function (err) {
if (err) { if (err) {
logger.warn('Database connection has failed! Make sure your database is running.'); logger.warn('Database connection has failed! Make sure your database is running.');

View File

@ -16,7 +16,7 @@
"main": "./lib", "main": "./lib",
"dependencies": { "dependencies": {
"lodash": "^4.17.4", "lodash": "^4.17.4",
"mongoose": "^5.0.15", "mongoose": "^5.0.16",
"mongoose-float": "^1.0.2", "mongoose-float": "^1.0.2",
"pluralize": "^6.0.0", "pluralize": "^6.0.0",
"strapi-utils": "3.0.0-alpha.12" "strapi-utils": "3.0.0-alpha.12"

View File

@ -78,7 +78,7 @@ module.exports = {
return acc; return acc;
}, {}); }, {});
const entry = await this const request = await this
.forge(values) .forge(values)
.save() .save()
.catch((err) => { .catch((err) => {
@ -90,11 +90,13 @@ module.exports = {
throw err; throw err;
}); });
const entry = request.toJSON ? request.toJSON() : request;
return module.exports.update.call(this, { return module.exports.update.call(this, {
[this.primaryKey]: entry[this.primaryKey], [this.primaryKey]: entry[this.primaryKey],
values: _.merge({ values: _.assign({
id: entry[this.primaryKey] id: entry[this.primaryKey]
}, params.values) }, params.values, entry)
}); });
}, },
@ -167,9 +169,7 @@ module.exports = {
case 'oneToMany': case 'oneToMany':
case 'manyToOne': case 'manyToOne':
case 'manyToMany': case 'manyToMany':
if (association.nature === 'manyToMany' && details.dominant === true) { if (response[current] && _.isArray(response[current]) && current !== 'id') {
acc[current] = params.values[current];
} else if (response[current] && _.isArray(response[current]) && current !== 'id') {
// Records to add in the relation. // Records to add in the relation.
const toAdd = _.differenceWith(params.values[current], response[current], (a, b) => const toAdd = _.differenceWith(params.values[current], response[current], (a, b) =>
a[this.primaryKey].toString() === b[this.primaryKey].toString() a[this.primaryKey].toString() === b[this.primaryKey].toString()

View File

@ -35,7 +35,7 @@ module.exports = {
return acc; return acc;
}, {}); }, {});
const entry = await this.create(values) const request = await this.create(values)
.catch((err) => { .catch((err) => {
const message = err.message.split('index:'); const message = err.message.split('index:');
const field = _.words(_.last(message).split('_')[0]); const field = _.words(_.last(message).split('_')[0]);
@ -44,11 +44,13 @@ module.exports = {
throw error; throw error;
}); });
const entry = request.toJSON ? request.toJSON() : request;
return module.exports.update.call(this, { return module.exports.update.call(this, {
[this.primaryKey]: entry[this.primaryKey], [this.primaryKey]: entry[this.primaryKey],
values: _.merge({ values: _.assign({
id: entry[this.primaryKey] id: entry[this.primaryKey]
}, params.values) }, params.values, entry)
}); });
}, },

View File

@ -81,7 +81,9 @@ function formReducer(state = initialState, action) {
case RESET_FORM_ERRORS: case RESET_FORM_ERRORS:
return state.set('formErrors', List()); return state.set('formErrors', List());
case RESET_IS_FORM_SET: case RESET_IS_FORM_SET:
return state.set('isFormSet', false); return state
.set('isFormSet', false)
.update('modifiedData', () => Map({}));
case SET_ATTRIBUTE_FORM: { case SET_ATTRIBUTE_FORM: {
if (state.get('isFormSet')) { if (state.get('isFormSet')) {
return state return state

View File

@ -4,7 +4,8 @@ export default function checkAttributeValidations(errors) {
const attributeIndex = split(this.props.hash, '::')[3]; const attributeIndex = split(this.props.hash, '::')[3];
const sameAttributes = filter(this.props.contentTypeData.attributes, (attr) => attr.name === this.props.modifiedDataAttribute.name); const sameAttributes = filter(this.props.contentTypeData.attributes, (attr) => attr.name === this.props.modifiedDataAttribute.name);
const sameParamsKey = filter(this.props.contentTypeData.attributes, (attr) => attr.params.key === this.props.modifiedDataAttribute.params.key); const sameParamsKey = filter(this.props.contentTypeData.attributes, (attr) =>
attr.params.key === this.props.modifiedDataAttribute.params.key && attr.params.target === this.props.modifiedDataAttribute.params.target);
const sameParamsKeyAndName = filter(this.props.contentTypeData.attributes, (attr) => attr.name === this.props.modifiedDataAttribute.params.key); const sameParamsKeyAndName = filter(this.props.contentTypeData.attributes, (attr) => attr.name === this.props.modifiedDataAttribute.params.key);
const formErrors = concat(errors, hasNestedValue(this.props.modifiedDataAttribute)); const formErrors = concat(errors, hasNestedValue(this.props.modifiedDataAttribute));
const isEditingParamsKey = this.props.modifiedDataAttribute.params.key !== get(this.props.contentTypeData.attributes, [attributeIndex, 'params', 'key']); const isEditingParamsKey = this.props.modifiedDataAttribute.params.key !== get(this.props.contentTypeData.attributes, [attributeIndex, 'params', 'key']);

View File

@ -115,13 +115,13 @@ module.exports = {
try { try {
const modelJSON = _.cloneDeep(require(modelFilePath)); const modelJSON = _.cloneDeep(require(modelFilePath));
modelJSON.attributes = formatedAttributes; modelJSON.connection = connection;
modelJSON.collectionName = collectionName;
modelJSON.info = { modelJSON.info = {
name, name,
description description
}; };
modelJSON.connection = connection; modelJSON.attributes = formatedAttributes;
modelJSON.collectionName = collectionName;
const clearRelationsErrors = Service.clearRelations(model, plugin); const clearRelationsErrors = Service.clearRelations(model, plugin);

View File

@ -181,7 +181,7 @@ module.exports = {
_.forEach(attributesConfigurable, attribute => { _.forEach(attributesConfigurable, attribute => {
if (_.has(attribute, 'params.type')) { if (_.has(attribute, 'params.type')) {
attrs[attribute.name] = attribute.params; attrs[attribute.name] = _.omit(attribute.params, 'multiple');
if (attribute.params.type === 'media') { if (attribute.params.type === 'media') {
const via = _.findKey(strapi.plugins.upload.models.file.attributes, {collection: '*'}); const via = _.findKey(strapi.plugins.upload.models.file.attributes, {collection: '*'});

View File

@ -34,7 +34,6 @@
"form.database.item.provider.mongo": "Mongo", "form.database.item.provider.mongo": "Mongo",
"form.database.item.provider.postgres": "PostgreSQL", "form.database.item.provider.postgres": "PostgreSQL",
"form.database.item.provider.mysql": "MySQL", "form.database.item.provider.mysql": "MySQL",
"form.database.item.provider.sqlite3": "SQlite3",
"form.database.item.provider.redis": "Redis", "form.database.item.provider.redis": "Redis",
"form.application.name": "Anwendung", "form.application.name": "Anwendung",

View File

@ -34,7 +34,6 @@
"form.database.item.provider.mongo": "Mongo", "form.database.item.provider.mongo": "Mongo",
"form.database.item.provider.postgres": "PostgresSQL", "form.database.item.provider.postgres": "PostgresSQL",
"form.database.item.provider.mysql": "MySQL", "form.database.item.provider.mysql": "MySQL",
"form.database.item.provider.sqlite3": "SQlite3",
"form.database.item.provider.redis": "Redis", "form.database.item.provider.redis": "Redis",
"form.application.name": "Application", "form.application.name": "Application",

View File

@ -34,7 +34,6 @@
"form.database.item.provider.mongo": "Mongo", "form.database.item.provider.mongo": "Mongo",
"form.database.item.provider.postgres": "PostgresSQL", "form.database.item.provider.postgres": "PostgresSQL",
"form.database.item.provider.mysql": "MySQL", "form.database.item.provider.mysql": "MySQL",
"form.database.item.provider.sqlite3": "SQlite3",
"form.database.item.provider.redis": "Redis", "form.database.item.provider.redis": "Redis",
"form.application.name": "Application", "form.application.name": "Application",

View File

@ -34,7 +34,6 @@
"form.database.item.provider.mongo": "Mongo", "form.database.item.provider.mongo": "Mongo",
"form.database.item.provider.postgres": "PostgresSQL", "form.database.item.provider.postgres": "PostgresSQL",
"form.database.item.provider.mysql": "MySQL", "form.database.item.provider.mysql": "MySQL",
"form.database.item.provider.sqlite3": "SQlite3",
"form.database.item.provider.redis": "Redis", "form.database.item.provider.redis": "Redis",
"form.application.name": "Aplikacja", "form.application.name": "Aplikacja",

View File

@ -34,7 +34,6 @@
"form.database.item.provider.mongo": "Mongo", "form.database.item.provider.mongo": "Mongo",
"form.database.item.provider.postgres": "PostgresSQL", "form.database.item.provider.postgres": "PostgresSQL",
"form.database.item.provider.mysql": "MySQL", "form.database.item.provider.mysql": "MySQL",
"form.database.item.provider.sqlite3": "SQlite3",
"form.database.item.provider.redis": "Redis", "form.database.item.provider.redis": "Redis",
"form.application.name": "Приложение", "form.application.name": "Приложение",
@ -652,4 +651,4 @@
"language.zu_ZA": "isiZulu (iNingizimu Afrika)", "language.zu_ZA": "isiZulu (iNingizimu Afrika)",
"pageNotFound": "Cтраница не найдена" "pageNotFound": "Cтраница не найдена"
} }

View File

@ -34,7 +34,6 @@
"form.database.item.provider.mongo": "Mongo", "form.database.item.provider.mongo": "Mongo",
"form.database.item.provider.postgres": "PostgresSQL", "form.database.item.provider.postgres": "PostgresSQL",
"form.database.item.provider.mysql": "MySQL", "form.database.item.provider.mysql": "MySQL",
"form.database.item.provider.sqlite3": "SQlite3",
"form.database.item.provider.redis": "Redis", "form.database.item.provider.redis": "Redis",
"form.application.name": "Uygulama", "form.application.name": "Uygulama",

View File

@ -34,7 +34,6 @@
"form.database.item.provider.mongo": "Mongo", "form.database.item.provider.mongo": "Mongo",
"form.database.item.provider.postgres": "PostgresSQL", "form.database.item.provider.postgres": "PostgresSQL",
"form.database.item.provider.mysql": "MySQL", "form.database.item.provider.mysql": "MySQL",
"form.database.item.provider.sqlite3": "SQlite3",
"form.database.item.provider.redis": "Redis", "form.database.item.provider.redis": "Redis",
"form.application.name": "應用程式", "form.application.name": "應用程式",

View File

@ -137,6 +137,7 @@ module.exports = {
fs.unlinkSync(filePath); fs.unlinkSync(filePath);
ctx.send({ ok: true }); ctx.send({ ok: true });
strapi.reload();
} catch (e) { } catch (e) {
ctx.badRequest(null, Service.formatErrors([{ ctx.badRequest(null, Service.formatErrors([{
target: 'name', target: 'name',

View File

@ -520,11 +520,6 @@ module.exports = {
value: 'mysql', value: 'mysql',
port: 3306 port: 3306
}, },
{
name: 'form.database.item.provider.sqlite3',
value: 'sqlite3',
port: 1433
},
{ {
name: 'form.database.item.provider.redis', name: 'form.database.item.provider.redis',
value: 'redis', value: 'redis',
@ -636,7 +631,7 @@ module.exports = {
}, },
getClientConnector: client => { getClientConnector: client => {
const bookshelfClients = ['postgres', 'mysql', 'sqlite3']; const bookshelfClients = ['postgres', 'mysql'];
const mongooseClients = ['mongo']; const mongooseClients = ['mongo'];
const redisClients = ['redis']; const redisClients = ['redis'];
@ -662,9 +657,6 @@ module.exports = {
case 'mongo': case 'mongo':
return '#43b121'; return '#43b121';
break; break;
case 'sqlite3':
return '#006fff';
break;
default: default:
return '#000000'; return '#000000';
} }
@ -885,8 +877,7 @@ module.exports = {
installDependency: (params, name) => { installDependency: (params, name) => {
const clientsDependencies = { const clientsDependencies = {
postgres: 'pg', postgres: 'pg',
mysql: 'mysql', mysql: 'mysql'
sqlite3: 'sqlite3'
}; };
const client = _.get(clientsDependencies, _.get(params, `database.connections.${name}.settings.client`)); const client = _.get(clientsDependencies, _.get(params, `database.connections.${name}.settings.client`));

View File

@ -13,48 +13,6 @@ const _ = require('lodash');
const fs = require('fs'); const fs = require('fs');
module.exports = async cb => { module.exports = async cb => {
const Model = strapi.plugins.upload.models.file;
if (Model.orm === 'bookshelf') {
const hasTable = await strapi.connections[Model.connection].schema.hasTable(Model.tableName || Model.collectionName);
if (!hasTable) {
const quote = Model.client === 'pg' ? '"' : '`';
strapi.log.warn(`
TABLE \`upload_file\` DOESN'T EXIST
TABLE \`upload_file_morph\` DOESN'T EXIST
CREATE TABLE ${quote}${Model.tableName || Model.collectionName}${quote} (
id ${Model.client === 'pg' ? 'SERIAL' : 'INT AUTO_INCREMENT'} NOT NULL PRIMARY KEY,
name text,
hash text,
ext text,
mime text,
size text,
url text,
provider text,
updated_at ${Model.client === 'pg' ? 'timestamp with time zone' : 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'},
created_at ${Model.client === 'pg' ? 'timestamp with time zone' : 'timestamp DEFAULT CURRENT_TIMESTAMP'}
);
CREATE TABLE ${quote}upload_file_morph${quote} (
id ${Model.client === 'pg' ? 'SERIAL' : 'INT AUTO_INCREMENT'} NOT NULL PRIMARY KEY,
upload_file_id ${Model.client === 'pg' ? 'integer' : 'int'},
related_id ${Model.client === 'pg' ? 'integer' : 'int'},
related_type text,
field text
);
1 EXECUTE THE FOLLOWING SQL QUERY
2 RESTART YOUR SERVER
`);
strapi.stop();
}
}
// set plugin store // set plugin store
const pluginStore = strapi.store({ const pluginStore = strapi.store({
environment: strapi.config.environment, environment: strapi.config.environment,

View File

@ -28,7 +28,7 @@
"required": true "required": true
}, },
"size": { "size": {
"type": "float", "type": "string",
"configurable": false, "configurable": false,
"required": true "required": true
}, },

View File

@ -74,7 +74,7 @@ module.exports = async cb => {
callback: '/auth/twitter/callback' callback: '/auth/twitter/callback'
} }
}; };
const prevGrantConfig = await pluginStore.get({key: 'grant'}) const prevGrantConfig = await pluginStore.get({key: 'grant'}) || {};
// store grant auth config to db // store grant auth config to db
// when plugin_users-permissions_grant is not existed in db // when plugin_users-permissions_grant is not existed in db
// or we have added/deleted provider here. // or we have added/deleted provider here.
@ -124,7 +124,5 @@ module.exports = async cb => {
await pluginStore.set({key: 'advanced', value}); await pluginStore.set({key: 'advanced', value});
} }
strapi.plugins['users-permissions'].services.userspermissions.syncSchema(() => {
strapi.plugins['users-permissions'].services.userspermissions.initialize(cb); strapi.plugins['users-permissions'].services.userspermissions.initialize(cb);
});
}; };

View File

@ -4,7 +4,13 @@ module.exports = {
find: async function (params = {}, populate) { find: async function (params = {}, populate) {
const records = await this.query(function(qb) { const records = await this.query(function(qb) {
_.forEach(params.where, (where, key) => { _.forEach(params.where, (where, key) => {
qb.where(key, where[0].symbol, where[0].value); if (_.isArray(where.value)) {
for (const value in where.value) {
qb[value ? 'where' : 'orWhere'](key, where.symbol, where.value[value])
}
} else {
qb.where(key, where.symbol, where.value);
}
}); });
if (params.sort) { if (params.sort) {

View File

@ -401,179 +401,6 @@ module.exports = {
} }
}, },
syncSchema: async (cb) => {
if (strapi.plugins['users-permissions'].models.user.orm !== 'bookshelf') {
return cb();
}
// Extract necessary information from plugin's models.
const {
user: { collectionName: userTableName, connection: userConnection, client: userClient },
role: { collectionName: roleTableName, connection: roleConnection, client: roleClient },
permission: { collectionName: permissionTableName, connection: permissionConnection, client: permissionClient }
} = strapi.plugins['users-permissions'].models;
const details = {
user: {
tableName: userTableName,
connection: userConnection,
client: userClient
},
role: {
tableName: roleTableName,
connection: roleConnection,
client: roleClient
},
permission: {
tableName: permissionTableName,
connection: permissionConnection,
client: permissionClient
}
};
// Check if the tables are existing.
const hasTables = await Promise.all(Object.keys(details).map(name =>
strapi.connections[details[name].connection].schema.hasTable(details[name].tableName)
));
const missingTables = [];
const tablesToCreate = [];
for (let index = 0; index < hasTables.length; ++index) {
const hasTable = hasTables[index];
const currentModel = Object.keys(details)[index];
const quote = details[currentModel].client === 'pg' ? '"' : '`';
if (!hasTable) {
missingTables.push(`
TABLE \`${details[currentModel].tableName}\` DOESN'T EXIST`);
switch (currentModel) {
case 'user':
tablesToCreate.push(`
CREATE TABLE ${quote}${details[currentModel].tableName}${quote} (
id ${details[currentModel].client === 'pg' ? 'SERIAL' : 'INT AUTO_INCREMENT'} NOT NULL PRIMARY KEY,
username text,
email text,
provider text,
role ${details[currentModel].client === 'pg' ? 'integer' : 'int'},
${quote}resetPasswordToken${quote} text,
password text,
updated_at ${details[currentModel].client === 'pg' ? 'timestamp with time zone' : 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'},
created_at ${details[currentModel].client === 'pg' ? 'timestamp with time zone' : 'timestamp DEFAULT CURRENT_TIMESTAMP'}
);`);
break;
case 'role':
tablesToCreate.push(`
CREATE TABLE ${quote}${details[currentModel].tableName}${quote} (
id ${details[currentModel].client === 'pg' ? 'SERIAL' : 'INT AUTO_INCREMENT'} NOT NULL PRIMARY KEY,
name text,
description text,
type text
);`);
break;
case 'permission':
tablesToCreate.push(`
CREATE TABLE ${quote}${details[currentModel].tableName}${quote} (
id ${details[currentModel].client === 'pg' ? 'SERIAL' : 'INT AUTO_INCREMENT'} NOT NULL PRIMARY KEY,
role ${details[currentModel].client === 'pg' ? 'integer' : 'int'},
type text,
controller text,
action text,
enabled boolean,
policy text
);`);
break;
default:
}
}
}
if (!_.isEmpty(tablesToCreate)) {
tablesToCreate.unshift(`
1 EXECUTE THE FOLLOWING SQL QUERY`);
tablesToCreate.push(`
2 RESTART YOUR SERVER`)
strapi.log.warn(missingTables.concat(tablesToCreate).join(''));
// Stop the server.
strapi.stop();
}
const missingColumns = [];
const tablesToAlter = [];
for (let index = 0; index < hasTables.length; ++index) {
const currentModel = Object.keys(details)[index];
const quote = details[currentModel].client === 'pg' ? '"' : '`';
const attributes = {
id: {
type: details[currentModel].client === 'pg' ? 'integer' : 'int'
},
..._.cloneDeep(strapi.plugins['users-permissions'].models[currentModel].attributes)
};
// Add created_at and updated_at attributes for the model User.
if (currentModel === 'user') {
Object.assign(attributes, {
created_at: {
type: details[currentModel].client === 'pg' ? 'timestamp with time zone' : 'timestamp'
},
updated_at: {
type: details[currentModel].client === 'pg' ? 'timestamp with time zone' : 'timestamp'
}
});
}
const columns = Object.keys(attributes);
// Check if there are the required attributes.
const hasColumns = await Promise.all(columns.map(attribute =>
strapi.connections[details[currentModel].connection].schema.hasColumn(details[currentModel].tableName, attribute)
));
hasColumns.forEach((hasColumn, index) => {
const currentColumn = columns[index];
const attribute = attributes[currentColumn];
if (!hasColumn && !attribute.collection) {
const currentType = attribute.model ? 'integer' : attribute.type;
const type = currentType === 'string' ? 'text' : currentType;
missingColumns.push(`
TABLE \`${details[currentModel].tableName}\` HAS MISSING COLUMNS`);
tablesToAlter.push(`
ALTER TABLE ${quote}${details[currentModel].tableName}${quote} ADD ${details[currentModel].client === 'pg' ? `${quote}${currentColumn}${quote}` : `${currentColumn}`} ${type};`);
}
});
}
if (!_.isEmpty(tablesToAlter)) {
tablesToAlter.unshift(`
1 EXECUTE THE FOLLOWING SQL QUERIES`);
tablesToAlter.push(`
2 RESTART YOUR SERVER`)
strapi.log.warn(missingColumns.concat(tablesToAlter).join(''));
// Stop the server.
return strapi.stop();
}
cb();
},
template: (layout, data) => { template: (layout, data) => {
const compiledObject = _.template(layout); const compiledObject = _.template(layout);
return compiledObject(data); return compiledObject(data);

View File

@ -27,6 +27,11 @@ const { cli, logger } = require('strapi-utils');
*/ */
module.exports = function() { module.exports = function() {
// Check that we're in a valid Strapi project.
if (!cli.isStrapiApp()) {
return logger.error('This command can only be used inside a Strapi project.');
}
try { try {
const strapi = function () { const strapi = function () {
try { try {

View File

@ -176,6 +176,8 @@ class Strapi extends EventEmitter {
} }
async load() { async load() {
await this.enhancer();
this.app.use(async (ctx, next) => { this.app.use(async (ctx, next) => {
if (ctx.request.url === '/_health' && ctx.request.method === 'HEAD') { if (ctx.request.url === '/_health' && ctx.request.method === 'HEAD') {
ctx.set('strapi', 'You are so French!'); ctx.set('strapi', 'You are so French!');
@ -199,8 +201,8 @@ class Strapi extends EventEmitter {
// Usage. // Usage.
await utils.usage.call(this); await utils.usage.call(this);
// Init core store manager // Init core store
await store.pre.call(this); await store.call(this);
// Initialize hooks and middlewares. // Initialize hooks and middlewares.
await Promise.all([ await Promise.all([
@ -208,9 +210,6 @@ class Strapi extends EventEmitter {
initializeHooks.call(this) initializeHooks.call(this)
]); ]);
// Core store post middleware and hooks init validation.
await store.post.call(this);
// Harmonize plugins configuration. // Harmonize plugins configuration.
await plugins.call(this); await plugins.call(this);
} }

View File

@ -327,6 +327,7 @@ module.exports.app = async function() {
}, {}); }, {});
this.config.port = get(this.config.currentEnvironment, 'server.port') || this.config.port; this.config.port = get(this.config.currentEnvironment, 'server.port') || this.config.port;
this.config.host = get(this.config.currentEnvironment, 'server.host') || this.config.host;
this.config.url = `http://${this.config.host}:${this.config.port}`; this.config.url = `http://${this.config.host}:${this.config.port}`;
}; };

View File

@ -1,7 +1,6 @@
'use strict'; 'use strict';
module.exports = { module.exports = function () {
pre: function () {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.models['core_store'] = { this.models['core_store'] = {
connection: 'default', connection: 'default',
@ -14,7 +13,7 @@ module.exports = {
type: 'string' type: 'string'
}, },
value: { value: {
type: 'string' type: 'text'
}, },
type: { type: {
type: 'string' type: 'string'
@ -135,44 +134,4 @@ module.exports = {
resolve(); 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' ? '"' : '`';
this.log.warn(`
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,
${quote}key${quote} text,
${quote}value${quote} 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});
1 EXECUTE THE FOLLOWING SQL QUERY
2 RESTART YOUR SERVER
`);
// Stop the server.
return this.stop();
}
resolve();
});
}
}; };