Ghost/core/server/api/v2/themes.js
Nazar Gargol cb322fc1be Improved error context usage
refs #10571

- Change was done to avoid usage of hardcoded value and provide more context. Additionally errorDetails are formatted in a readable way
2019-04-23 09:45:11 +02:00

220 lines
6.9 KiB
JavaScript

const Promise = require('bluebird');
const fs = require('fs-extra');
const debug = require('ghost-ignition').debug('api:themes');
const common = require('../../lib/common');
const themeService = require('../../services/themes');
const settingsCache = require('../../services/settings/cache');
const models = require('../../models');
module.exports = {
docName: 'themes',
browse: {
permissions: true,
query() {
return themeService.toJSON();
}
},
activate: {
headers: {
cacheInvalidate: true
},
options: [
'name'
],
validation: {
options: {
name: {
required: true
}
}
},
permissions: true,
query(frame) {
let themeName = frame.options.name;
let checkedTheme;
const newSettings = [{
key: 'active_theme',
value: themeName
}];
const loadedTheme = themeService.list.get(themeName);
if (!loadedTheme) {
return Promise.reject(new common.errors.ValidationError({
message: common.i18n.t('notices.data.validation.index.themeCannotBeActivated', {themeName: themeName}),
errorDetails: newSettings
}));
}
return themeService.validate.checkSafe(loadedTheme)
.then((_checkedTheme) => {
checkedTheme = _checkedTheme;
// @NOTE: we use the model, not the API here, as we don't want to trigger permissions
return models.Settings.edit(newSettings, frame.options);
})
.then(() => {
debug('Activating theme (method B on API "activate")', themeName);
themeService.activate(loadedTheme, checkedTheme);
return themeService.toJSON(themeName, checkedTheme);
});
}
},
upload: {
headers: {},
permissions: {
method: 'add'
},
query(frame) {
// @NOTE: consistent filename uploads
frame.options.originalname = frame.file.originalname.toLowerCase();
let zip = {
path: frame.file.path,
name: frame.file.originalname,
shortName: themeService.storage.getSanitizedFileName(frame.file.originalname.split('.zip')[0])
};
let checkedTheme;
// check if zip name is casper.zip
if (zip.name === 'casper.zip') {
throw new common.errors.ValidationError({
message: common.i18n.t('errors.api.themes.overrideCasper')
});
}
return themeService.validate.checkSafe(zip, true)
.then((_checkedTheme) => {
checkedTheme = _checkedTheme;
return themeService.storage.exists(zip.shortName);
})
.then((themeExists) => {
// CASE: delete existing theme
if (themeExists) {
return themeService.storage.delete(zip.shortName);
}
})
.then(() => {
// CASE: store extracted theme
return themeService.storage.save({
name: zip.shortName,
path: checkedTheme.path
});
})
.then(() => {
// CASE: loads the theme from the fs & sets the theme on the themeList
return themeService.loadOne(zip.shortName);
})
.then((loadedTheme) => {
// CASE: if this is the active theme, we are overriding
if (zip.shortName === settingsCache.get('active_theme')) {
debug('Activating theme (method C, on API "override")', zip.shortName);
themeService.activate(loadedTheme, checkedTheme);
// CASE: clear cache
this.headers.cacheInvalidate = true;
}
common.events.emit('theme.uploaded');
// @TODO: unify the name across gscan and Ghost!
return themeService.toJSON(zip.shortName, checkedTheme);
})
.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) => {
common.logging.error(new common.errors.GhostError({err: err}));
});
}
});
}
},
download: {
options: [
'name'
],
validation: {
options: {
name: {
required: true
}
}
},
permissions: {
method: 'read'
},
query(frame) {
let themeName = frame.options.name;
const theme = themeService.list.get(themeName);
if (!theme) {
return Promise.reject(new common.errors.BadRequestError({
message: common.i18n.t('errors.api.themes.invalidThemeName')
}));
}
return themeService.storage.serve({
name: themeName
});
}
},
destroy: {
statusCode: 204,
headers: {
cacheInvalidate: true
},
options: [
'name'
],
validation: {
options: {
name: {
required: true
}
}
},
permissions: true,
query(frame) {
let themeName = frame.options.name;
if (themeName === 'casper') {
throw new common.errors.ValidationError({
message: common.i18n.t('errors.api.themes.destroyCasper')
});
}
if (themeName === settingsCache.get('active_theme')) {
throw new common.errors.ValidationError({
message: common.i18n.t('errors.api.themes.destroyActive')
});
}
const theme = themeService.list.get(themeName);
if (!theme) {
throw new common.errors.NotFoundError({
message: common.i18n.t('errors.api.themes.themeDoesNotExist')
});
}
return themeService.storage.delete(themeName)
.then(() => {
themeService.list.del(themeName);
});
}
}
};