mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-03 23:00:14 -05:00
Updated serializers/model layer validation using settings type
refs https://github.com/TryGhost/Ghost/issues/10318 - Updates `boolean` serialization in v2/canary serializers to apply only for `boolean` type settings - Updates `boolean` transformation in model layer `format`/`parse` to check on `boolean` type setting - Removes error thrown on Read-only setting for settings edit endpoint - Updates v2/canary input serializers to remove any Read-only settings (using RO flag) to avoid edits - Added type/group mappings in the importer when pre-migration settings table import data is present - Updates tests
This commit is contained in:
parent
8a50a3e9c9
commit
d5f68dbbc5
11 changed files with 216 additions and 112 deletions
|
@ -1,6 +1,7 @@
|
|||
const _ = require('lodash');
|
||||
const url = require('./utils/url');
|
||||
const typeGroupMapper = require('./utils/settings-type-group-mapper');
|
||||
const typeGroupMapper = require('../../../../shared/serializers/input/utils/settings-filter-type-group-mapper');
|
||||
const settingsCache = require('../../../../../services/settings/cache');
|
||||
|
||||
module.exports = {
|
||||
browse(apiConfig, frame) {
|
||||
|
@ -38,11 +39,22 @@ module.exports = {
|
|||
if (_.isString(frame.data)) {
|
||||
frame.data = {settings: [{key: frame.data, value: frame.options}]};
|
||||
}
|
||||
const settings = settingsCache.getAll();
|
||||
|
||||
// Ignore and drop all values with Read-only flag
|
||||
frame.data.settings = frame.data.settings.filter((setting) => {
|
||||
const settingFlagsStr = settings[setting.key] ? settings[setting.key].flags : '';
|
||||
const settingFlagsArr = settingFlagsStr ? settingFlagsStr.split(',') : [];
|
||||
return !settingFlagsArr.includes('RO');
|
||||
});
|
||||
|
||||
frame.data.settings.forEach((setting) => {
|
||||
// CASE: transform objects/arrays into string (we store stringified objects in the db)
|
||||
// @TODO: This belongs into the model layer. We should stringify before saving and parse when fetching from db.
|
||||
// @TODO: Fix when dropping v0.1
|
||||
const settingType = settings[setting.key] ? settings[setting.key].type : '';
|
||||
|
||||
//TODO: Needs to be removed once we get rid of all `object` type settings
|
||||
if (_.isObject(setting.value)) {
|
||||
setting.value = JSON.stringify(setting.value);
|
||||
}
|
||||
|
@ -50,12 +62,12 @@ module.exports = {
|
|||
// @TODO: handle these transformations in a centralised API place (these rules should apply for ALL resources)
|
||||
|
||||
// CASE: Ensure we won't forward strings, otherwise model events or model interactions can fail
|
||||
if (setting.value === '0' || setting.value === '1') {
|
||||
if (settingType === 'boolean' && (setting.value === '0' || setting.value === '1')) {
|
||||
setting.value = !!+setting.value;
|
||||
}
|
||||
|
||||
// CASE: Ensure we won't forward strings, otherwise model events or model interactions can fail
|
||||
if (setting.value === 'false' || setting.value === 'true') {
|
||||
if (settingType === 'boolean' && (setting.value === 'false' || setting.value === 'true')) {
|
||||
setting.value = setting.value === 'true';
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const Promise = require('bluebird');
|
||||
const _ = require('lodash');
|
||||
const {i18n} = require('../../../../../lib/common');
|
||||
const {BadRequestError, NotFoundError} = require('@tryghost/errors');
|
||||
const {NotFoundError} = require('@tryghost/errors');
|
||||
|
||||
module.exports = {
|
||||
read(apiConfig, frame) {
|
||||
|
@ -19,15 +19,7 @@ module.exports = {
|
|||
const errors = [];
|
||||
|
||||
_.each(frame.data.settings, (setting) => {
|
||||
if (setting.key === 'active_theme') {
|
||||
// @NOTE: active theme has to be changed via theme endpoints
|
||||
errors.push(
|
||||
new BadRequestError({
|
||||
message: i18n.t('errors.api.settings.activeThemeSetViaAPI.error'),
|
||||
help: i18n.t('errors.api.settings.activeThemeSetViaAPI.help')
|
||||
})
|
||||
);
|
||||
} else if (setting.key === 'ghost_head' || setting.key === 'ghost_foot') {
|
||||
if (setting.key === 'ghost_head' || setting.key === 'ghost_foot') {
|
||||
// @NOTE: was removed https://github.com/TryGhost/Ghost/issues/10373
|
||||
errors.push(new NotFoundError({
|
||||
message: i18n.t('errors.api.settings.problemFindingSetting', {
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
// NOTE: mapping is based on maping present in migration - 3.22/04-populate-settings-groups-and-flags
|
||||
const keyGroupMapping = {
|
||||
members_public_key: 'core',
|
||||
members_private_key: 'core',
|
||||
members_email_auth_secret: 'core',
|
||||
db_hash: 'core',
|
||||
next_update_check: 'core',
|
||||
notifications: 'core',
|
||||
session_secret: 'core',
|
||||
theme_session_secret: 'core',
|
||||
ghost_public_key: 'core',
|
||||
ghost_private_key: 'core',
|
||||
title: 'site',
|
||||
description: 'site',
|
||||
logo: 'site',
|
||||
cover_image: 'site',
|
||||
icon: 'site',
|
||||
accent_color: 'site',
|
||||
lang: 'site',
|
||||
timezone: 'site',
|
||||
codeinjection_head: 'site',
|
||||
codeinjection_foot: 'site',
|
||||
facebook: 'site',
|
||||
twitter: 'site',
|
||||
navigation: 'site',
|
||||
secondary_navigation: 'site',
|
||||
meta_title: 'site',
|
||||
meta_description: 'site',
|
||||
og_image: 'site',
|
||||
og_title: 'site',
|
||||
og_description: 'site',
|
||||
twitter_image: 'site',
|
||||
twitter_title: 'site',
|
||||
twitter_description: 'site',
|
||||
active_theme: 'theme',
|
||||
is_private: 'private',
|
||||
password: 'private',
|
||||
public_hash: 'private',
|
||||
amp: 'amp',
|
||||
labs: 'labs',
|
||||
slack: 'slack',
|
||||
unsplash: 'unsplash',
|
||||
shared_views: 'views',
|
||||
bulk_email_settings: 'email',
|
||||
default_content_visibility: 'members',
|
||||
members_subscription_settings: 'members',
|
||||
stripe_connect_integration: 'members',
|
||||
portal_name: 'portal',
|
||||
portal_button: 'portal',
|
||||
portal_plans: 'portal'
|
||||
};
|
||||
|
||||
const mapKeyToGroup = (key) => {
|
||||
return keyGroupMapping[key];
|
||||
};
|
||||
|
||||
module.exports = mapKeyToGroup;
|
|
@ -0,0 +1,66 @@
|
|||
// NOTE: mapping is based on maping present in migration - 3.22/07-update-type-for-settings
|
||||
const keyTypeMapping = {
|
||||
db_hash: 'string',
|
||||
session_secret: 'string',
|
||||
theme_session_secret: 'string',
|
||||
ghost_public_key: 'string',
|
||||
ghost_private_key: 'string',
|
||||
title: 'string',
|
||||
description: 'string',
|
||||
logo: 'string',
|
||||
cover_image: 'string',
|
||||
icon: 'string',
|
||||
accent_color: 'string',
|
||||
lang: 'string',
|
||||
timezone: 'string',
|
||||
codeinjection_head: 'string',
|
||||
codeinjection_foot: 'string',
|
||||
facebook: 'string',
|
||||
twitter: 'string',
|
||||
meta_title: 'string',
|
||||
meta_description: 'string',
|
||||
og_image: 'string',
|
||||
og_title: 'string',
|
||||
og_description: 'string',
|
||||
twitter_image: 'string',
|
||||
twitter_title: 'string',
|
||||
twitter_description: 'string',
|
||||
active_theme: 'string',
|
||||
password: 'string',
|
||||
public_hash: 'string',
|
||||
members_public_key: 'string',
|
||||
members_private_key: 'string',
|
||||
members_email_auth_secret: 'string',
|
||||
default_content_visibility: 'string',
|
||||
members_from_address: 'string',
|
||||
stripe_product_name: 'string',
|
||||
stripe_secret_key: 'string',
|
||||
stripe_publishable_key: 'string',
|
||||
stripe_connect_publishable_key: 'string',
|
||||
stripe_connect_secret_key: 'string',
|
||||
stripe_connect_display_name: 'string',
|
||||
stripe_connect_account_id: 'string',
|
||||
notifications: 'array',
|
||||
navigation: 'array',
|
||||
secondary_navigation: 'array',
|
||||
slack: 'array',
|
||||
shared_views: 'array',
|
||||
portal_plans: 'array',
|
||||
stripe_plans: 'array',
|
||||
next_update_check: 'number',
|
||||
amp: 'boolean',
|
||||
is_private: 'boolean',
|
||||
members_allow_free_signup: 'boolean',
|
||||
portal_name: 'boolean',
|
||||
portal_button: 'boolean',
|
||||
stripe_connect_livemode: 'boolean',
|
||||
labs: 'object',
|
||||
unsplash: 'object',
|
||||
bulk_email_settings: 'object'
|
||||
};
|
||||
|
||||
const mapKeyToType = (key) => {
|
||||
return keyTypeMapping[key];
|
||||
};
|
||||
|
||||
module.exports = mapKeyToType;
|
|
@ -1,6 +1,7 @@
|
|||
const _ = require('lodash');
|
||||
const url = require('./utils/url');
|
||||
const typeGroupMapper = require('./utils/settings-type-group-mapper');
|
||||
const typeGroupMapper = require('../../../../shared/serializers/input/utils/settings-filter-type-group-mapper');
|
||||
const settingsCache = require('../../../../../services/settings/cache');
|
||||
|
||||
module.exports = {
|
||||
browse(apiConfig, frame) {
|
||||
|
@ -35,23 +36,30 @@ module.exports = {
|
|||
frame.data = {settings: [{key: frame.data, value: frame.options}]};
|
||||
}
|
||||
|
||||
const settings = settingsCache.getAll();
|
||||
|
||||
// Ignore and drop all values with Read-only flag
|
||||
frame.data.settings = frame.data.settings.filter((setting) => {
|
||||
const settingFlagsStr = settings[setting.key] ? settings[setting.key].flags : '';
|
||||
const settingFlagsArr = settingFlagsStr ? settingFlagsStr.split(',') : [];
|
||||
return !settingFlagsArr.includes('RO');
|
||||
});
|
||||
|
||||
frame.data.settings.forEach((setting) => {
|
||||
// CASE: transform objects/arrays into string (we store stringified objects in the db)
|
||||
// @TODO: This belongs into the model layer. We should stringify before saving and parse when fetching from db.
|
||||
// @TODO: Fix when dropping v0.1
|
||||
const settingType = settings[setting.key] ? settings[setting.key].type : '';
|
||||
|
||||
// TODO: Needs to be removed once we get rid of all `object` type settings
|
||||
if (_.isObject(setting.value)) {
|
||||
setting.value = JSON.stringify(setting.value);
|
||||
}
|
||||
|
||||
// @TODO: handle these transformations in a centralised API place (these rules should apply for ALL resources)
|
||||
|
||||
// CASE: Ensure we won't forward strings, otherwise model events or model interactions can fail
|
||||
if (setting.value === '0' || setting.value === '1') {
|
||||
// CASE: Ensure we won't forward strings for booleans, otherwise model events or model interactions can fail
|
||||
if (settingType === 'boolean' && (setting.value === '0' || setting.value === '1')) {
|
||||
setting.value = !!+setting.value;
|
||||
}
|
||||
|
||||
// CASE: Ensure we won't forward strings, otherwise model events or model interactions can fail
|
||||
if (setting.value === 'false' || setting.value === 'true') {
|
||||
// CASE: Ensure we won't forward strings for booleans, otherwise model events or model interactions can fail
|
||||
if (settingType === 'boolean' && (setting.value === 'false' || setting.value === 'true')) {
|
||||
setting.value = setting.value === 'true';
|
||||
}
|
||||
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
const typeGroupMapping = {
|
||||
core: [
|
||||
'core'
|
||||
],
|
||||
blog: [
|
||||
'site',
|
||||
'amp',
|
||||
'labs',
|
||||
'slack',
|
||||
'unsplash',
|
||||
'views'
|
||||
],
|
||||
theme: [
|
||||
'theme'
|
||||
],
|
||||
members: [
|
||||
'members'
|
||||
],
|
||||
private: [
|
||||
'private'
|
||||
],
|
||||
portal: [
|
||||
'portal'
|
||||
],
|
||||
bulk_email: [
|
||||
'email'
|
||||
],
|
||||
site: [
|
||||
'site'
|
||||
]
|
||||
};
|
||||
|
||||
const mapTypeToGroup = (typeOptions) => {
|
||||
const types = typeOptions.split(',');
|
||||
|
||||
const mappedTypes = types.map((type) => {
|
||||
const sanitizedType = type ? type.trim() : null;
|
||||
|
||||
return typeGroupMapping[sanitizedType];
|
||||
}).filter(type => !!type);
|
||||
|
||||
return mappedTypes.join(',');
|
||||
};
|
||||
|
||||
module.exports = mapTypeToGroup;
|
|
@ -133,10 +133,22 @@ Settings = ghostBookshelf.Model.extend({
|
|||
|
||||
format() {
|
||||
const attrs = ghostBookshelf.Model.prototype.format.apply(this, arguments);
|
||||
const settingType = attrs.type;
|
||||
|
||||
// @NOTE: type TEXT will transform boolean to "0"
|
||||
if (_.isBoolean(attrs.value)) {
|
||||
attrs.value = attrs.value.toString();
|
||||
if (settingType === 'boolean') {
|
||||
// CASE: Ensure we won't forward strings, otherwise model events or model interactions can fail
|
||||
if (attrs.value === '0' || attrs.value === '1') {
|
||||
attrs.value = !!+attrs.value;
|
||||
}
|
||||
|
||||
// CASE: Ensure we won't forward strings, otherwise model events or model interactions can fail
|
||||
if (attrs.value === 'false' || attrs.value === 'true') {
|
||||
attrs.value = JSON.parse(attrs.value);
|
||||
}
|
||||
|
||||
if (_.isBoolean(attrs.value)) {
|
||||
attrs.value = attrs.value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return attrs;
|
||||
|
@ -145,13 +157,14 @@ Settings = ghostBookshelf.Model.extend({
|
|||
parse() {
|
||||
const attrs = ghostBookshelf.Model.prototype.parse.apply(this, arguments);
|
||||
|
||||
// transform "0" to false
|
||||
// transform "false" to false
|
||||
if (attrs.value === '0' || attrs.value === '1') {
|
||||
// transform "0" to false for boolean type
|
||||
const settingType = attrs.type;
|
||||
if (settingType === 'boolean' && (attrs.value === '0' || attrs.value === '1')) {
|
||||
attrs.value = !!+attrs.value;
|
||||
}
|
||||
|
||||
if (attrs.value === 'false' || attrs.value === 'true') {
|
||||
// transform "false" to false for boolean type
|
||||
if (settingType === 'boolean' && (attrs.value === 'false' || attrs.value === 'true')) {
|
||||
attrs.value = JSON.parse(attrs.value);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const should = require('should');
|
||||
const mapper = require('../../../../../../../../core//server/api/canary/utils/serializers/input/utils/settings-type-group-mapper');
|
||||
const mapper = require('../../../../../../../../core/server/api/shared/serializers/input/utils/settings-filter-type-group-mapper');
|
||||
|
||||
describe('Unit: canary/utils/serializers/input/utils/settings-type-group-mapper', function () {
|
||||
describe('browse', function () {
|
|
@ -48,9 +48,10 @@ describe('Unit: models/settings', function () {
|
|||
][step - 1]();
|
||||
});
|
||||
|
||||
return models.Settings.edit({
|
||||
return models.Settings.add({
|
||||
key: 'description',
|
||||
value: 'added value'
|
||||
value: 'added value',
|
||||
type: 'string'
|
||||
})
|
||||
.then(() => {
|
||||
eventSpy.calledTwice.should.be.true();
|
||||
|
@ -188,22 +189,22 @@ describe('Unit: models/settings', function () {
|
|||
it('ensure correct parsing when fetching from db', function () {
|
||||
const setting = models.Settings.forge();
|
||||
|
||||
let returns = setting.parse({key: 'is_private', value: 'false'});
|
||||
let returns = setting.parse({key: 'is_private', value: 'false', type: 'boolean'});
|
||||
should.equal(returns.value, false);
|
||||
|
||||
returns = setting.parse({key: 'is_private', value: false});
|
||||
returns = setting.parse({key: 'is_private', value: false, type: 'boolean'});
|
||||
should.equal(returns.value, false);
|
||||
|
||||
returns = setting.parse({key: 'is_private', value: true});
|
||||
returns = setting.parse({key: 'is_private', value: true, type: 'boolean'});
|
||||
should.equal(returns.value, true);
|
||||
|
||||
returns = setting.parse({key: 'is_private', value: 'true'});
|
||||
returns = setting.parse({key: 'is_private', value: 'true', type: 'boolean'});
|
||||
should.equal(returns.value, true);
|
||||
|
||||
returns = setting.parse({key: 'is_private', value: '0'});
|
||||
returns = setting.parse({key: 'is_private', value: '0', type: 'boolean'});
|
||||
should.equal(returns.value, false);
|
||||
|
||||
returns = setting.parse({key: 'is_private', value: '1'});
|
||||
returns = setting.parse({key: 'is_private', value: '1', type: 'boolean'});
|
||||
should.equal(returns.value, true);
|
||||
|
||||
returns = setting.parse({key: 'something', value: 'null'});
|
||||
|
|
|
@ -2208,7 +2208,7 @@
|
|||
"id": "5c2ca6e0e015a6761618240d",
|
||||
"key": "db_hash",
|
||||
"value": "04a3a271-634b-44d8-842e-49286b38a162",
|
||||
"type": "core",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2218,7 +2218,7 @@
|
|||
"id": "5c2ca6e0e015a6761618240e",
|
||||
"key": "next_update_check",
|
||||
"value": null,
|
||||
"type": "core",
|
||||
"type": "number",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2228,7 +2228,7 @@
|
|||
"id": "5c2ca6e0e015a6761618240f",
|
||||
"key": "notifications",
|
||||
"value": "[]",
|
||||
"type": "core",
|
||||
"type": "array",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2238,7 +2238,7 @@
|
|||
"id": "5c2ca6e0e015a67616182410",
|
||||
"key": "session_secret",
|
||||
"value": "e255c011755a47cba690cb5089e2c05f488ab0e6d682d44809f622e25864e980",
|
||||
"type": "core",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2248,7 +2248,7 @@
|
|||
"id": "5c2ca6e0e015a67616182411",
|
||||
"key": "title",
|
||||
"value": "Ghost",
|
||||
"type": "site",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2258,7 +2258,7 @@
|
|||
"id": "5c2ca6e0e015a67616182412",
|
||||
"key": "description",
|
||||
"value": "The professional publishing platform",
|
||||
"type": "site",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2268,7 +2268,7 @@
|
|||
"id": "5c2ca6e0e015a67616182413",
|
||||
"key": "logo",
|
||||
"value": "https://static.ghost.org/v1.0.0/images/ghost-logo.svg",
|
||||
"type": "site",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2278,7 +2278,7 @@
|
|||
"id": "5c2ca6e0e015a67616182414",
|
||||
"key": "cover_image",
|
||||
"value": "https://static.ghost.org/v1.0.0/images/blog-cover.jpg",
|
||||
"type": "site",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2288,7 +2288,7 @@
|
|||
"id": "5c2ca6e0e015a67616182415",
|
||||
"key": "icon",
|
||||
"value": "",
|
||||
"type": "site",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2298,7 +2298,7 @@
|
|||
"id": "5c2ca6e0e015a67616182416",
|
||||
"key": "lang",
|
||||
"value": "en",
|
||||
"type": "site",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2308,7 +2308,7 @@
|
|||
"id": "5c2ca6e0e015a67616182417",
|
||||
"key": "timezone",
|
||||
"value": "Etc/UTC",
|
||||
"type": "site",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2318,7 +2318,7 @@
|
|||
"id": "5c2ca6e0e015a6761618241a",
|
||||
"key": "amp",
|
||||
"value": "true",
|
||||
"type": "amp",
|
||||
"type": "boolean",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2328,7 +2328,7 @@
|
|||
"id": "5c2ca6e0e015a6761618241b",
|
||||
"key": "ghost_head",
|
||||
"value": "",
|
||||
"type": "site",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2338,7 +2338,7 @@
|
|||
"id": "5c2ca6e0e015a6761618241c",
|
||||
"key": "ghost_foot",
|
||||
"value": "",
|
||||
"type": "site",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2348,7 +2348,7 @@
|
|||
"id": "5c2ca6e0e015a6761618241d",
|
||||
"key": "facebook",
|
||||
"value": "ghost",
|
||||
"type": "site",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2358,7 +2358,7 @@
|
|||
"id": "5c2ca6e0e015a6761618241e",
|
||||
"key": "twitter",
|
||||
"value": "tryghost",
|
||||
"type": "site",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2368,7 +2368,7 @@
|
|||
"id": "5c2ca6e0e015a6761618241f",
|
||||
"key": "labs",
|
||||
"value": "{\"publicAPI\": true}",
|
||||
"type": "labs",
|
||||
"type": "object",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2378,7 +2378,7 @@
|
|||
"id": "5c2ca6e0e015a67616182420",
|
||||
"key": "navigation",
|
||||
"value": "[{\"label\":\"Home\", \"url\":\"/\"},{\"label\":\"Tag\", \"url\":\"/tag/getting-started/\"}, {\"label\":\"Author\", \"url\":\"/author/ghost/\"},{\"label\":\"Help\", \"url\":\"https://ghost.org/docs/\"}]",
|
||||
"type": "site",
|
||||
"type": "array",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2388,7 +2388,7 @@
|
|||
"id": "5c2ca6e0e015a67616182421",
|
||||
"key": "slack",
|
||||
"value": "[{\"url\":\"\", \"username\":\"Ghost\"}]",
|
||||
"type": "slack",
|
||||
"type": "array",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2398,7 +2398,7 @@
|
|||
"id": "5c2ca6e0e015a67616182422",
|
||||
"key": "unsplash",
|
||||
"value": "{\"isActive\": true}",
|
||||
"type": "unsplash",
|
||||
"type": "object",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2408,7 +2408,7 @@
|
|||
"id": "5c2ca6e0e015a67616182423",
|
||||
"key": "active_theme",
|
||||
"value": "casper",
|
||||
"type": "theme",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2418,7 +2418,7 @@
|
|||
"id": "5c2ca6e0e015a67616182424",
|
||||
"key": "active_apps",
|
||||
"value": "[]",
|
||||
"type": "app",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2428,7 +2428,7 @@
|
|||
"id": "5c2ca6e0e015a67616182425",
|
||||
"key": "installed_apps",
|
||||
"value": "[]",
|
||||
"type": "app",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2438,7 +2438,7 @@
|
|||
"id": "5c2ca6e0e015a67616182426",
|
||||
"key": "is_private",
|
||||
"value": "false",
|
||||
"type": "private",
|
||||
"type": "boolean",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2448,7 +2448,7 @@
|
|||
"id": "5c2ca6e0e015a67616182427",
|
||||
"key": "password",
|
||||
"value": "",
|
||||
"type": "private",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2458,7 +2458,7 @@
|
|||
"id": "5c2ca6e0e015a67616182428",
|
||||
"key": "public_hash",
|
||||
"value": "a7cb7ad9a319203bc4f1d1a90c4116",
|
||||
"type": "private",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2468,7 +2468,7 @@
|
|||
"id": "5c2ca6e0e015a67616182429",
|
||||
"key": "members_public_key",
|
||||
"value": "-----BEGIN RSA PUBLIC KEY-----\nMIGJAoGBAJ/6gCIMJyUZsR+lwV/ObX1mYSt7MFcy7LcPeDvepSruyabB9Z98Tit6Npfr79cc\nvxt95S8oUxXqRKNQgrDyJG6NJLMx9rcU2OMmdLAG5wJVlBz0D1eOwIaGKDzjZL9B42QIJPsy\nFQiNntWGYgiIPjpUT2u4rnsV0ATo9TB/bk3xAgMBAAE=\n-----END RSA PUBLIC KEY-----\n",
|
||||
"type": "core",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
@ -2478,7 +2478,7 @@
|
|||
"id": "5c2ca6e0e015a6761618242a",
|
||||
"key": "members_private_key",
|
||||
"value": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQCf+oAiDCclGbEfpcFfzm19ZmErezBXMuy3D3g73qUq7smmwfWffE4rejaX\n6+/XHL8bfeUvKFMV6kSjUIKw8iRujSSzMfa3FNjjJnSwBucCVZQc9A9XjsCGhig842S/QeNk\nCCT7MhUIjZ7VhmIIiD46VE9ruK57FdAE6PUwf25N8QIDAQABAoGAZm70Gljju6qusg/lOJ4p\nlzC1qSywsDTIQxKhrtwJr+rDrYXl6x+hwc74I+CLapZae5Tp6X8NbCvblSKY/AmfbzEw4QnQ\nybQC5WVC/VPl6jWYIodpwUM7RdUNYqWMiY07XnSwL2ejXszW3MO3NPGqh1irp0U4RwutaRfV\n4D6xDMECQQDh4PdiYJetxrruQBg3u5E4VaX/M1xzRGqo4jCdaO3e4+M1WymcPGmHJC3cypzg\n0dEdkhD9KH1AziSEjEeu/9kZAkEAtU/V+nAsdxsUby6LMBIB+3y6DcQwwygobfwkD2A+3qVQ\nTHxNwh6kL2Xif9U9d4+w1XQE8kwmZsKoQ9z8PC2+mQJAHuGi8NBD7H4/EFOy++uo7wrGpx1e\nhmPUMUK7Ysn1u4NsjN7p0XJw+wj3PDh3OkV1UZWmvPXMKhAE7ho/sq1IAQJATURloyGUwXln\n3u3N4UF7WMpRm7ZFNZXyjNSMJYVVpZp7uuyqUpSuUYiw2ttsI3y31m9oAD4Vi2tfO/R8BcVU\n2QJBAM4tqJaEy4jQ4aa9XEfU0/LU1TwZbeHZJ8Y+pDarHfYiHShQ2JKlMDAeh/DNqD0VEpeI\nSeO5Hxbv/56NVT+pNQE=\n-----END RSA PRIVATE KEY-----\n",
|
||||
"type": "core",
|
||||
"type": "string",
|
||||
"created_at": "2019-01-02T11:56:16.000Z",
|
||||
"created_by": "1",
|
||||
"updated_at": "2019-01-02T11:56:16.000Z",
|
||||
|
|
Loading…
Add table
Reference in a new issue