mirror of
https://github.com/strapi/strapi.git
synced 2025-11-02 02:44:55 +00:00
Merge branch 'master' into fix/remove-old-file
This commit is contained in:
commit
d9e3fde726
@ -332,6 +332,7 @@ Most of the application's configurations are defined by environment. It means th
|
||||
- `port` (integer): Port on which the server should be running. Default value: `1337`.
|
||||
- `autoReload`
|
||||
- `enabled` (boolean): Enable or disabled server reload on files update. Default value: depends on the environment.
|
||||
- `emitErrors` (boolean): Enable errors to be emited to `koa` when they happen in order to attach custom logic or use error reporting services.
|
||||
- `proxy`
|
||||
- `enabled` (boolean): Enable proxy support such as Apache or Nginx. Default value: `false`.
|
||||
- `ssl` (boolean): Enable proxy SSL support
|
||||
@ -340,6 +341,7 @@ Most of the application's configurations are defined by environment. It means th
|
||||
- [`cron`](https://en.wikipedia.org/wiki/Cron)
|
||||
- `enabled` (boolean): Enable or disable CRON tasks to schedule jobs at specific dates. Default value: `false`.
|
||||
- `admin`
|
||||
- `autoOpen` (boolean): Enable or disabled administration opening on start (default: `true`)
|
||||
- `path` (string): Allow to change the URL to access the admin (default: `/admin`).
|
||||
- `build`
|
||||
- `host` (string): URL to access the admin panel (default: `http://localhost:1337/admin`).
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -44,7 +44,7 @@ import ListPluginsPage from 'containers/ListPluginsPage/Loadable';
|
||||
import Logout from 'components/Logout';
|
||||
import NotFoundPage from 'containers/NotFoundPage/Loadable';
|
||||
import OverlayBlocker from 'components/OverlayBlocker';
|
||||
import PluginPage from 'containers/PluginPage/Loadable';
|
||||
import PluginPage from 'containers/PluginPage';
|
||||
|
||||
// Utils
|
||||
import auth from 'utils/auth';
|
||||
|
||||
@ -51,4 +51,4 @@
|
||||
"npm": ">= 5.0.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,4 +43,4 @@
|
||||
"npm": ">= 5.3.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
@ -43,4 +43,4 @@
|
||||
"npm": ">= 5.3.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
@ -43,4 +43,4 @@
|
||||
"npm": ">= 5.3.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
@ -49,4 +49,4 @@
|
||||
"npm": ">= 5.3.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
@ -44,4 +44,4 @@
|
||||
"npm": ">= 5.3.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
@ -43,4 +43,4 @@
|
||||
"npm": ">= 5.3.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
@ -43,4 +43,4 @@
|
||||
"npm": ">= 5.3.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
@ -69,6 +69,7 @@ module.exports = {
|
||||
'reactstrap': path.resolve(rootAdminpath, 'node_modules', 'strapi-helper-plugin', 'node_modules', 'reactstrap'),
|
||||
'react-dnd': path.resolve(rootAdminpath, 'node_modules', 'strapi-helper-plugin', 'node_modules', 'react-dnd'),
|
||||
'react-dnd-hmtl5-backend': path.resolve(rootAdminpath, 'node_modules', 'strapi-helper-plugin', 'node_modules', 'react-dnd-html5-backend'),
|
||||
'styled-components': path.resolve(rootAdminpath, 'node_modules', 'strapi-helper-plugin', 'node_modules', 'styled-components'),
|
||||
},
|
||||
symlinks: false,
|
||||
extensions: [
|
||||
|
||||
@ -238,11 +238,5 @@ module.exports = base({
|
||||
|
||||
devtool: 'cheap-module-source-map',
|
||||
disableExtractTextPlugin: false,
|
||||
externals: {
|
||||
'styled-components': {
|
||||
commonjs: 'styled-components',
|
||||
commonjs2: 'styled-components',
|
||||
amd: 'styled-components',
|
||||
},
|
||||
},
|
||||
externals: {},
|
||||
});
|
||||
|
||||
@ -56,6 +56,10 @@ class ImgPreview extends React.Component {
|
||||
this.setState({ isInitValue: true });
|
||||
}
|
||||
}
|
||||
|
||||
if (isEmpty(nextProps.files)) {
|
||||
this.setState({ isImg: false, imgURL: null });
|
||||
}
|
||||
}
|
||||
|
||||
componentDidCatch(error, info) {
|
||||
|
||||
@ -137,7 +137,7 @@ class InputFile extends React.Component {
|
||||
position={this.state.position}
|
||||
updateFilePosition={this.updateFilePosition}
|
||||
/>
|
||||
<label style={{"margin-bottom": 0, width: '100%'}}>
|
||||
<label style={{ marginBottom: 0, width: '100%' }}>
|
||||
<input
|
||||
className={styles.inputFile}
|
||||
multiple={multiple}
|
||||
|
||||
@ -19,9 +19,9 @@ import InputErrors from 'components/InputErrors';
|
||||
// Styles
|
||||
import styles from './styles.scss';
|
||||
|
||||
class InputFileWithErrors extends React.Component {
|
||||
state = { errors: [], label: false, hasValue: false };
|
||||
|
||||
class InputFileWithErrors extends React.PureComponent {
|
||||
state = { errors: [], label: null, hasValue: false };
|
||||
|
||||
componentDidMount() {
|
||||
const { errors } = this.props;
|
||||
let newState = Object.assign({}, this.state);
|
||||
@ -37,21 +37,28 @@ class InputFileWithErrors extends React.Component {
|
||||
this.setState(newState);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (!this.state.hasValue && !isEmpty(nextProps.value) && nextProps.multiple && differenceBy(nextProps.value, this.props.value, 'name').length > 0) {
|
||||
this.setState({ label: 1, hasValue: true });
|
||||
componentDidUpdate(prevProps) {
|
||||
if (!this.state.hasValue && !isEmpty(this.props.value) && this.props.multiple && differenceBy(this.props.value, prevProps.value, 'name').length > 0) {
|
||||
this.updateState({ label: 1, hasValue: true });
|
||||
} else if(isEmpty(this.props.value)) {
|
||||
this.updateState({ label: null });
|
||||
}
|
||||
// Check if errors have been updated during validations
|
||||
if (nextProps.didCheckErrors !== this.props.didCheckErrors) {
|
||||
if (prevProps.didCheckErrors !== this.props.didCheckErrors) {
|
||||
// Remove from the state the errors that have already been set
|
||||
const errors = isEmpty(nextProps.errors) ? [] : nextProps.errors;
|
||||
this.setState({ errors });
|
||||
const errors = isEmpty(this.props.errors) ? [] : this.props.errors;
|
||||
this.updateState({ errors });
|
||||
}
|
||||
}
|
||||
|
||||
setLabel = (label) => {
|
||||
this.setState({ label });
|
||||
}
|
||||
|
||||
updateState = state => {
|
||||
this.setState(state);
|
||||
}
|
||||
|
||||
// TODO handle errors lifecycle
|
||||
render() {
|
||||
const {
|
||||
|
||||
@ -5,13 +5,28 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
const LoadingIndicatorPage = () => {
|
||||
const LoadingIndicatorPage = (props) => {
|
||||
|
||||
if (props.error) {
|
||||
console.log(props.error);
|
||||
return <div>An error occurred</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.loaderPage}><div /></div>
|
||||
);
|
||||
};
|
||||
|
||||
LoadingIndicatorPage.defaultProps = {
|
||||
error: null,
|
||||
};
|
||||
|
||||
LoadingIndicatorPage.propTypes = {
|
||||
error: PropTypes.object,
|
||||
};
|
||||
|
||||
export default LoadingIndicatorPage;
|
||||
|
||||
@ -109,7 +109,7 @@
|
||||
"sass-loader": "^6.0.6",
|
||||
"shelljs": "^0.7.8",
|
||||
"style-loader": "^0.18.2",
|
||||
"styled-components": "^3.2.6",
|
||||
"styled-components": "3.2.6",
|
||||
"url-loader": "^0.5.9",
|
||||
"webpack": "^3.5.5",
|
||||
"webpack-bundle-analyzer": "^2.9.0",
|
||||
|
||||
@ -55,4 +55,4 @@
|
||||
"npm": ">= 5.3.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
@ -43,4 +43,4 @@
|
||||
"npm": ">= 5.3.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
@ -227,7 +227,7 @@ export class EditPage extends React.Component {
|
||||
let value = e.target.value;
|
||||
// Check if date
|
||||
if (isObject(e.target.value) && e.target.value._isAMomentObject === true) {
|
||||
value = moment(e.target.value, 'YYYY-MM-DD HH:mm:ss').format();
|
||||
value = moment(e.target.value).format('YYYY-MM-DD HH:mm:ss');
|
||||
} else if (['float', 'integer', 'biginteger', 'decimal'].indexOf(get(this.getSchema(), ['fields', e.target.name, 'type'])) !== -1) {
|
||||
value = toNumber(e.target.value);
|
||||
}
|
||||
@ -259,7 +259,7 @@ export class EditPage extends React.Component {
|
||||
}
|
||||
default:
|
||||
const pathname = `${this.props.match.path.replace(':slug', model).replace(':id', id)}`;
|
||||
|
||||
|
||||
this.props.history.push({
|
||||
pathname,
|
||||
search: `?source=${source}&redirectURI=${generateRedirectURI({ model, search: `?source=${source}` })}`,
|
||||
@ -341,11 +341,11 @@ export class EditPage extends React.Component {
|
||||
const pathname = source !== 'content-manager'
|
||||
? `${basePath}/plugins/${source}/${this.getModelName()}`
|
||||
: `${basePath}/${this.getModelName()}`;
|
||||
|
||||
|
||||
if (this.showLoaders()) {
|
||||
return (
|
||||
<div className={!this.hasDisplayedRelations() ? 'col-lg-12' : 'col-lg-9'}>
|
||||
<div className={styles.main_wrapper}>
|
||||
<div className={styles.main_wrapper}>
|
||||
<LoadingIndicator />
|
||||
</div>
|
||||
</div>
|
||||
@ -506,5 +506,3 @@ export default compose(
|
||||
withSaga,
|
||||
withConnect,
|
||||
)(DragDropContext(HTML5Backend)(EditPage));
|
||||
|
||||
|
||||
|
||||
@ -107,6 +107,7 @@ export function* dataDeleteAll({ entriesToDelete, model, source }) {
|
||||
|
||||
yield put(deleteSeveralDataSuccess());
|
||||
yield call(dataGet, { currentModel: model, source });
|
||||
strapi.notification.success('content-manager.success.record.delete');
|
||||
} catch(err) {
|
||||
strapi.notification.error('content-manager.error.record.delete');
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
"containers.Edit.delete": "Удалить",
|
||||
"containers.Edit.reset": "Сбросить",
|
||||
"containers.Edit.returnList": "Вернуться к списку",
|
||||
"containers.List.addAnEntry": "Добавить новую {entity}",
|
||||
"containers.List.addAnEntry": "Добавить новые {entity}",
|
||||
"containers.List.pluginHeaderDescription": "{label} записей найдено",
|
||||
"containers.List.pluginHeaderDescription.singular": "{label} запись найдена",
|
||||
"components.LimitSelect.itemsPerPage": "Элементов на странице",
|
||||
@ -46,9 +46,9 @@
|
||||
"components.TableDelete.entries.plural": "{число} записей выбрано",
|
||||
"components.TableDelete.entries.singular": "{число} записей выделено",
|
||||
"components.TableDelete.delete": "Удалить все",
|
||||
"components.TableEmpty.withFilters": "Нет {Тип данных} с примененными фильтрами...",
|
||||
"components.TableEmpty.withoutFilter": "Нет {Тип данных}...",
|
||||
"components.TableEmpty.withSearch": "Нет {Тип данных} согласно поиску ({поиск})",
|
||||
"components.TableEmpty.withFilters": "Нет {contentType} с примененными фильтрами...",
|
||||
"components.TableEmpty.withoutFilter": "Нет {contentType}...",
|
||||
"components.TableEmpty.withSearch": "Нет {contentType} согласно поиску ({search})",
|
||||
"error.validation.json": "Это не JSON",
|
||||
"form.Input.label.inputDescription": "Это знчение переопределит метку, в заголовке таблицы",
|
||||
"form.Input.label": "Метка",
|
||||
@ -107,4 +107,4 @@
|
||||
"popUpWarning.title": "Пожалуйста подтвердите",
|
||||
"popUpWarning.bodyMessage.contentType.delete": "Вы уверены, что хотите удалить эту запись?"
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -52,4 +52,4 @@
|
||||
"npm": ">= 5.0.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,6 +183,18 @@ module.exports = {
|
||||
return ctx.badRequest(null, [{ messages: removeModelErrors }]);
|
||||
}
|
||||
|
||||
const pluginStore = strapi.store({
|
||||
environment: '',
|
||||
type: 'plugin',
|
||||
name: 'content-manager'
|
||||
});
|
||||
|
||||
const schema = await pluginStore.get({ key: 'schema' });
|
||||
|
||||
delete schema.layout[model];
|
||||
|
||||
await pluginStore.set({ key: 'schema', value: schema });
|
||||
|
||||
ctx.send({ ok: true });
|
||||
|
||||
strapi.reload();
|
||||
|
||||
@ -42,7 +42,7 @@ module.exports = {
|
||||
const attrToRemoveInfos = manager.attrToRemoveInfos; // Retrieve the removed item infos
|
||||
const arrayOfLastLineElements = manager.arrayOfEndLineElements;
|
||||
const isRemovingAFullWidthNode = attrToRemoveInfos.bootstrapCol === 12;
|
||||
|
||||
|
||||
if (isRemovingAFullWidthNode) {
|
||||
const currentNodeLine = _.findIndex(arrayOfLastLineElements, ['index', attrToRemoveInfos.index]); // Used only to know if removing a full size element on the first line
|
||||
if (currentNodeLine === 0) {
|
||||
@ -55,7 +55,7 @@ module.exports = {
|
||||
const previousLineRangeIndexes = firstElementOnLine === lastElementOnLine ? [firstElementOnLine] : _.range(firstElementOnLine, lastElementOnLine);
|
||||
const elementsOnLine = manager.getElementsOnALine(previousLineRangeIndexes);
|
||||
const previousLineColNumber = manager.getLineSize(elementsOnLine);
|
||||
|
||||
|
||||
if (previousLineColNumber >= 10) {
|
||||
newList = newList
|
||||
.delete(index);
|
||||
@ -65,7 +65,7 @@ module.exports = {
|
||||
newList = newList
|
||||
.delete(index)
|
||||
.insert(index, colsToAdd[0]);
|
||||
|
||||
|
||||
if (colsToAdd.length > 1) {
|
||||
newList = newList
|
||||
.insert(index, colsToAdd[1]);
|
||||
@ -84,7 +84,7 @@ module.exports = {
|
||||
newList = newList
|
||||
.delete(attrToRemoveInfos.index);
|
||||
} else {
|
||||
const random = Math.floor(Math.random() * 1000);
|
||||
const random = Math.random().toString(36).substring(7);
|
||||
newList = newList
|
||||
.delete(attrToRemoveInfos.index)
|
||||
.insert(rightBoundIndex, `__col-md-${attrToRemoveInfos.bootstrapCol}__${random}`);
|
||||
@ -96,7 +96,6 @@ module.exports = {
|
||||
const lastManager = createManager(state, newList, keys, 0, fromJS(layout.attributes));
|
||||
newList = reorderList(lastManager, lastManager.getLayout());
|
||||
});
|
||||
|
||||
|
||||
// Delete them from the available fields
|
||||
fieldsToRemove.forEach(field => {
|
||||
|
||||
@ -13,7 +13,7 @@ const cluster = require('cluster');
|
||||
// Public dependencies
|
||||
const fs = require('fs');
|
||||
const _ = require('lodash');
|
||||
const {cyan} = require('chalk');
|
||||
const { cyan } = require('chalk');
|
||||
|
||||
// Logger.
|
||||
const { cli, logger } = require('strapi-utils');
|
||||
@ -87,8 +87,6 @@ module.exports = function(appPath = '') {
|
||||
|
||||
setFilesToWatch(appPath);
|
||||
|
||||
|
||||
|
||||
if (cluster.isMaster) {
|
||||
cluster.on('message', (worker, message) => {
|
||||
switch (message) {
|
||||
|
||||
@ -70,6 +70,7 @@ class Strapi extends EventEmitter {
|
||||
port: process.env.PORT || 1337,
|
||||
environment: toLower(process.env.NODE_ENV) || 'development',
|
||||
environments: {},
|
||||
admin: {},
|
||||
paths: {
|
||||
admin: 'admin',
|
||||
api: 'api',
|
||||
@ -110,7 +111,7 @@ class Strapi extends EventEmitter {
|
||||
// Update source admin.
|
||||
await admin.call(this);
|
||||
// Launch server.
|
||||
this.server.listen(this.config.port, err => {
|
||||
this.server.listen(this.config.port, async (err) => {
|
||||
if (err) {
|
||||
this.log.debug(`⚠️ Server wasn't able to start properly.`);
|
||||
this.log.error(err);
|
||||
@ -124,7 +125,7 @@ class Strapi extends EventEmitter {
|
||||
this.log.info(`Version: ${this.config.info.strapi} (node v${this.config.info.node})`);
|
||||
this.log.info('To shut down your server, press <CTRL> + C at any time');
|
||||
console.log();
|
||||
this.log.info(`☄️ Admin panel: ${this.config.url}/admin`);
|
||||
this.log.info(`☄️ Admin panel: ${this.config.admin.url}`);
|
||||
this.log.info(`⚡️ Server: ${this.config.url}`);
|
||||
console.log();
|
||||
|
||||
@ -134,10 +135,15 @@ class Strapi extends EventEmitter {
|
||||
if (cb && typeof cb === 'function') {
|
||||
cb();
|
||||
}
|
||||
|
||||
if (this.config.environment === 'development' && get(this.config.currentEnvironment, 'server.admin.autoOpen', true) !== false) {
|
||||
await utils.openBrowser.call(this);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
this.log.debug(`⛔️ Server wasn't able to start properly.`);
|
||||
this.log.error(err);
|
||||
console.log(err);
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
// Dependencies.
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const glob = require('glob');
|
||||
const { merge, setWith, get, upperFirst, isEmpty, isObject, pullAll, defaults, assign, clone, cloneDeep, camelCase } = require('lodash');
|
||||
@ -321,9 +322,13 @@ module.exports.app = async function() {
|
||||
// default settings
|
||||
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}`;
|
||||
|
||||
// default construct url
|
||||
this.config.url = `http://${this.config.host}:${this.config.port}`
|
||||
// Admin.
|
||||
this.config.admin.devMode = isAdminInDevMode.call(this);
|
||||
this.config.admin.url = this.config.admin.devMode ?
|
||||
`http://${this.config.host}:4000/${get(this.config.currentEnvironment.server, 'admin.path', 'admin')}`:
|
||||
`${this.config.url}/${get(this.config.currentEnvironment.server, 'admin.path', 'admin')}`;
|
||||
|
||||
// proxy settings
|
||||
this.config.proxy = get(this.config.currentEnvironment, 'server.proxy' || {});
|
||||
@ -376,3 +381,13 @@ const enableHookNestedDependencies = function (name, flattenHooksConfig, force =
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const isAdminInDevMode = function () {
|
||||
try {
|
||||
fs.accessSync(path.resolve(this.config.appPath, 'admin', 'admin', 'build', 'index.html'), fs.constants.R_OK | fs.constants.W_OK);
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@ -25,6 +25,11 @@ module.exports = strapi => {
|
||||
// App logic.
|
||||
await next();
|
||||
} catch (error) {
|
||||
// emit error if configured
|
||||
if (_.get(strapi, 'config.currentEnvironment.server.emitErrors', false)) {
|
||||
strapi.app.emit('error', error, ctx);
|
||||
}
|
||||
|
||||
// Log error.
|
||||
console.error(error);
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ const fetch = require('node-fetch');
|
||||
const Buffer = require('buffer').Buffer;
|
||||
const crypto = require('crypto');
|
||||
const exposer = require('./exposer');
|
||||
const openBrowser = require('./openBrowser');
|
||||
|
||||
module.exports = {
|
||||
loadFile: function(url) {
|
||||
@ -149,5 +150,6 @@ module.exports = {
|
||||
} catch (e) {
|
||||
// Silent.
|
||||
}
|
||||
}
|
||||
},
|
||||
openBrowser
|
||||
};
|
||||
|
||||
157
packages/strapi/lib/utils/openBrowser.js
Normal file
157
packages/strapi/lib/utils/openBrowser.js
Normal file
@ -0,0 +1,157 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var execSync = require('child_process').execSync;
|
||||
var chalk = require('chalk');
|
||||
var spawn = require('cross-spawn');
|
||||
var opn = require('opn');
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
// https://github.com/sindresorhus/opn#app
|
||||
var OSX_CHROME = 'google chrome';
|
||||
|
||||
const Actions = Object.freeze({
|
||||
NONE: 0,
|
||||
BROWSER: 1,
|
||||
SCRIPT: 2,
|
||||
});
|
||||
|
||||
function getBrowserEnv() {
|
||||
// Attempt to honor this environment variable.
|
||||
// It is specific to the operating system.
|
||||
// See https://github.com/sindresorhus/opn#app for documentation.
|
||||
const value = process.env.BROWSER;
|
||||
let action;
|
||||
if (!value) {
|
||||
// Default.
|
||||
action = Actions.BROWSER;
|
||||
} else if (value.toLowerCase().endsWith('.js')) {
|
||||
action = Actions.SCRIPT;
|
||||
} else if (value.toLowerCase() === 'none') {
|
||||
action = Actions.NONE;
|
||||
} else {
|
||||
action = Actions.BROWSER;
|
||||
}
|
||||
return { action, value };
|
||||
}
|
||||
|
||||
function executeNodeScript(scriptPath, url) {
|
||||
const extraArgs = process.argv.slice(2);
|
||||
const child = spawn('node', [scriptPath, ...extraArgs, url], {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
child.on('close', code => {
|
||||
if (code !== 0) {
|
||||
console.log();
|
||||
console.log(
|
||||
chalk.red(
|
||||
'The script specified as BROWSER environment variable failed.'
|
||||
)
|
||||
);
|
||||
console.log(`${chalk.cyan(scriptPath)} exited with code ${code}.`);
|
||||
console.log();
|
||||
return;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
function startBrowserProcess(browser, url) {
|
||||
// If we're on OS X, the user hasn't specifically
|
||||
// requested a different browser, we can try opening
|
||||
// Chrome with AppleScript. This lets us reuse an
|
||||
// existing tab when possible instead of creating a new one.
|
||||
const shouldTryOpenChromeWithAppleScript =
|
||||
process.platform === 'darwin' &&
|
||||
(typeof browser !== 'string' || browser === OSX_CHROME);
|
||||
|
||||
if (shouldTryOpenChromeWithAppleScript) {
|
||||
try {
|
||||
// Try our best to reuse existing tab
|
||||
// on OS X Google Chrome with AppleScript
|
||||
execSync('ps cax | grep "Google Chrome"');
|
||||
execSync(`osascript resources/openChrome.applescript "${encodeURI(url)}"`, {
|
||||
cwd: __dirname,
|
||||
stdio: 'ignore',
|
||||
});
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
// Ignore errors.
|
||||
}
|
||||
}
|
||||
|
||||
// Another special case: on OS X, check if BROWSER has been set to "open".
|
||||
// In this case, instead of passing `open` to `opn` (which won't work),
|
||||
// just ignore it (thus ensuring the intended behavior, i.e. opening the system browser):
|
||||
// https://github.com/facebook/create-react-app/pull/1690#issuecomment-283518768
|
||||
if (process.platform === 'darwin' && browser === 'open') {
|
||||
browser = undefined;
|
||||
}
|
||||
|
||||
// Fallback to opn
|
||||
// (It will always open new tab)
|
||||
try {
|
||||
var options = { app: browser };
|
||||
opn(url, options).catch(() => {}); // Prevent `unhandledRejection` error.
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function pingDashboard(url, multipleTime = false) {
|
||||
try {
|
||||
await fetch(url, { method:'HEAD', timeout: 300, body: null });
|
||||
// Inform the user that we're going to open the administration panel.
|
||||
this.log.info("⏳ Opening the admin panel...");
|
||||
} catch (e) {
|
||||
if (e.code !== 'ECONNREFUSED' && e.type !== 'request-timeout') {
|
||||
return console.error(e);
|
||||
}
|
||||
|
||||
// Only display once.
|
||||
if (!multipleTime) {
|
||||
this.log.warn(`⚠️ The admin panel is unavailable... Impossible to open it in the browser.`);
|
||||
}
|
||||
|
||||
// Only retry if the user is running the administration on another server.
|
||||
if (this.config.admin.devMode) {
|
||||
// Wait 1 second until the next ping.
|
||||
await new Promise((resolve) => { setTimeout(resolve, 1000); });
|
||||
await pingDashboard.call(this, url, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the BROWSER evironment variable and decides what to do with it. Returns
|
||||
* true if it opened a browser or ran a node.js script, otherwise false.
|
||||
*/
|
||||
async function openBrowser() {
|
||||
const url = this.config.admin.url;
|
||||
|
||||
// Ping the dashboard to ensure it's available.
|
||||
await pingDashboard.call(this, url);
|
||||
|
||||
const { action, value } = getBrowserEnv();
|
||||
switch (action) {
|
||||
case Actions.NONE:
|
||||
// Special case: BROWSER="none" will prevent opening completely.
|
||||
return false;
|
||||
case Actions.SCRIPT:
|
||||
return executeNodeScript(value, url);
|
||||
case Actions.BROWSER:
|
||||
return startBrowserProcess(value, url);
|
||||
default:
|
||||
throw new Error('Not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = openBrowser;
|
||||
83
packages/strapi/lib/utils/resources/openChrome.applescript
Normal file
83
packages/strapi/lib/utils/resources/openChrome.applescript
Normal file
@ -0,0 +1,83 @@
|
||||
(*
|
||||
Copyright (c) 2015-present, Facebook, Inc.
|
||||
|
||||
This source code is licensed under the MIT license found in the
|
||||
LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
|
||||
property targetTab: null
|
||||
property targetTabIndex: -1
|
||||
property targetWindow: null
|
||||
|
||||
on run argv
|
||||
set theURL to item 1 of argv
|
||||
|
||||
tell application "Chrome"
|
||||
|
||||
if (count every window) = 0 then
|
||||
make new window
|
||||
end if
|
||||
|
||||
-- 1: Looking for tab running debugger
|
||||
-- then, Reload debugging tab if found
|
||||
-- then return
|
||||
set found to my lookupTabWithUrl(theURL)
|
||||
if found then
|
||||
set targetWindow's active tab index to targetTabIndex
|
||||
tell targetTab to reload
|
||||
tell targetWindow to activate
|
||||
set index of targetWindow to 1
|
||||
return
|
||||
end if
|
||||
|
||||
-- 2: Looking for Empty tab
|
||||
-- In case debugging tab was not found
|
||||
-- We try to find an empty tab instead
|
||||
set found to my lookupTabWithUrl("chrome://newtab/")
|
||||
if found then
|
||||
set targetWindow's active tab index to targetTabIndex
|
||||
set URL of targetTab to theURL
|
||||
tell targetWindow to activate
|
||||
return
|
||||
end if
|
||||
|
||||
-- 3: Create new tab
|
||||
-- both debugging and empty tab were not found
|
||||
-- make a new tab with url
|
||||
tell window 1
|
||||
activate
|
||||
make new tab with properties {URL:theURL}
|
||||
end tell
|
||||
end tell
|
||||
end run
|
||||
|
||||
-- Function:
|
||||
-- Lookup tab with given url
|
||||
-- if found, store tab, index, and window in properties
|
||||
-- (properties were declared on top of file)
|
||||
on lookupTabWithUrl(lookupUrl)
|
||||
tell application "Chrome"
|
||||
-- Find a tab with the given url
|
||||
set found to false
|
||||
set theTabIndex to -1
|
||||
repeat with theWindow in every window
|
||||
set theTabIndex to 0
|
||||
repeat with theTab in every tab of theWindow
|
||||
set theTabIndex to theTabIndex + 1
|
||||
if (theTab's URL as string) contains lookupUrl then
|
||||
-- assign tab, tab index, and window to properties
|
||||
set targetTab to theTab
|
||||
set targetTabIndex to theTabIndex
|
||||
set targetWindow to theWindow
|
||||
set found to true
|
||||
exit repeat
|
||||
end if
|
||||
end repeat
|
||||
|
||||
if found then
|
||||
exit repeat
|
||||
end if
|
||||
end repeat
|
||||
end tell
|
||||
return found
|
||||
end lookupTabWithUrl
|
||||
@ -53,6 +53,7 @@
|
||||
"lodash": "^4.17.5",
|
||||
"node-fetch": "^1.7.3",
|
||||
"node-schedule": "^1.2.0",
|
||||
"opn": "^5.3.0",
|
||||
"rimraf": "^2.6.2",
|
||||
"semver": "^5.4.1",
|
||||
"stack-trace": "0.0.10",
|
||||
@ -92,4 +93,4 @@
|
||||
},
|
||||
"preferGlobal": true,
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user