2015-10-01 00:30:16 +02:00
|
|
|
'use strict';
|
|
|
|
|
2017-07-24 19:58:03 +02:00
|
|
|
// Dependencies.
|
2017-07-25 17:12:18 +02:00
|
|
|
const http = require('http');
|
|
|
|
const path = require('path');
|
2018-05-04 17:02:27 +02:00
|
|
|
const { EventEmitter } = require('events');
|
2019-06-14 18:13:25 +02:00
|
|
|
const fse = require('fs-extra');
|
2018-05-04 17:02:27 +02:00
|
|
|
const Koa = require('koa');
|
2019-04-05 16:11:09 +02:00
|
|
|
const _ = require('lodash');
|
2017-09-29 14:26:28 +02:00
|
|
|
const { logger, models } = require('strapi-utils');
|
2018-05-04 17:02:27 +02:00
|
|
|
const utils = require('./utils');
|
2019-04-05 16:11:09 +02:00
|
|
|
const {
|
2019-05-02 17:51:58 +02:00
|
|
|
loadConfig,
|
2019-04-05 16:11:09 +02:00
|
|
|
loadApis,
|
2019-05-02 17:51:58 +02:00
|
|
|
loadAdmin,
|
|
|
|
loadPlugins,
|
2019-04-05 16:11:09 +02:00
|
|
|
loadMiddlewares,
|
|
|
|
loadHooks,
|
2019-04-09 15:29:17 +02:00
|
|
|
bootstrap,
|
2019-04-10 18:11:55 +02:00
|
|
|
loadExtensions,
|
2019-04-09 21:51:28 +02:00
|
|
|
initCoreStore,
|
2019-04-05 16:11:09 +02:00
|
|
|
} = require('./core');
|
2017-07-24 19:58:03 +02:00
|
|
|
const initializeMiddlewares = require('./middlewares');
|
|
|
|
const initializeHooks = require('./hooks');
|
2019-04-10 18:11:55 +02:00
|
|
|
const createStrapiFs = require('./core/fs');
|
2019-04-11 16:19:15 +02:00
|
|
|
const getPrefixedDeps = require('./utils/get-prefixed-dependencies');
|
|
|
|
const defaultQueries = require('./core-api/queries');
|
2019-04-10 18:11:55 +02:00
|
|
|
|
2015-10-01 00:30:16 +02:00
|
|
|
/**
|
|
|
|
* Construct an Strapi instance.
|
|
|
|
*
|
|
|
|
* @constructor
|
|
|
|
*/
|
|
|
|
|
2016-07-26 11:57:50 +02:00
|
|
|
class Strapi extends EventEmitter {
|
2019-05-17 11:37:35 +02:00
|
|
|
constructor({ dir, autoReload = false } = {}) {
|
2016-07-06 15:51:52 +02:00
|
|
|
super();
|
|
|
|
|
2018-01-08 14:17:10 +01:00
|
|
|
this.setMaxListeners(100);
|
2016-07-06 15:51:52 +02:00
|
|
|
|
2017-08-02 13:17:40 +02:00
|
|
|
this.reload = this.reload();
|
|
|
|
|
2016-07-06 15:51:52 +02:00
|
|
|
// Expose `koa`.
|
2016-11-04 16:00:19 +01:00
|
|
|
this.app = new Koa();
|
2016-07-06 15:51:52 +02:00
|
|
|
|
|
|
|
// Mount the HTTP server.
|
2017-07-31 11:35:57 +02:00
|
|
|
this.server = http.createServer(this.app.callback());
|
|
|
|
|
2017-08-10 15:38:08 +02:00
|
|
|
// Logger.
|
|
|
|
this.log = logger;
|
|
|
|
|
2017-09-12 17:58:31 +02:00
|
|
|
// Utils.
|
|
|
|
this.utils = {
|
2018-05-18 14:22:24 +02:00
|
|
|
models,
|
2017-09-12 17:58:31 +02:00
|
|
|
};
|
|
|
|
|
2017-07-31 11:35:57 +02:00
|
|
|
// Exclude EventEmitter, Koa and HTTP server to be freezed.
|
|
|
|
this.propertiesToNotFreeze = Object.keys(this);
|
|
|
|
|
2017-07-28 18:34:13 +02:00
|
|
|
// Expose `admin`.
|
|
|
|
this.admin = {};
|
|
|
|
|
|
|
|
// Expose `plugin`.
|
|
|
|
this.plugins = {};
|
|
|
|
|
2019-05-17 11:37:35 +02:00
|
|
|
this.dir = dir || process.cwd();
|
2019-05-02 17:51:58 +02:00
|
|
|
const pkgJSON = require(path.resolve(this.dir, 'package.json'));
|
2019-04-11 10:34:01 +02:00
|
|
|
|
2017-07-24 19:58:03 +02:00
|
|
|
// Default configurations.
|
|
|
|
this.config = {
|
2017-07-27 16:51:29 +02:00
|
|
|
launchedAt: Date.now(),
|
2019-05-02 17:51:58 +02:00
|
|
|
appPath: this.dir,
|
2019-05-13 17:32:52 +02:00
|
|
|
autoReload,
|
2017-07-24 19:58:03 +02:00
|
|
|
host: process.env.HOST || process.env.HOSTNAME || 'localhost',
|
|
|
|
port: process.env.PORT || 1337,
|
2019-04-05 16:11:09 +02:00
|
|
|
environment: _.toLower(process.env.NODE_ENV) || 'development',
|
2017-07-25 17:12:18 +02:00
|
|
|
environments: {},
|
2018-09-13 16:44:42 +02:00
|
|
|
admin: {},
|
2017-07-24 19:58:03 +02:00
|
|
|
paths: {
|
2017-07-25 17:12:18 +02:00
|
|
|
admin: 'admin',
|
2017-07-24 19:58:03 +02:00
|
|
|
api: 'api',
|
2017-07-25 17:12:18 +02:00
|
|
|
config: 'config',
|
2017-07-24 19:58:03 +02:00
|
|
|
controllers: 'controllers',
|
|
|
|
models: 'models',
|
|
|
|
plugins: 'plugins',
|
2017-07-25 17:12:18 +02:00
|
|
|
policies: 'policies',
|
|
|
|
tmp: '.tmp',
|
|
|
|
services: 'services',
|
|
|
|
static: 'public',
|
2017-07-24 19:58:03 +02:00
|
|
|
validators: 'validators',
|
2018-05-18 14:22:24 +02:00
|
|
|
views: 'views',
|
2017-07-25 17:12:18 +02:00
|
|
|
},
|
2017-07-26 18:53:48 +02:00
|
|
|
middleware: {},
|
|
|
|
hook: {},
|
2017-07-28 18:34:13 +02:00
|
|
|
functions: {},
|
2018-05-18 14:22:24 +02:00
|
|
|
routes: {},
|
2019-04-11 10:34:01 +02:00
|
|
|
info: pkgJSON,
|
|
|
|
installedPlugins: getPrefixedDeps('strapi-plugin', pkgJSON),
|
|
|
|
installedMiddlewares: getPrefixedDeps('strapi-middleware', pkgJSON),
|
|
|
|
installedHooks: getPrefixedDeps('strapi-hook', pkgJSON),
|
2017-07-24 19:58:03 +02:00
|
|
|
};
|
2019-04-10 18:11:55 +02:00
|
|
|
|
2019-05-31 12:20:43 +02:00
|
|
|
this.groupManager = new UniqueWritetMap('Group');
|
|
|
|
this.serviceManager = new UniqueWritetMap('Service');
|
2019-04-10 18:11:55 +02:00
|
|
|
this.fs = createStrapiFs(this);
|
2019-06-19 08:45:10 +02:00
|
|
|
this.requireProjectBootstrap();
|
2016-07-06 15:51:52 +02:00
|
|
|
}
|
2017-02-01 14:48:30 +01:00
|
|
|
|
2019-06-14 18:13:25 +02:00
|
|
|
requireProjectBootstrap() {
|
|
|
|
const bootstrapPath = path.resolve(
|
|
|
|
this.dir,
|
|
|
|
'config/functions/bootstrap.js'
|
|
|
|
);
|
|
|
|
|
|
|
|
if (fse.existsSync(bootstrapPath)) {
|
|
|
|
require(bootstrapPath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-26 16:19:08 +02:00
|
|
|
async start(cb) {
|
2017-07-24 19:58:03 +02:00
|
|
|
try {
|
2018-03-16 11:42:17 +01:00
|
|
|
// Emit starting event.
|
|
|
|
this.emit('server:starting');
|
2019-04-30 14:47:49 +02:00
|
|
|
|
2017-07-24 19:58:03 +02:00
|
|
|
await this.load();
|
2017-07-25 17:12:18 +02:00
|
|
|
// Run bootstrap function.
|
2019-04-09 15:29:17 +02:00
|
|
|
await this.runBootstrapFunctions();
|
2017-07-31 11:35:57 +02:00
|
|
|
// Freeze object.
|
|
|
|
await this.freeze();
|
2018-11-08 21:07:33 +01:00
|
|
|
// Init first start
|
2019-04-30 14:47:49 +02:00
|
|
|
utils.init(this.config);
|
|
|
|
|
2017-07-25 17:12:18 +02:00
|
|
|
// Launch server.
|
2019-04-09 12:09:03 +02:00
|
|
|
this.server.listen(this.config.port, async err => {
|
2019-04-05 16:11:09 +02:00
|
|
|
if (err) return this.stopWithError(err);
|
2017-07-24 19:58:03 +02:00
|
|
|
|
2018-06-22 16:28:54 +02:00
|
|
|
this.log.info('Time: ' + new Date());
|
2019-04-05 16:11:09 +02:00
|
|
|
this.log.info(
|
|
|
|
'Launched in: ' + (Date.now() - this.config.launchedAt) + ' ms'
|
|
|
|
);
|
2018-06-22 16:28:54 +02:00
|
|
|
this.log.info('Environment: ' + this.config.environment);
|
|
|
|
this.log.info('Process PID: ' + process.pid);
|
2019-04-05 16:11:09 +02:00
|
|
|
this.log.info(
|
|
|
|
`Version: ${this.config.info.strapi} (node v${this.config.info.node})`
|
|
|
|
);
|
2017-07-27 13:06:05 +02:00
|
|
|
this.log.info('To shut down your server, press <CTRL> + C at any time');
|
2018-06-22 16:28:54 +02:00
|
|
|
console.log();
|
2018-09-17 15:50:13 +08:00
|
|
|
this.log.info(`☄️ Admin panel: ${this.config.admin.url}`);
|
2018-06-22 16:28:54 +02:00
|
|
|
this.log.info(`⚡️ Server: ${this.config.url}`);
|
|
|
|
console.log();
|
2017-07-25 17:12:18 +02:00
|
|
|
|
2018-03-16 11:42:17 +01:00
|
|
|
// Emit started event.
|
|
|
|
this.emit('server:started');
|
|
|
|
|
2017-07-25 17:12:18 +02:00
|
|
|
if (cb && typeof cb === 'function') {
|
|
|
|
cb();
|
|
|
|
}
|
2018-09-17 15:50:13 +08:00
|
|
|
|
2019-04-09 12:09:03 +02:00
|
|
|
if (
|
|
|
|
(this.config.environment === 'development' &&
|
2019-04-05 16:11:09 +02:00
|
|
|
_.get(
|
|
|
|
this.config.currentEnvironment,
|
|
|
|
'server.admin.autoOpen',
|
|
|
|
true
|
|
|
|
) !== false) ||
|
2019-04-09 12:09:03 +02:00
|
|
|
this.config.init
|
|
|
|
) {
|
2018-09-17 15:50:13 +08:00
|
|
|
await utils.openBrowser.call(this);
|
|
|
|
}
|
2017-07-24 19:58:03 +02:00
|
|
|
});
|
2018-03-16 11:42:17 +01:00
|
|
|
} catch (err) {
|
2019-04-05 16:11:09 +02:00
|
|
|
this.stopWithError(err);
|
2017-07-24 19:58:03 +02:00
|
|
|
}
|
2016-07-26 11:57:50 +02:00
|
|
|
}
|
|
|
|
|
2019-04-05 16:11:09 +02:00
|
|
|
/**
|
|
|
|
* Add behaviors to the server
|
|
|
|
*/
|
2017-07-25 17:12:18 +02:00
|
|
|
async enhancer() {
|
2019-04-05 16:11:09 +02:00
|
|
|
// handle port in use cleanly
|
|
|
|
this.server.on('error', err => {
|
|
|
|
if (err.code === 'EADDRINUSE') {
|
|
|
|
return this.stopWithError(
|
|
|
|
`The port ${err.port} is already used by another application.`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.log.error(err);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Close current connections to fully destroy the server
|
2017-07-25 17:12:18 +02:00
|
|
|
const connections = {};
|
2017-07-28 16:14:50 +02:00
|
|
|
|
2017-07-25 17:12:18 +02:00
|
|
|
this.server.on('connection', conn => {
|
|
|
|
const key = conn.remoteAddress + ':' + conn.remotePort;
|
|
|
|
connections[key] = conn;
|
2016-07-26 11:57:50 +02:00
|
|
|
|
2019-04-09 12:09:03 +02:00
|
|
|
conn.on('close', function() {
|
2018-05-04 17:02:27 +02:00
|
|
|
delete connections[key];
|
2017-07-25 17:12:18 +02:00
|
|
|
});
|
|
|
|
});
|
2016-07-26 11:57:50 +02:00
|
|
|
|
2017-07-25 17:12:18 +02:00
|
|
|
this.server.destroy = cb => {
|
|
|
|
this.server.close(cb);
|
2016-07-26 11:57:50 +02:00
|
|
|
|
2017-07-25 17:12:18 +02:00
|
|
|
for (let key in connections) {
|
|
|
|
connections[key].destroy();
|
2018-05-04 17:02:27 +02:00
|
|
|
}
|
2017-07-25 17:12:18 +02:00
|
|
|
};
|
2016-07-26 11:57:50 +02:00
|
|
|
}
|
|
|
|
|
2019-04-05 16:11:09 +02:00
|
|
|
stopWithError(err) {
|
|
|
|
this.log.debug(`⛔️ Server wasn't able to start properly.`);
|
|
|
|
this.log.error(err);
|
|
|
|
return this.stop();
|
|
|
|
}
|
|
|
|
|
2016-07-26 11:57:50 +02:00
|
|
|
stop() {
|
2017-07-25 17:12:18 +02:00
|
|
|
// Destroy server and available connections.
|
2017-07-24 19:58:03 +02:00
|
|
|
this.server.destroy();
|
2017-09-04 15:38:29 +02:00
|
|
|
|
2019-05-13 17:32:52 +02:00
|
|
|
if (this.config.autoReload) {
|
2017-10-02 14:17:44 +02:00
|
|
|
process.send('stop');
|
|
|
|
}
|
2017-09-04 15:38:29 +02:00
|
|
|
|
2017-07-25 17:12:18 +02:00
|
|
|
// Kill process.
|
2019-03-12 11:41:38 +01:00
|
|
|
process.exit(1);
|
2016-07-26 11:57:50 +02:00
|
|
|
}
|
|
|
|
|
2017-07-24 19:58:03 +02:00
|
|
|
async load() {
|
2018-04-25 14:33:38 +02:00
|
|
|
await this.enhancer();
|
|
|
|
|
2017-10-02 14:17:44 +02:00
|
|
|
this.app.use(async (ctx, next) => {
|
2017-09-07 16:35:12 +02:00
|
|
|
if (ctx.request.url === '/_health' && ctx.request.method === 'HEAD') {
|
2018-03-14 13:18:05 +01:00
|
|
|
ctx.set('strapi', 'You are so French!');
|
2017-10-02 14:17:44 +02:00
|
|
|
ctx.status = 204;
|
2017-09-06 11:06:18 +02:00
|
|
|
} else {
|
|
|
|
await next();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-05-02 17:51:58 +02:00
|
|
|
const [
|
|
|
|
config,
|
|
|
|
api,
|
|
|
|
admin,
|
|
|
|
plugins,
|
2019-06-08 18:50:07 +02:00
|
|
|
middlewares,
|
2019-05-02 17:51:58 +02:00
|
|
|
hook,
|
|
|
|
extensions,
|
|
|
|
] = await Promise.all([
|
|
|
|
loadConfig(this),
|
|
|
|
loadApis(this),
|
|
|
|
loadAdmin(this),
|
|
|
|
loadPlugins(this),
|
|
|
|
loadMiddlewares(this),
|
|
|
|
loadHooks(this.config),
|
|
|
|
loadExtensions(this.config),
|
|
|
|
]);
|
|
|
|
|
|
|
|
_.merge(this.config, config);
|
|
|
|
|
|
|
|
this.api = api;
|
|
|
|
this.admin = admin;
|
|
|
|
this.plugins = plugins;
|
2019-04-05 16:11:09 +02:00
|
|
|
this.middleware = middlewares;
|
2019-05-02 17:51:58 +02:00
|
|
|
this.hook = hook;
|
2017-07-24 19:58:03 +02:00
|
|
|
|
2019-04-10 18:11:55 +02:00
|
|
|
/**
|
|
|
|
* Handle plugin extensions
|
|
|
|
*/
|
|
|
|
// merge extensions config folders
|
2019-06-14 23:07:04 +02:00
|
|
|
_.mergeWith(this.plugins, extensions.merges, (objValue, srcValue, key) => {
|
|
|
|
// concat routes
|
|
|
|
if (_.isArray(srcValue) && _.isArray(objValue) && key === 'routes') {
|
2019-04-23 10:28:40 +02:00
|
|
|
return srcValue.concat(objValue);
|
2019-04-18 17:11:20 +02:00
|
|
|
}
|
|
|
|
});
|
2019-04-10 18:11:55 +02:00
|
|
|
// overwrite plugins with extensions overwrites
|
|
|
|
extensions.overwrites.forEach(({ path, mod }) =>
|
2019-04-11 09:32:16 +02:00
|
|
|
_.set(this.plugins, path, mod)
|
2019-04-10 18:11:55 +02:00
|
|
|
);
|
|
|
|
|
2017-07-25 17:12:18 +02:00
|
|
|
// Populate AST with configurations.
|
2019-05-02 17:51:58 +02:00
|
|
|
|
2019-04-11 09:32:16 +02:00
|
|
|
await bootstrap(this);
|
2019-05-02 17:51:58 +02:00
|
|
|
|
2019-05-31 12:20:43 +02:00
|
|
|
// init service manager
|
|
|
|
this.initServices();
|
|
|
|
this.initGroups();
|
|
|
|
|
2018-03-14 13:18:05 +01:00
|
|
|
// Usage.
|
2019-04-05 16:11:09 +02:00
|
|
|
await utils.usage(this.config);
|
2019-05-02 17:51:58 +02:00
|
|
|
|
2018-04-24 12:30:43 +02:00
|
|
|
// Init core store
|
2019-04-09 21:51:28 +02:00
|
|
|
initCoreStore(this);
|
2019-05-02 17:51:58 +02:00
|
|
|
|
2017-07-25 17:12:18 +02:00
|
|
|
// Initialize hooks and middlewares.
|
2019-05-02 17:51:58 +02:00
|
|
|
|
2019-04-05 16:11:09 +02:00
|
|
|
await Promise.all([
|
|
|
|
initializeMiddlewares.call(this),
|
|
|
|
initializeHooks.call(this),
|
2019-06-08 18:50:07 +02:00
|
|
|
]).catch(err => {
|
|
|
|
console.error(err);
|
|
|
|
throw err;
|
|
|
|
});
|
2016-07-26 11:57:50 +02:00
|
|
|
}
|
|
|
|
|
2017-08-02 11:25:18 +02:00
|
|
|
reload() {
|
2018-03-28 20:13:09 +02:00
|
|
|
const state = {
|
2019-04-09 12:09:03 +02:00
|
|
|
shouldReload: 0,
|
2018-03-28 20:13:09 +02:00
|
|
|
};
|
|
|
|
|
2019-04-09 12:09:03 +02:00
|
|
|
const reload = function() {
|
2018-08-31 13:47:10 +02:00
|
|
|
if (state.shouldReload > 0) {
|
2018-07-15 15:51:30 +02:00
|
|
|
// Reset the reloading state
|
2018-08-31 13:47:10 +02:00
|
|
|
state.shouldReload -= 1;
|
2018-07-15 15:51:30 +02:00
|
|
|
reload.isReloading = false;
|
2018-03-28 20:13:09 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-05-13 17:32:52 +02:00
|
|
|
if (this.config.autoReload) {
|
2019-03-11 10:42:43 +01:00
|
|
|
this.server.close();
|
2018-01-04 16:03:34 +01:00
|
|
|
process.send('reload');
|
|
|
|
}
|
2017-08-02 11:25:18 +02:00
|
|
|
};
|
|
|
|
|
2018-03-28 20:13:09 +02:00
|
|
|
Object.defineProperty(reload, 'isWatching', {
|
|
|
|
configurable: true,
|
|
|
|
enumerable: true,
|
2018-05-18 14:22:24 +02:00
|
|
|
set: value => {
|
2018-03-28 20:13:09 +02:00
|
|
|
// Special state when the reloader is disabled temporarly (see GraphQL plugin example).
|
2018-08-31 13:47:10 +02:00
|
|
|
if (state.isWatching === false && value === true) {
|
|
|
|
state.shouldReload += 1;
|
|
|
|
}
|
2018-03-28 20:13:09 +02:00
|
|
|
state.isWatching = value;
|
2018-05-02 22:16:13 +02:00
|
|
|
},
|
|
|
|
get: () => {
|
|
|
|
return state.isWatching;
|
2018-05-18 14:22:24 +02:00
|
|
|
},
|
2018-03-28 20:13:09 +02:00
|
|
|
});
|
|
|
|
|
2017-08-02 11:25:18 +02:00
|
|
|
reload.isReloading = false;
|
|
|
|
reload.isWatching = true;
|
|
|
|
|
|
|
|
return reload;
|
2016-07-26 11:57:50 +02:00
|
|
|
}
|
|
|
|
|
2019-04-09 15:29:17 +02:00
|
|
|
async runBootstrapFunctions() {
|
2019-04-05 16:11:09 +02:00
|
|
|
const execBootstrap = fn => {
|
|
|
|
if (!fn) return Promise.resolve();
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
const timeoutMs = this.config.bootstrapTimeout || 3500;
|
|
|
|
const timer = setTimeout(() => {
|
|
|
|
this.log.warn(
|
|
|
|
`The bootstrap function is taking unusually long to execute (${timeoutMs} miliseconds).`
|
|
|
|
);
|
|
|
|
this.log.warn('Make sure you call it?');
|
|
|
|
}, timeoutMs);
|
|
|
|
|
|
|
|
let ranBootstrapFn = false;
|
|
|
|
|
|
|
|
try {
|
|
|
|
fn(err => {
|
|
|
|
if (ranBootstrapFn) {
|
|
|
|
this.log.error(
|
|
|
|
'You called the callback in `strapi.config.boostrap` more than once!'
|
|
|
|
);
|
|
|
|
|
|
|
|
return reject();
|
2018-05-18 14:22:24 +02:00
|
|
|
}
|
2019-04-05 16:11:09 +02:00
|
|
|
|
|
|
|
ranBootstrapFn = true;
|
|
|
|
clearTimeout(timer);
|
|
|
|
|
|
|
|
return resolve(err);
|
2019-04-09 12:09:03 +02:00
|
|
|
});
|
2019-04-05 16:11:09 +02:00
|
|
|
} catch (e) {
|
|
|
|
if (ranBootstrapFn) {
|
|
|
|
this.log.error(
|
|
|
|
'The bootstrap function threw an error after its callback was called.'
|
|
|
|
);
|
2018-05-18 14:22:24 +02:00
|
|
|
|
2019-04-05 16:11:09 +02:00
|
|
|
return reject(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
ranBootstrapFn = true;
|
|
|
|
clearTimeout(timer);
|
|
|
|
|
|
|
|
return resolve(e);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
return Promise.all(
|
|
|
|
Object.values(this.plugins).map(plugin =>
|
|
|
|
execBootstrap(_.get(plugin, 'config.functions.bootstrap'))
|
|
|
|
)
|
|
|
|
).then(() => execBootstrap(this.config.functions.bootstrap));
|
2016-07-26 11:57:50 +02:00
|
|
|
}
|
|
|
|
|
2017-07-31 11:35:57 +02:00
|
|
|
async freeze() {
|
|
|
|
const propertiesToNotFreeze = this.propertiesToNotFreeze || [];
|
|
|
|
|
|
|
|
// Remove object from tree.
|
|
|
|
delete this.propertiesToNotFreeze;
|
|
|
|
|
2018-05-18 14:22:24 +02:00
|
|
|
return Object.keys(this)
|
2019-04-05 16:11:09 +02:00
|
|
|
.filter(x => !_.includes(propertiesToNotFreeze, x))
|
2018-05-18 14:22:24 +02:00
|
|
|
.forEach(key => {
|
|
|
|
Object.freeze(this[key]);
|
|
|
|
});
|
2016-07-26 11:57:50 +02:00
|
|
|
}
|
2017-08-29 19:34:34 +02:00
|
|
|
|
2019-05-16 21:37:45 +02:00
|
|
|
/**
|
|
|
|
* Binds queries with a specific model
|
|
|
|
* @param {string} entity - entity name
|
|
|
|
* @param {string} plugin - plugin name or null
|
|
|
|
* @param {Object} queriesMap - a map of orm to queries object factory (defaults to ./core-api/queries)
|
|
|
|
*/
|
2019-04-26 10:17:04 +02:00
|
|
|
query(entity, plugin, queriesMap = defaultQueries) {
|
2017-08-29 19:34:34 +02:00
|
|
|
if (!entity) {
|
2019-04-11 16:19:15 +02:00
|
|
|
throw new Error(
|
2019-04-05 16:11:09 +02:00
|
|
|
`You can't call the query method without passing the model's name as a first argument.`
|
|
|
|
);
|
2017-08-29 19:34:34 +02:00
|
|
|
}
|
|
|
|
|
2019-05-21 16:18:18 +02:00
|
|
|
const modelKey = entity.toLowerCase();
|
2017-08-29 19:34:34 +02:00
|
|
|
|
2019-04-11 16:19:15 +02:00
|
|
|
const model =
|
|
|
|
plugin === 'admin'
|
2019-05-21 16:18:18 +02:00
|
|
|
? _.get(strapi.admin, ['models', modelKey], undefined)
|
|
|
|
: _.get(strapi.plugins, [plugin, 'models', modelKey]) ||
|
|
|
|
_.get(strapi, ['models', modelKey]) ||
|
2019-04-11 16:19:15 +02:00
|
|
|
undefined;
|
2017-11-15 16:59:12 +01:00
|
|
|
|
2019-04-11 16:19:15 +02:00
|
|
|
if (!model) {
|
2019-05-21 16:18:18 +02:00
|
|
|
throw new Error(`The model ${modelKey} can't be found.`);
|
2017-08-29 19:34:34 +02:00
|
|
|
}
|
|
|
|
|
2019-04-11 16:19:15 +02:00
|
|
|
const connector = model.orm;
|
2017-08-29 19:34:34 +02:00
|
|
|
|
|
|
|
if (!connector) {
|
2019-04-11 16:19:15 +02:00
|
|
|
throw new Error(
|
2019-05-21 16:18:18 +02:00
|
|
|
`Impossible to determine the use ORM for the model ${modelKey}.`
|
2019-04-05 16:11:09 +02:00
|
|
|
);
|
2017-08-29 19:34:34 +02:00
|
|
|
}
|
|
|
|
|
2019-04-26 10:17:04 +02:00
|
|
|
let buildQueries = queriesMap[connector];
|
2019-05-21 16:18:18 +02:00
|
|
|
let queries = buildQueries({ model, modelKey, strapi: this });
|
2017-08-29 19:34:34 +02:00
|
|
|
|
2019-04-11 16:19:15 +02:00
|
|
|
return Object.assign(queries, {
|
|
|
|
orm: connector,
|
|
|
|
primaryKey: model.primaryKey,
|
|
|
|
associations: model.associations,
|
|
|
|
});
|
2017-08-29 19:34:34 +02:00
|
|
|
}
|
2019-05-31 12:20:43 +02:00
|
|
|
|
|
|
|
initServices() {
|
|
|
|
this.serviceManager.clear();
|
|
|
|
|
|
|
|
Object.entries(this.plugins).forEach(([pluginKey, plugin]) => {
|
|
|
|
if (!plugin.services) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Object.entries(plugin.services).forEach(([serviceKey, service]) => {
|
|
|
|
this.serviceManager.set(`${pluginKey}.${serviceKey}`, service);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
if (this.admin.services) {
|
|
|
|
Object.entries(this.admin.services).forEach(([serviceKey, service]) => {
|
|
|
|
this.serviceManager.set(`admin.${serviceKey}`, service);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Object.entries(this.api).forEach(([apiKey, api]) => {
|
|
|
|
if (!api.services) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Object.entries(api.services).forEach(([serviceKey, service]) => {
|
|
|
|
this.serviceManager.set(`${apiKey}.${serviceKey}`, service);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
service(key) {
|
|
|
|
return this.serviceManager.get(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
initGroups() {
|
|
|
|
this.groupManager.clear();
|
|
|
|
|
|
|
|
Object.entries(this.api).forEach(([apiKey, api]) => {
|
|
|
|
if (!api.groups) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Object.entries(api.groups).forEach(([groupKey, group]) => {
|
|
|
|
this.groupManager.set(`${apiKey}.${groupKey}`, group);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2016-07-26 11:57:50 +02:00
|
|
|
}
|
|
|
|
|
2019-04-11 09:32:16 +02:00
|
|
|
module.exports = options => {
|
|
|
|
const strapi = new Strapi(options);
|
|
|
|
global.strapi = strapi;
|
|
|
|
return strapi;
|
|
|
|
};
|
2019-05-31 12:20:43 +02:00
|
|
|
|
|
|
|
class UniqueWritetMap extends Map {
|
|
|
|
constructor(mapName) {
|
|
|
|
super();
|
|
|
|
this.mapName = mapName;
|
|
|
|
}
|
|
|
|
|
|
|
|
get(key) {
|
|
|
|
if (!this.has(key)) {
|
|
|
|
throw new Error(`${this.mapName} ${key} not found`);
|
|
|
|
}
|
|
|
|
|
|
|
|
return super.get(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
set(key, value) {
|
|
|
|
if (this.has(key)) {
|
|
|
|
throw new Error(
|
|
|
|
`${
|
|
|
|
this.mapName
|
|
|
|
} ${key} already exists. Make sure you don't have conflicts with your installed plugins`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
super.set(key, value);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
}
|