Ghost/core/server/services/route-settings/route-settings.js

162 lines
4.7 KiB
JavaScript
Raw Normal View History

const Promise = require('bluebird');
const moment = require('moment-timezone');
const fs = require('fs-extra');
const path = require('path');
const urlService = require('../../../frontend/services/url');
const debug = require('@tryghost/debug')('services:route-settings');
Refactored `common` lib import to use destructuring (#11835) * refactored `core/frontend/apps` to destructure common imports * refactored `core/frontend/services/{apps, redirects, routing}` to destructure common imports * refactored `core/frontend/services/settings` to destructure common imports * refactored remaining `core/frontend/services` to destructure common imports * refactored `core/server/adapters` to destructure common imports * refactored `core/server/data/{db, exporter, schema, validation}` to destructure common imports * refactored `core/server/data/importer` to destructure common imports * refactored `core/server/models/{base, plugins, relations}` to destructure common imports * refactored remaining `core/server/models` to destructure common imports * refactored `core/server/api/canary/utils/serializers/output` to destructure common imports * refactored remaining `core/server/api/canary/utils` to destructure common imports * refactored remaining `core/server/api/canary` to destructure common imports * refactored `core/server/api/shared` to destructure common imports * refactored `core/server/api/v2/utils` to destructure common imports * refactored remaining `core/server/api/v2` to destructure common imports * refactored `core/frontend/meta` to destructure common imports * fixed some tests referencing `common.errors` instead of `@tryghost/errors` - Not all of them need to be updated; only updating the ones that are causing failures * fixed errors import being shadowed by local scope
2020-05-22 21:22:20 +03:00
const errors = require('@tryghost/errors');
const tpl = require('@tryghost/tpl');
const config = require('../../../shared/config');
const bridge = require('../../../bridge');
const ensureSettingsFile = require('./ensure-settings');
const SettingsLoader = require('./loader');
const messages = {
loadError: 'Could not load {filename} file.'
};
/**
* The `routes.yaml` file offers a way to configure your Ghost blog. It's currently a setting feature
* we have added. That's why the `routes.yaml` file is treated as a "setting" right now.
* If we want to add single permissions for this file (e.g. upload/download routes.yaml), we can add later.
*
* How does it work?
*
* - we first reset all url generators (each url generator belongs to one express router)
* - we don't destroy the resources, we only release them (this avoids reloading all resources from the db again)
* - then we reload the whole site app, which will reset all routers and re-create the url generators
*/
const filename = 'routes';
const ext = 'yaml';
const getSettingsFolder = async () => {
return config.getContentPath('settings');
};
const getSettingsFilePath = async () => {
const settingsFolder = await getSettingsFolder();
return path.join(settingsFolder, `${filename}.${ext}`);
};
const getBackupFilePath = async () => {
const settingsFolder = await getSettingsFolder();
return path.join(settingsFolder, `${filename}-${moment().format('YYYY-MM-DD-HH-mm-ss')}.${ext}`);
};
const createBackupFile = async (settingsPath, backupPath) => {
return await fs.copy(settingsPath, backupPath);
};
const restoreBackupFile = async (settingsPath, backupPath) => {
return await fs.copy(backupPath, settingsPath);
};
const saveFile = async (filePath, settingsPath) => {
return await fs.copy(filePath, settingsPath);
};
const readFile = (settingsFilePath) => {
return fs.readFile(settingsFilePath, 'utf-8')
.catch((err) => {
if (err.code === 'ENOENT') {
return Promise.resolve([]);
}
Refactored `common` lib import to use destructuring (#11835) * refactored `core/frontend/apps` to destructure common imports * refactored `core/frontend/services/{apps, redirects, routing}` to destructure common imports * refactored `core/frontend/services/settings` to destructure common imports * refactored remaining `core/frontend/services` to destructure common imports * refactored `core/server/adapters` to destructure common imports * refactored `core/server/data/{db, exporter, schema, validation}` to destructure common imports * refactored `core/server/data/importer` to destructure common imports * refactored `core/server/models/{base, plugins, relations}` to destructure common imports * refactored remaining `core/server/models` to destructure common imports * refactored `core/server/api/canary/utils/serializers/output` to destructure common imports * refactored remaining `core/server/api/canary/utils` to destructure common imports * refactored remaining `core/server/api/canary` to destructure common imports * refactored `core/server/api/shared` to destructure common imports * refactored `core/server/api/v2/utils` to destructure common imports * refactored remaining `core/server/api/v2` to destructure common imports * refactored `core/frontend/meta` to destructure common imports * fixed some tests referencing `common.errors` instead of `@tryghost/errors` - Not all of them need to be updated; only updating the ones that are causing failures * fixed errors import being shadowed by local scope
2020-05-22 21:22:20 +03:00
if (errors.utils.isIgnitionError(err)) {
throw err;
}
Refactored `common` lib import to use destructuring (#11835) * refactored `core/frontend/apps` to destructure common imports * refactored `core/frontend/services/{apps, redirects, routing}` to destructure common imports * refactored `core/frontend/services/settings` to destructure common imports * refactored remaining `core/frontend/services` to destructure common imports * refactored `core/server/adapters` to destructure common imports * refactored `core/server/data/{db, exporter, schema, validation}` to destructure common imports * refactored `core/server/data/importer` to destructure common imports * refactored `core/server/models/{base, plugins, relations}` to destructure common imports * refactored remaining `core/server/models` to destructure common imports * refactored `core/server/api/canary/utils/serializers/output` to destructure common imports * refactored remaining `core/server/api/canary/utils` to destructure common imports * refactored remaining `core/server/api/canary` to destructure common imports * refactored `core/server/api/shared` to destructure common imports * refactored `core/server/api/v2/utils` to destructure common imports * refactored remaining `core/server/api/v2` to destructure common imports * refactored `core/frontend/meta` to destructure common imports * fixed some tests referencing `common.errors` instead of `@tryghost/errors` - Not all of them need to be updated; only updating the ones that are causing failures * fixed errors import being shadowed by local scope
2020-05-22 21:22:20 +03:00
throw new errors.NotFoundError({
err: err
});
});
};
const setFromFilePath = async (filePath) => {
const settingsPath = await getSettingsFilePath();
const backupPath = await getBackupFilePath();
await createBackupFile(settingsPath, backupPath);
await saveFile(filePath, settingsPath);
urlService.resetGenerators({releaseResourcesOnly: true});
const bringBackValidRoutes = async () => {
urlService.resetGenerators({releaseResourcesOnly: true});
await restoreBackupFile(settingsPath, backupPath);
return bridge.reloadFrontend();
};
try {
bridge.reloadFrontend();
} catch (err) {
return bringBackValidRoutes()
.finally(() => {
throw err;
});
}
// @TODO: how can we get rid of this from here?
let tries = 0;
function isBlogRunning() {
debug('waiting for blog running');
return Promise.delay(1000)
.then(() => {
debug('waited for blog running');
if (!urlService.hasFinished()) {
if (tries > 5) {
throw new errors.InternalServerError({
message: tpl(messages.loadError, {filename: `${filename}.${ext}`})
});
}
tries = tries + 1;
return isBlogRunning();
}
});
}
return isBlogRunning()
.catch((err) => {
return bringBackValidRoutes()
.finally(() => {
throw err;
});
});
};
const get = async () => {
const settingsFilePath = await getSettingsFilePath();
return readFile(settingsFilePath);
};
const init = function () {
debug('init routes settings service');
// Make sure that supported settings files are available
// inside of the `content/setting` directory
return ensureSettingsFile('routes.yaml');
};
module.exports.init = init;
/**
* Fetches routes YAML settings synchronously.
* will return a JSON Object like this:
* {routes: {}, collections: {}, resources: {}}
* @returns {Object} routes.yaml in JSON format
*/
module.exports.loadRouteSettingsSync = () => {
return SettingsLoader.loadSettingsSync();
};
module.exports.api = {
setFromFilePath: setFromFilePath,
get: get
};