Ghost/core/server/services/themes/storage.js

153 lines
5.0 KiB
JavaScript
Raw Normal View History

const debug = require('@tryghost/debug')('themes');
const fs = require('fs-extra');
const ObjectID = require('bson-objectid');
const tpl = require('@tryghost/tpl');
const logging = require('@tryghost/logging');
const errors = require('@tryghost/errors');
const validate = require('./validate');
const list = require('./list');
const ThemeStorage = require('./ThemeStorage');
const themeLoader = require('./loader');
const activator = require('./activation-bridge');
const toJSON = require('./to-json');
const settingsCache = require('../../../shared/settings-cache');
const messages = {
themeDoesNotExist: 'Theme does not exist.',
invalidThemeName: 'Please select a valid theme.',
overrideCasper: 'Please rename your zip, it\'s not allowed to override the default casper theme.',
destroyCasper: 'Deleting the default casper theme is not allowed.',
destroyActive: 'Deleting the active theme is not allowed.'
};
let themeStorage;
const getStorage = () => {
themeStorage = themeStorage || new ThemeStorage();
return themeStorage;
};
module.exports = {
getZip: async (themeName) => {
const theme = list.get(themeName);
if (!theme) {
throw new errors.BadRequestError({
message: tpl(messages.invalidThemeName)
});
}
return await getStorage().serve({
name: themeName
});
},
setFromZip: async (zip) => {
const themeName = getStorage().getSanitizedFileName(zip.name.split('.zip')[0]);
const backupName = `${themeName}_${ObjectID()}`;
// check if zip name is casper.zip
if (zip.name === 'casper.zip') {
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.ValidationError({
message: tpl(messages.overrideCasper)
});
}
let checkedTheme;
let overrideTheme;
let renamedExisting = false;
try {
checkedTheme = await validate.checkSafe(themeName, zip, true);
const themeExists = await getStorage().exists(themeName);
// CASE: move the existing theme to a backup folder
if (themeExists) {
debug('setFromZip Theme exists already');
renamedExisting = true;
await getStorage().rename(themeName, backupName);
}
// CASE: store extracted theme
await getStorage().save({
name: themeName,
path: checkedTheme.path
});
// CASE: loads the theme from the fs & sets the theme on the themeList
const loadedTheme = await themeLoader.loadOneTheme(themeName);
overrideTheme = (themeName === settingsCache.get('active_theme'));
// CASE: if this is the active theme, we are overriding
if (overrideTheme) {
debug('setFromZip Theme is active already');
activator.activateFromAPIOverride(themeName, loadedTheme, checkedTheme);
}
// @TODO: unify the name across gscan and Ghost!
return {
themeOverridden: overrideTheme,
theme: toJSON(themeName, checkedTheme)
};
} catch (error) {
// restore backup if we renamed an existing theme but saving failed
if (renamedExisting) {
return getStorage().exists(themeName).then((themeExists) => {
if (!themeExists) {
return getStorage().rename(backupName, themeName).then(() => {
throw error;
});
}
throw error;
});
}
throw error;
} finally {
// @TODO: we should probably do this as part of saving the theme
// CASE: remove extracted dir from gscan happens in background
if (checkedTheme) {
fs.remove(checkedTheme.path)
.catch((err) => {
logging.error(new errors.GhostError({err: err}));
});
}
// CASE: remove the backup we created earlier
getStorage()
.delete(backupName)
.catch((err) => {
logging.error(new errors.GhostError({err: err}));
});
}
},
destroy: async function (themeName) {
if (themeName === 'casper') {
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.ValidationError({
message: tpl(messages.destroyCasper)
});
}
if (themeName === settingsCache.get('active_theme')) {
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.ValidationError({
message: tpl(messages.destroyActive)
});
}
const theme = list.get(themeName);
if (!theme) {
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({
message: tpl(messages.themeDoesNotExist)
});
}
let result = await getStorage().delete(themeName);
list.del(themeName);
return result;
}
};