Merge branch 'master' into fix/graphql-queries-context

This commit is contained in:
Alexandre BODIN 2019-10-07 10:46:54 +02:00 committed by GitHub
commit 5e4c2bd394
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1186 additions and 685 deletions

View File

@ -33,9 +33,120 @@ By default, the administration panel is exposed via [http://localhost:1337/admin
The panel will be available through [http://localhost:1337/dashboard](http://localhost:1337/dashboard) with the configurations above.
---
### Development mode
**_Currently not available_**
To enable the front-end development mode you need to start your application using the `--watch-admin` flag.
```bash
cd my-app
strapi develop --watch-admin
```
With this option you can do the following:
#### Customize the `strapi-admin` package
All files added in `my-app/admin/src/` will either be replaced or added
**Example: Changing the available locales of your application**
```bash
# Create both the admin and admin/src/translations folders
cd my-app && mkdir -p admin/src/translations
# Change the available locales of the administration panel
touch admin/src/i18n.js
# Change the import and exports of the translations files
touch admin/src/translations/index.js
```
**Path --** `my-app/admin/src/translations/index.js`
```js
import en from './en.json';
import fr from './fr.json';
const trads = {
en,
fr,
};
export default trads;
```
**Path --** `my-app/admin/src/i18n.js`
```js
import { addLocaleData } from 'react-intl';
import { reduce } from 'lodash';
import en from 'react-intl/locale-data/en';
import fr from 'react-intl/locale-data/fr';
import trads from './translations';
// We dismiss pt-BR and zh-Hans locales since they are not supported by react-intl
const locales = {
en,
fr,
};
const languages = Object.keys(trads);
/**
* Dynamically generate `translationsMessages object`.
*/
const translationMessages = reduce(
languages,
(result, language) => {
const obj = result;
obj[language] = trads[language];
if (locales[language]) {
addLocaleData(locales[language]);
}
return obj;
},
{}
);
export { languages, translationMessages };
```
::: note
With this modification only English and French will be available in your admin
:::
#### Customize a plugin
Similarly to the back-end override system any file added in `my-app/extensions/<plugin-name>/admin/` will be copied and used instead of the original one (use with care).
**Example: Changing the current WYSIWYG**
```bash
cd my-app/extensions
# Create the content manager folder
mkdir content-manager && cd content-manager
# Create the admin folder
mkdir -p admin/src
# Create the components folder and the WysiwygWithErrors one
cd admin/src && mkdir -p components/WysiwygWithErrors
# Create the index.js so the original file is overridden
touch components/WysiwygWithErrors/index.js
```
**Path --** `my-app/extensions/content-manager/admin/src/components/WysiwygWithErrors/index.js`
```js
import React from 'react';
import MyNewWYSIWYG from 'my-awesome-lib';
// This is a dummy example
const WysiwygWithErrors = props => <MyNewWYSIWYG {...props} />;
export default WysiwygWithErrors;
```
---
### Styles
@ -45,6 +156,14 @@ For example, to change the top-left displayed admin panel's color, `./node_modul
Thus, you are replacing the files that would normally be in `node_modules/strapi-admin/admin/src` and directing them to `admin/src/some/file/path`.
To apply your changes you need to rebuild your admin panel
```
npm run build
```
---
### Logo
To change the top-left displayed admin panel's logo, add your custom image at `./admin/src/assets/images/logo-strapi.png`.

View File

@ -37,6 +37,18 @@ Start a Strapi application with autoReload enabled.
Strapi modifies/creates files at runtime and needs to restart when new files are created. To achieve this, `strapi develop` adds a file watcher and restarts the application when necessary.
```
strapi develop
options: [--no-build |--watch-admin ]
```
- **strapi develop**<br/>
Starts your application with the autoReload enabled
- **strapi develop --no-build**<br/>
Starts your application with the autoReload enabled and skip the administration panel build process
- **strapi develop --watch-admin**<br/>
Starts your application with the autoReload enabled and the front-end development server. It allows you to customize the administration panel.
::: note
You should never use this command to run a Strapi application in production.
:::

View File

@ -110,7 +110,9 @@ Strapi comes with the following providers:
Set your providers credentials in the admin interface (Plugin Users & Permissions > Providers).
Then update and enable the provider you want use.
To authenticate the user, use the GET method to request the url, `/connect/:provider`. eg: `GET /connect/facebook`
To authenticate the user, use the GET method to request the url, `/connect/:provider`. eg: `GET /connect/facebook`.
You can also pass a custom callback url instead of using the default registered provider callback, by passing `callback` in the query. eg: `GET /connect/facebook?callback=https://my-frontend.com/en/auth/facebook`.
After authentication, create and customize your own redirect callback at `/auth/:provider/callback`. The `jwt` and `user` data will be available in a .json response.

View File

@ -27,51 +27,67 @@ describe('<LocaleToggle />', () => {
it('should return the en flag', () => {
const renderedComponent = shallow(<LocaleToggle {...props} />);
const { getFlagUrl } = renderedComponent.instance();
expect(getFlagUrl('en')).toEqual('https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/flags/4x3/us.svg');
expect(getFlagUrl('en')).toEqual(
'https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/flags/4x3/us.svg'
);
});
it('should return the pt-BR flag', () => {
const renderedComponent = shallow(<LocaleToggle {...props} />);
const { getFlagUrl } = renderedComponent.instance();
expect(getFlagUrl('pt-BR')).toEqual('https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/flags/4x3/br.svg');
expect(getFlagUrl('pt-BR')).toEqual(
'https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/flags/4x3/br.svg'
);
});
it('should return the zh flag', () => {
const renderedComponent = shallow(<LocaleToggle {...props} />);
const { getFlagUrl } = renderedComponent.instance();
expect(getFlagUrl('zh')).toEqual('https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/flags/4x3/tw.svg');
expect(getFlagUrl('zh-Hans')).toEqual('https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/flags/4x3/cn.svg');
expect(getFlagUrl('zh')).toEqual(
'https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/flags/4x3/tw.svg'
);
expect(getFlagUrl('zh-Hans')).toEqual(
'https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/flags/4x3/cn.svg'
);
});
it('should return the ar flag', () => {
const renderedComponent = shallow(<LocaleToggle {...props} />);
const { getFlagUrl } = renderedComponent.instance();
expect(getFlagUrl('ar')).toEqual('https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/flags/4x3/sa.svg');
expect(getFlagUrl('ar')).toEqual(
'https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/flags/4x3/sa.svg'
);
});
it('should return the ko flag', () => {
const renderedComponent = shallow(<LocaleToggle {...props} />);
const { getFlagUrl } = renderedComponent.instance();
expect(getFlagUrl('ko')).toEqual('https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/flags/4x3/kr.svg');
expect(getFlagUrl('ko')).toEqual(
'https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/flags/4x3/kr.svg'
);
});
it('should return the ja flag', () => {
const renderedComponent = shallow(<LocaleToggle {...props} />);
const { getFlagUrl } = renderedComponent.instance();
expect(getFlagUrl('ja')).toEqual('https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/flags/4x3/jp.svg');
expect(getFlagUrl('ja')).toEqual(
'https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/flags/4x3/jp.svg'
);
});
it('should return the locale flag', () => {
const renderedComponent = shallow(<LocaleToggle {...props} />);
const { getFlagUrl } = renderedComponent.instance();
const locale = 'fr';
expect(getFlagUrl(locale)).toEqual(`https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/flags/4x3/${locale}.svg`);
expect(getFlagUrl(locale)).toEqual(
`https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.1.0/flags/4x3/${locale}.svg`
);
});
});
@ -100,18 +116,17 @@ describe('<LocaleToggle />', () => {
it('should be injected', () => {
const dispatch = jest.fn();
const result = mapDispatchToProps(dispatch);
expect(result.changeLocale).toBeDefined();
});
it('should dispatch the changeLocale action when called', () => {
const dispatch = jest.fn();
const result = mapDispatchToProps(dispatch);
result.changeLocale();
expect(dispatch).toHaveBeenCalledWith(changeLocale());
});
});
});
});

View File

@ -1,7 +1,11 @@
/* eslint-disable no-useless-escape */
const path = require('path');
const fs = require('fs-extra');
const webpack = require('webpack');
const getWebpackConfig = require('./webpack.config.js');
const WebpackDevServer = require('webpack-dev-server');
const chalk = require('chalk');
const chokidar = require('chokidar');
const getPkgPath = name =>
path.dirname(require.resolve(`${name}/package.json`));
@ -83,7 +87,7 @@ async function copyCustomAdmin(src, dest) {
await fs.copy(src, path.resolve(dest, 'admin'));
}
async function build({ dir, env, options }) {
async function createCacheDir(dir) {
const cacheDir = path.resolve(dir, '.cache');
const pkgJSON = require(path.join(dir, 'package.json'));
@ -113,9 +117,34 @@ async function build({ dir, env, options }) {
await copyCustomAdmin(path.join(dir, 'admin'), cacheDir);
}
// override plugins' admin code with user customizations
const pluginsToOverride = pluginsToCopy.reduce((acc, current) => {
const pluginName = current.replace(/^strapi-plugin-/i, '');
if (fs.pathExistsSync(path.join(dir, 'extensions', pluginName, 'admin'))) {
acc.push(pluginName);
}
return acc;
}, []);
await Promise.all(
pluginsToOverride.map(plugin =>
copyCustomAdmin(
path.join(dir, 'extensions', plugin, 'admin'),
path.join(cacheDir, 'plugins', `strapi-plugin-${plugin}`)
)
)
);
}
async function build({ dir, env, options }) {
// Create the cache dir containing the front-end files.
await createCacheDir(dir);
const cacheDir = path.resolve(dir, '.cache');
const entry = path.resolve(cacheDir, 'admin', 'src', 'app.js');
const dest = path.resolve(dir, 'build');
const config = getWebpackConfig({ entry, dest, env, options });
const compiler = webpack(config);
@ -152,7 +181,150 @@ async function build({ dir, env, options }) {
});
}
async function watchAdmin({ dir, port, options }) {
// Create the cache dir containing the front-end files.
await createCacheDir(dir);
const entry = path.join(dir, '.cache', 'admin', 'src', 'app.js');
const dest = path.join(dir, 'build');
const env = 'development';
const args = {
entry,
dest,
env,
port,
options,
};
const opts = {
clientLogLevel: 'silent',
hot: true,
quiet: true,
open: true,
publicPath: options.publicPath,
historyApiFallback: {
index: options.publicPath,
},
};
const server = new WebpackDevServer(webpack(getWebpackConfig(args)), opts);
server.listen(port, 'localhost', function(err) {
if (err) {
console.log(err);
}
console.log(chalk.green('Starting the development server...'));
console.log();
console.log(
chalk.green(
`Admin development at http://localhost:${port}${opts.publicPath}`
)
);
});
watchFiles(dir);
}
async function watchFiles(dir) {
const cacheDir = path.join(dir, '.cache');
const pkgJSON = require(path.join(dir, 'package.json'));
const admin = path.join(dir, 'admin');
const appPlugins = Object.keys(pkgJSON.dependencies).filter(
dep =>
dep.startsWith('strapi-plugin') &&
fs.existsSync(path.resolve(getPkgPath(dep), 'admin', 'src', 'index.js'))
);
const pluginsToWatch = appPlugins.map(plugin =>
path.join(
dir,
'extensions',
plugin.replace(/^strapi-plugin-/i, ''),
'admin'
)
);
const filesToWatch = [admin, ...pluginsToWatch];
const watcher = chokidar.watch(filesToWatch, {
ignoreInitial: true,
ignorePermissionErrors: true,
});
watcher.on('all', async (event, filePath) => {
const re = /\/extensions\/([^\/]*)\/.*$/gm;
const matched = re.exec(filePath);
const isExtension = matched !== null;
const pluginName = isExtension ? matched[1] : '';
const packageName = isExtension
? `strapi-plugin-${pluginName}`
: 'strapi-admin';
const targetPath = isExtension
? filePath.split('/extensions/')[1].replace(pluginName, '')
: filePath.split('/admin')[1];
const destFolder = isExtension
? path.join(cacheDir, 'plugins', packageName)
: path.join(cacheDir, 'admin');
if (event === 'unlink' || event === 'unlinkDir') {
const originalFilePathInNodeModules = path.join(
getPkgPath(packageName),
isExtension ? '' : 'admin',
targetPath
);
// Remove the file or folder
// We need to copy the original files when deleting an override one
try {
fs.removeSync(path.join(destFolder, targetPath));
} catch (err) {
console.log('An error occured while deleting the file', err);
}
// Check if the file or folder exists in node_modules
// If so copy the old one
if (fs.pathExistsSync(path.resolve(originalFilePathInNodeModules))) {
try {
await fs.copy(
path.resolve(originalFilePathInNodeModules),
path.join(destFolder, targetPath)
);
// The plugins.js file needs to be recreated
// when we delete either the admin folder
// the admin/src folder
// or the plugins.js file
// since the path are different when developing inside the monorepository or inside an app
const shouldCopyPluginsJSFile =
filePath.split('/admin/src').filter(p => !!p).length === 1;
if (
(event === 'unlinkDir' &&
!isExtension &&
shouldCopyPluginsJSFile) ||
(!isExtension && filePath.includes('plugins.js'))
) {
await createPluginsJs(appPlugins, path.join(cacheDir));
}
} catch (err) {
// Do nothing
}
}
} else {
// In any other case just copy the file into the .cache folder
try {
await fs.copy(filePath, path.join(destFolder, targetPath));
} catch (err) {
console.log(err);
}
}
});
}
module.exports = {
build,
createPluginsJs,
watchAdmin,
};

View File

@ -102,6 +102,7 @@
"license": "MIT",
"gitHead": "c85658a19b8fef0f3164c19693a45db305dc07a9",
"devDependencies": {
"chokidar": "^3.1.1",
"webpack": "^4.40.1",
"webpack-cli": "^3.3.2",
"webpack-dev-server": "^3.4.1"

View File

@ -37,12 +37,15 @@ module.exports = ({
filename: '[name].[chunkhash].css',
chunkFilename: '[name].[chunkhash].chunkhash.css',
}),
new WebpackBar(),
]
: [
new DuplicatePckgChecker({
verbose: true,
}),
new FriendlyErrorsWebpackPlugin(),
new FriendlyErrorsWebpackPlugin({
clearConsole: false,
}),
];
const scssLoader = isProduction
@ -237,7 +240,6 @@ module.exports = ({
mainFields: ['browser', 'jsnext:main', 'main'],
},
plugins: [
new WebpackBar(),
new HtmlWebpackPlugin({
inject: true,
template: path.resolve(__dirname, 'index.html'),

View File

@ -11,11 +11,12 @@ import PropTypes from 'prop-types';
import styles from './styles.scss';
class GlobalPagination extends React.Component {
getLastPageNumber = () => Math.ceil(this.props.count / this.props.params._limit) || 1;
getLastPageNumber = () =>
Math.ceil(this.props.count / this.props.params._limit) || 1;
handleDotsClick = (e) => e.preventDefault();
handleDotsClick = e => e.preventDefault();
handlePreviousPageClick = (e) => {
handlePreviousPageClick = e => {
e.preventDefault();
if (!this.isFirstPage()) {
@ -25,9 +26,9 @@ class GlobalPagination extends React.Component {
};
this.props.onChangeParams({ target });
}
}
};
handleNextPageClick = (e) => {
handleNextPageClick = e => {
e.preventDefault();
if (!this.isLastPage()) {
@ -37,31 +38,32 @@ class GlobalPagination extends React.Component {
};
this.props.onChangeParams({ target });
}
}
};
handleFirstPageClick = (e) => {
handleFirstPageClick = e => {
e.preventDefault();
const target = {
name: 'params._page',
value: 1,
};
this.props.onChangeParams({ target });
}
};
handleLastPageClick = (e) => {
handleLastPageClick = e => {
e.preventDefault();
const target = {
name: 'params._page',
value: this.getLastPageNumber(),
};
this.props.onChangeParams({ target });
}
};
isFirstPage = () => this.props.params._page === 1;
isLastPage = () => this.props.params._page === this.getLastPageNumber();
needAfterLinksDots = () => this.props.params._page < this.getLastPageNumber() - 1;
needAfterLinksDots = () =>
this.props.params._page < this.getLastPageNumber() - 1;
needPreviousLinksDots = () => this.props.params._page > 3;
@ -111,19 +113,18 @@ class GlobalPagination extends React.Component {
}
// Generate links
return (
map(linksOptions, (linksOption, key) => (
<li
className={`${linksOption.isActive && styles.navLiActive}`}
key={key}
return map(linksOptions, (linksOption, key) => (
<li className={`${linksOption.isActive && styles.navLiActive}`} key={key}>
<a
href=""
disabled={linksOption.isActive}
onClick={linksOption.handleClick}
>
<a href="" disabled={linksOption.isActive} onClick={linksOption.handleClick}>
{linksOption.value}
</a>
</li>
))
);
}
{linksOption.value}
</a>
</li>
));
};
render() {
return (
@ -141,9 +142,7 @@ class GlobalPagination extends React.Component {
<i className="fa fa-angle-left" aria-hidden="true"></i>
</a>
<nav className={styles.nav}>
<ul className={styles.navUl}>
{this.renderLinks()}
</ul>
<ul className={styles.navUl}>{this.renderLinks()}</ul>
</nav>
<a
href=""
@ -172,16 +171,10 @@ GlobalPagination.defaultProps = {
};
GlobalPagination.propTypes = {
count: PropTypes.oneOfType([
PropTypes.number,
PropTypes.bool,
]),
count: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]),
onChangeParams: PropTypes.func,
params: PropTypes.shape({
_page: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
_page: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
_limit: PropTypes.number,
}),
};

View File

@ -33,13 +33,16 @@ module.exports = function(strapi) {
* Initialize the hook
*/
initialize () {
initialize() {
// Force cache mode in production
if (strapi.config.environment === 'production') {
strapi.config.hook.settings.ejs.cache = true;
}
render(strapi.app, Object.assign(this.defaults, strapi.config.hook.settings.ejs));
render(
strapi.app,
Object.assign(this.defaults, strapi.config.hook.settings.ejs)
);
strapi.app.context.render = co.wrap(strapi.app.context.render);
},

View File

@ -159,7 +159,9 @@ module.exports = {
return _.set(
acc,
current,
property ? property.map(val => val[assocModel.primaryKey] || val) : property
property
? property.map(val => val[assocModel.primaryKey] || val)
: property
);
}
@ -187,12 +189,14 @@ module.exports = {
return assocModel.updateMany(
{
[assocModel.primaryKey]: {
$in: property ? property.map(
val =>
new mongoose.Types.ObjectId(
val[assocModel.primaryKey] || val
$in: property
? property.map(
val =>
new mongoose.Types.ObjectId(
val[assocModel.primaryKey] || val
)
)
) : property,
: property,
},
},
{

View File

@ -172,9 +172,9 @@ const buildAssocResolvers = (model, name, { plugin }) => {
queryOpts,
['query', ref.primaryKey],
obj[association.alias]
? obj[association.alias].map(
val => val[ref.primaryKey] || val
).sort()
? obj[association.alias]
.map(val => val[ref.primaryKey] || val)
.sort()
: []
);
} else {

View File

@ -46,8 +46,8 @@
"configurable": false,
"required": true
},
"public_id": {
"type": "string",
"provider_metadata": {
"type": "json",
"configurable": false
},
"related": {

View File

@ -263,7 +263,8 @@ module.exports = {
if (!_.get(config, 'enabled')) {
return ctx.badRequest(null, 'This provider is disabled.');
}
// Ability to pass OAuth callback dynamically
grantConfig[provider].callback = ctx.query && ctx.query.callback ? ctx.query.callback : grantConfig[provider].callback;
return grant(grantConfig)(ctx, next);
},

View File

@ -42,18 +42,23 @@ module.exports = {
if (err) {
return reject(err);
}
file.public_id = image.public_id;
file.url = image.secure_url;
file.provider_metadata = {
public_id: image.public_id,
resource_type: image.resource_type,
};
resolve();
},
}
);
intoStream(file.buffer).pipe(upload_stream);
});
},
async delete(file) {
try {
const response = await cloudinary.uploader.destroy(file.public_id, {
const { resource_type, public_id } = file.provider_metadata;
const response = await cloudinary.uploader.destroy(public_id, {
invalidate: true,
resource_type: resource_type || 'image',
});
if (response.result !== 'ok') {
throw {

View File

@ -112,6 +112,7 @@ program
.command('develop')
.alias('dev')
.option('--no-build', 'Disable build', false)
.option('--watch-admin', 'Enable watch', true)
.description('Start your Strapi application in development mode')
.action(getLocalScript('develop'));
@ -190,6 +191,12 @@ program
.option('-d, --delete-files', 'Delete files', false)
.action(getLocalScript('uninstall'));
// `$ strapi watch-admin`
program
.command('watch-admin')
.description('Starts the admin dev server')
.action(getLocalScript('watchAdmin'));
/**
* Normalize help argument
*/

View File

@ -56,7 +56,7 @@ const createQueryManager = () => {
*/
class Strapi extends EventEmitter {
constructor({ dir, autoReload = false } = {}) {
constructor({ dir, autoReload = false, serveAdminPanel = true } = {}) {
super();
this.setMaxListeners(100);
@ -92,6 +92,7 @@ class Strapi extends EventEmitter {
// Default configurations.
this.config = {
serveAdminPanel,
launchedAt: Date.now(),
appPath: this.dir,
autoReload,
@ -170,7 +171,10 @@ class Strapi extends EventEmitter {
);
this.log.info('To shut down your server, press <CTRL> + C at any time');
console.log();
this.log.info(`☄️ Admin panel: ${this.config.admin.url}`);
if (this.config.serveAdminPanel === true) {
this.log.info(`☄️ Admin panel: ${this.config.admin.url}`);
}
this.log.info(`⚡️ Server: ${this.config.url}`);
console.log();

View File

@ -6,7 +6,7 @@ const _ = require('lodash');
const { green, yellow } = require('chalk');
const strapiAdmin = require('strapi-admin');
const loadConfigFile = require('../load/load-config-files');
const addSlash = require('../utils/addSlash');
/**
* `$ strapi build`
*/
@ -52,12 +52,3 @@ module.exports = async () => {
process.exit(1);
});
};
function addSlash(path) {
if (typeof path !== 'string') throw new Error('admin.path must be a string');
if (path === '' || path === '/') return '/';
if (path[0] != '/') path = '/' + path;
if (path[path.length - 1] != '/') path = path + '/';
return path;
}

View File

@ -7,16 +7,18 @@ const chokidar = require('chokidar');
const execa = require('execa');
const { logger } = require('strapi-utils');
const strapi = require('../index');
/**
* `$ strapi develop`
*
*/
module.exports = async function({ build }) {
module.exports = async function({ build, watchAdmin }) {
const dir = process.cwd();
if (build && !fs.existsSync(path.join(dir, 'build'))) {
// Don't run the build process if the admin is in watch mode
if (build && !watchAdmin && !fs.existsSync(path.join(dir, 'build'))) {
try {
execa.shellSync('npm run -s build', {
stdio: 'inherit',
@ -27,9 +29,24 @@ module.exports = async function({ build }) {
}
try {
const strapiInstance = strapi({ dir, autoReload: true });
const strapiInstance = strapi({
dir,
autoReload: true,
serveAdminPanel: watchAdmin ? false : true,
});
if (cluster.isMaster) {
// Start the front-end dev server
if (watchAdmin) {
try {
execa('npm', ['run', '-s', 'strapi', 'watch-admin'], {
stdio: 'inherit',
});
} catch (err) {
process.exit(1);
}
}
cluster.on('message', (worker, message) => {
switch (message) {
case 'reload':

View File

@ -0,0 +1,32 @@
/* eslint-disable no-useless-escape */
const path = require('path');
const strapiAdmin = require('strapi-admin');
const _ = require('lodash');
const loadConfigFile = require('../load/load-config-files');
const addSlash = require('../utils/addSlash');
module.exports = async function() {
const dir = process.cwd();
const envConfigDir = path.join(dir, 'config', 'environments', 'development');
const serverConfig = await loadConfigFile(envConfigDir, 'server.+(js|json)');
const port = _.get(serverConfig, 'port', 1337);
const host = _.get(serverConfig, 'host', 'localhost');
const adminPort = _.get(serverConfig, 'admin.port', 8000);
const adminBackend = _.get(
serverConfig,
'admin.build.backend',
`http://${host}:${port}`
);
const adminPath = _.get(serverConfig, 'admin.path', '/admin');
strapiAdmin.watchAdmin({
dir,
port: adminPort,
options: {
backend: adminBackend,
publicPath: addSlash(adminPath),
},
});
};

View File

@ -58,6 +58,8 @@ module.exports = strapi => {
})
);
if (!strapi.config.serveAdminPanel) return;
const basename = _.get(
strapi.config.currentEnvironment.server,
'admin.path'

View File

@ -0,0 +1,8 @@
module.exports = path => {
if (typeof path !== 'string') throw new Error('admin.path must be a string');
if (path === '' || path === '/') return '/';
if (path[0] != '/') path = '/' + path;
if (path[path.length - 1] != '/') path = path + '/';
return path;
};

1299
yarn.lock

File diff suppressed because it is too large Load Diff