mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-03 23:00:14 -05:00
25182b7b82
refs https://github.com/TryGhost/Team/issues/586 - Add the products table, so that we can store Products in Ghost - Add the members_products table, so that we can associate Members w/ Products - Use sort_order on the members_products table to follow the same convention in members_labels - Populate the products table with a single product, using the name from the stripe_product_name setting - Populate the members_products table with relations based on the status column of the members table Populating the tables allows us to transition from the current system, which does not care about products, into the new system, where Products are used to group members. The intention is that all existing paid members have the same product
156 lines
4.4 KiB
JavaScript
156 lines
4.4 KiB
JavaScript
const _ = require('lodash');
|
|
const Promise = require('bluebird');
|
|
const db = require('../../data/db');
|
|
const commands = require('../schema').commands;
|
|
const ghostVersion = require('../../lib/ghost-version');
|
|
const {i18n} = require('../../lib/common');
|
|
const logging = require('../../../shared/logging');
|
|
const errors = require('@tryghost/errors');
|
|
const security = require('@tryghost/security');
|
|
const models = require('../../models');
|
|
|
|
// NOTE: these tables can be optionally included to have full db-like export
|
|
const BACKUP_TABLES = [
|
|
'actions',
|
|
'api_keys',
|
|
'brute',
|
|
'emails',
|
|
'integrations',
|
|
'invites',
|
|
'labels',
|
|
'members',
|
|
'members_labels',
|
|
'members_products',
|
|
'members_stripe_customers',
|
|
'members_stripe_customers_subscriptions',
|
|
'migrations',
|
|
'migrations_lock',
|
|
'permissions',
|
|
'permissions_roles',
|
|
'permissions_users',
|
|
'products',
|
|
'webhooks',
|
|
'snippets',
|
|
'tokens',
|
|
'sessions',
|
|
'mobiledoc_revisions',
|
|
'email_batches',
|
|
'email_recipients',
|
|
'members_payment_events',
|
|
'members_login_events',
|
|
'members_email_change_events',
|
|
'members_status_events',
|
|
'members_paid_subscription_events',
|
|
'members_subscribe_events'
|
|
];
|
|
|
|
// NOTE: exposing only tables which are going to be included in a "default" export file
|
|
// they should match with the data that is supported by the importer.
|
|
// In the future it's best to move to resource-based exports instead of database-based ones
|
|
const TABLES_ALLOWLIST = [
|
|
'posts',
|
|
'posts_authors',
|
|
'posts_meta',
|
|
'posts_tags',
|
|
'roles',
|
|
'roles_users',
|
|
'settings',
|
|
'tags',
|
|
'users'
|
|
];
|
|
|
|
// NOTE: these are settings keys which should never end up in the export file
|
|
const SETTING_KEYS_BLOCKLIST = [
|
|
'stripe_connect_publishable_key',
|
|
'stripe_connect_secret_key',
|
|
'stripe_connect_account_id',
|
|
'stripe_secret_key',
|
|
'stripe_publishable_key',
|
|
'members_stripe_webhook_id',
|
|
'members_stripe_webhook_secret'
|
|
];
|
|
|
|
const modelOptions = {context: {internal: true}};
|
|
|
|
const exportFileName = async function exportFileName(options) {
|
|
const datetime = require('moment')().format('YYYY-MM-DD-HH-mm-ss');
|
|
let title = '';
|
|
|
|
options = options || {};
|
|
|
|
// custom filename
|
|
if (options.filename) {
|
|
return options.filename + '.json';
|
|
}
|
|
|
|
try {
|
|
const settingsTitle = await models.Settings.findOne({key: 'title'}, _.merge({}, modelOptions, _.pick(options, 'transacting')));
|
|
|
|
if (settingsTitle) {
|
|
title = security.string.safe(settingsTitle.get('value')) + '.';
|
|
}
|
|
|
|
return title + 'ghost.' + datetime + '.json';
|
|
} catch (err) {
|
|
logging.error(new errors.GhostError({err: err}));
|
|
return 'ghost.' + datetime + '.json';
|
|
}
|
|
};
|
|
|
|
const exportTable = function exportTable(tableName, options) {
|
|
if (TABLES_ALLOWLIST.includes(tableName) ||
|
|
(options.include && _.isArray(options.include) && options.include.indexOf(tableName) !== -1)) {
|
|
const query = (options.transacting || db.knex)(tableName);
|
|
|
|
return query.select();
|
|
}
|
|
};
|
|
|
|
const getSettingsTableData = function getSettingsTableData(settingsData) {
|
|
return settingsData && settingsData.filter((setting) => {
|
|
return !SETTING_KEYS_BLOCKLIST.includes(setting.key);
|
|
});
|
|
};
|
|
|
|
const doExport = async function doExport(options) {
|
|
options = options || {include: []};
|
|
|
|
try {
|
|
const tables = await commands.getTables(options.transacting);
|
|
|
|
const tableData = await Promise.mapSeries(tables, function (tableName) {
|
|
return exportTable(tableName, options);
|
|
});
|
|
|
|
const exportData = {
|
|
meta: {
|
|
exported_on: new Date().getTime(),
|
|
version: ghostVersion.full
|
|
},
|
|
data: {
|
|
// Filled below
|
|
}
|
|
};
|
|
|
|
tables.forEach((name, i) => {
|
|
if (name === 'settings') {
|
|
exportData.data[name] = getSettingsTableData(tableData[i]);
|
|
} else {
|
|
exportData.data[name] = tableData[i];
|
|
}
|
|
});
|
|
|
|
return exportData;
|
|
} catch (err) {
|
|
throw new errors.DataExportError({
|
|
err: err,
|
|
context: i18n.t('errors.data.export.errorExportingData')
|
|
});
|
|
}
|
|
};
|
|
|
|
module.exports = {
|
|
doExport: doExport,
|
|
fileName: exportFileName,
|
|
BACKUP_TABLES: BACKUP_TABLES
|
|
};
|