mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-22 10:21:36 +03:00
aaa54c603c
refs https://github.com/TryGhost/Team/issues/555 - Previous blocklist approach was resulting in adding every single new table into an export automatically. Which creates possibility to leak sensitive data if not used porperly. Allowlist approach gives better control over what is exported, makes this information explicit, and version-control friendlier
132 lines
4.2 KiB
JavaScript
132 lines
4.2 KiB
JavaScript
const Promise = require('bluebird');
|
|
const dbBackup = require('../../data/db/backup');
|
|
const exporter = require('../../data/exporter');
|
|
const importer = require('../../data/importer');
|
|
const errors = require('@tryghost/errors');
|
|
const models = require('../../models');
|
|
|
|
module.exports = {
|
|
docName: 'db',
|
|
|
|
backupContent: {
|
|
permissions: true,
|
|
options: [
|
|
'include',
|
|
'filename'
|
|
],
|
|
validation: {
|
|
options: {
|
|
include: {
|
|
values: exporter.BACKUP_TABLES
|
|
}
|
|
}
|
|
},
|
|
query(frame) {
|
|
// NOTE: we need to have `include` property available as backupDatabase uses it internally
|
|
Object.assign(frame.options, {include: frame.options.withRelated});
|
|
|
|
return dbBackup.backup(frame.options);
|
|
}
|
|
},
|
|
|
|
exportContent: {
|
|
options: [
|
|
'include',
|
|
'filename'
|
|
],
|
|
validation: {
|
|
options: {
|
|
include: {
|
|
values: exporter.BACKUP_TABLES
|
|
}
|
|
}
|
|
},
|
|
headers: {
|
|
disposition: {
|
|
type: 'file',
|
|
value: () => (exporter.fileName())
|
|
}
|
|
},
|
|
permissions: true,
|
|
async query(frame) {
|
|
if (frame.options.filename) {
|
|
let backup = await dbBackup.readBackup(frame.options.filename);
|
|
|
|
if (!backup) {
|
|
throw new errors.NotFoundError();
|
|
}
|
|
|
|
return backup;
|
|
}
|
|
|
|
return Promise.resolve()
|
|
.then(() => exporter.doExport({include: frame.options.withRelated}))
|
|
.catch((err) => {
|
|
return Promise.reject(new errors.GhostError({err: err}));
|
|
});
|
|
}
|
|
},
|
|
|
|
importContent: {
|
|
options: [
|
|
'include'
|
|
],
|
|
validation: {
|
|
options: {
|
|
include: {
|
|
values: exporter.BACKUP_TABLES
|
|
}
|
|
}
|
|
},
|
|
permissions: true,
|
|
query(frame) {
|
|
return importer.importFromFile(frame.file, {include: frame.options.withRelated});
|
|
}
|
|
},
|
|
|
|
deleteAllContent: {
|
|
statusCode: 204,
|
|
permissions: true,
|
|
query() {
|
|
/**
|
|
* @NOTE:
|
|
* We fetch all posts with `columns:id` to increase the speed of this endpoint.
|
|
* And if you trigger `post.destroy(..)`, this will trigger bookshelf and model events.
|
|
* But we only have to `id` available in the model. This won't work, because:
|
|
* - model layer can't trigger event e.g. `post.page` to trigger `post|page.unpublished`.
|
|
* - `onDestroyed` or `onDestroying` can contain custom logic
|
|
*/
|
|
function deleteContent() {
|
|
return models.Base.transaction((transacting) => {
|
|
const queryOpts = {
|
|
columns: 'id',
|
|
context: {internal: true},
|
|
destroyAll: true,
|
|
transacting: transacting
|
|
};
|
|
|
|
return models.Post.findAll(queryOpts)
|
|
.then((response) => {
|
|
return Promise.map(response.models, (post) => {
|
|
return models.Post.destroy(Object.assign({id: post.id}, queryOpts));
|
|
}, {concurrency: 100});
|
|
})
|
|
.then(() => models.Tag.findAll(queryOpts))
|
|
.then((response) => {
|
|
return Promise.map(response.models, (tag) => {
|
|
return models.Tag.destroy(Object.assign({id: tag.id}, queryOpts));
|
|
}, {concurrency: 100});
|
|
})
|
|
.catch((err) => {
|
|
throw new errors.GhostError({
|
|
err: err
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
return dbBackup.backup().then(deleteContent);
|
|
}
|
|
}
|
|
};
|