mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
commit
1c5a811760
7 changed files with 335 additions and 482 deletions
|
@ -397,4 +397,6 @@ when(ghost.init()).then(function () {
|
|||
}
|
||||
|
||||
});
|
||||
}).otherwise(errors.logAndThrowError);
|
||||
}, function (err) {
|
||||
errors.logErrorAndExit(err);
|
||||
});
|
||||
|
|
|
@ -7,6 +7,7 @@ var Ghost = require('../../ghost'),
|
|||
when = require('when'),
|
||||
nodefn = require('when/node/function'),
|
||||
_ = require('underscore'),
|
||||
schema = require('../data/schema'),
|
||||
|
||||
ghost = new Ghost(),
|
||||
db;
|
||||
|
@ -75,8 +76,7 @@ db = {
|
|||
.then(function (fileContents) {
|
||||
var importData,
|
||||
error = "",
|
||||
constraints = require('../data/migration/' + databaseVersion).constraints,
|
||||
constraintkeys = _.keys(constraints);
|
||||
tableKeys = _.keys(schema);
|
||||
|
||||
// Parse the json data
|
||||
try {
|
||||
|
@ -89,20 +89,20 @@ db = {
|
|||
return when.reject(new Error("Import data does not specify version"));
|
||||
}
|
||||
|
||||
_.each(constraintkeys, function (constkey) {
|
||||
_.each(tableKeys, function (constkey) {
|
||||
_.each(importData.data[constkey], function (elem) {
|
||||
var prop;
|
||||
for (prop in elem) {
|
||||
if (elem.hasOwnProperty(prop)) {
|
||||
if (constraints[constkey].hasOwnProperty(prop)) {
|
||||
if (schema[constkey].hasOwnProperty(prop)) {
|
||||
if (elem.hasOwnProperty(prop)) {
|
||||
if (!_.isNull(elem[prop])) {
|
||||
if (elem[prop].length > constraints[constkey][prop].maxlength) {
|
||||
if (elem[prop].length > schema[constkey][prop].maxlength) {
|
||||
error += error !== "" ? "<br>" : "";
|
||||
error += "Property '" + prop + "' exceeds maximum length of " + constraints[constkey][prop].maxlength + " (element:" + constkey + " / id:" + elem.id + ")";
|
||||
error += "Property '" + prop + "' exceeds maximum length of " + schema[constkey][prop].maxlength + " (element:" + constkey + " / id:" + elem.id + ")";
|
||||
}
|
||||
} else {
|
||||
if (!constraints[constkey][prop].nullable) {
|
||||
if (!schema[constkey][prop].nullable) {
|
||||
error += error !== "" ? "<br>" : "";
|
||||
error += "Property '" + prop + "' is not nullable (element:" + constkey + " / id:" + elem.id + ")";
|
||||
}
|
||||
|
@ -156,4 +156,4 @@ db = {
|
|||
}
|
||||
};
|
||||
|
||||
module.exports = db;
|
||||
module.exports = db;
|
|
@ -1,37 +1,13 @@
|
|||
var when = require('when'),
|
||||
_ = require('underscore'),
|
||||
migration = require('../migration'),
|
||||
client = require('../../models/base').client,
|
||||
knex = require('../../models/base').knex,
|
||||
schema = require('../schema'),
|
||||
|
||||
exporter;
|
||||
|
||||
function getTablesFromSqlite3() {
|
||||
return knex.raw("select * from sqlite_master where type = 'table'").then(function (response) {
|
||||
return _.reject(_.pluck(response[0], 'tbl_name'), function (name) {
|
||||
return name === 'sqlite_sequence';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getTablesFromMySQL() {
|
||||
return knex.raw("show tables").then(function (response) {
|
||||
return _.flatten(_.map(response[0], function (entry) {
|
||||
return _.values(entry);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
exporter = function () {
|
||||
var tablesToExport;
|
||||
|
||||
if (client === 'sqlite3') {
|
||||
tablesToExport = getTablesFromSqlite3();
|
||||
} else if (client === 'mysql') {
|
||||
tablesToExport = getTablesFromMySQL();
|
||||
} else {
|
||||
return when.reject("No exporter for database client " + client);
|
||||
}
|
||||
var tablesToExport = _.keys(schema);
|
||||
|
||||
return when.join(migration.getDatabaseVersion(), tablesToExport).then(function (results) {
|
||||
var version = results[0],
|
||||
|
|
|
@ -1,269 +0,0 @@
|
|||
var when = require('when'),
|
||||
knex = require('../../models/base').knex,
|
||||
up,
|
||||
down,
|
||||
constraints = {
|
||||
posts: {
|
||||
id: {maxlength: 0, nullable: false},
|
||||
uuid: {maxlength: 36, nullable: false},
|
||||
title: {maxlength: 150, nullable: false},
|
||||
slug: {maxlength: 150, nullable: false},
|
||||
markdown: {maxlength: 16777215, nullable: true},
|
||||
html: {maxlength: 16777215, nullable: true},
|
||||
image: {maxlength: 2000, nullable: true},
|
||||
featured: {maxlength: 0, nullable: false},
|
||||
page: {maxlength: 0, nullable: false},
|
||||
status: {maxlength: 150, nullable: false},
|
||||
language: {maxlength: 6, nullable: false},
|
||||
meta_title: {maxlength: 150, nullable: true},
|
||||
meta_description: {maxlength: 200, nullable: true},
|
||||
author_id: {maxlength: 0, nullable: false},
|
||||
created_at: {maxlength: 0, nullable: false},
|
||||
created_by: {maxlength: 0, nullable: false},
|
||||
updated_at: {maxlength: 0, nullable: true},
|
||||
updated_by: {maxlength: 0, nullable: true},
|
||||
published_at: {maxlength: 0, nullable: true},
|
||||
published_by: {maxlength: 0, nullable: true}
|
||||
},
|
||||
users: {
|
||||
id: {maxlength: 0, nullable: false},
|
||||
uuid: {maxlength: 36, nullable: false},
|
||||
name: {maxlength: 150, nullable: false},
|
||||
slug: {maxlength: 150, nullable: false},
|
||||
password: {maxlength: 60, nullable: false},
|
||||
email: {maxlength: 254, nullable: false},
|
||||
image: {maxlength: 2000, nullable: true},
|
||||
cover: {maxlength: 2000, nullable: true},
|
||||
bio: {maxlength: 200, nullable: true},
|
||||
website: {maxlength: 2000, nullable: true},
|
||||
location: {maxlength: 65535, nullable: true},
|
||||
accessibility: {maxlength: 65535, nullable: true},
|
||||
status: {maxlength: 150, nullable: false},
|
||||
language: {maxlength: 6, nullable: false},
|
||||
meta_title: {maxlength: 150, nullable: true},
|
||||
meta_description: {maxlength: 200, nullable: true},
|
||||
last_login: {maxlength: 0, nullable: true},
|
||||
created_at: {maxlength: 0, nullable: false},
|
||||
created_by: {maxlength: 0, nullable: false},
|
||||
updated_at: {maxlength: 0, nullable: true},
|
||||
updated_by: {maxlength: 0, nullable: true}
|
||||
},
|
||||
roles: {
|
||||
id: {maxlength: 0, nullable: false},
|
||||
uuid: {maxlength: 36, nullable: false},
|
||||
name: {maxlength: 150, nullable: false},
|
||||
description: {maxlength: 200, nullable: true},
|
||||
created_at: {maxlength: 0, nullable: false},
|
||||
created_by: {maxlength: 0, nullable: false},
|
||||
updated_at: {maxlength: 0, nullable: true},
|
||||
updated_by: {maxlength: 0, nullable: true}
|
||||
},
|
||||
roles_users: {
|
||||
id: {maxlength: 0, nullable: false},
|
||||
role_id: {maxlength: 0, nullable: false},
|
||||
user_id: {maxlength: 0, nullable: false}
|
||||
},
|
||||
permissions: {
|
||||
id: {maxlength: 0, nullable: false},
|
||||
uuid: {maxlength: 36, nullable: false},
|
||||
name: {maxlength: 150, nullable: false},
|
||||
object_type: {maxlength: 150, nullable: false},
|
||||
action_type: {maxlength: 150, nullable: false},
|
||||
object_id: {maxlength: 0, nullable: true},
|
||||
created_at: {maxlength: 0, nullable: false},
|
||||
created_by: {maxlength: 0, nullable: false},
|
||||
updated_at: {maxlength: 0, nullable: true},
|
||||
updated_by: {maxlength: 0, nullable: true}
|
||||
},
|
||||
permissions_users: {
|
||||
id: {maxlength: 0, nullable: false},
|
||||
user_id: {maxlength: 0, nullable: false},
|
||||
permission_id: {maxlength: 0, nullable: false}
|
||||
},
|
||||
permissions_roles: {
|
||||
id: {maxlength: 0, nullable: false},
|
||||
role_id: {maxlength: 0, nullable: false},
|
||||
permission_id: {maxlength: 0, nullable: false}
|
||||
},
|
||||
settings: {
|
||||
id: {maxlength: 0, nullable: false},
|
||||
uuid: {maxlength: 36, nullable: false},
|
||||
key: {maxlength: 150, nullable: false},
|
||||
value: {maxlength: 65535, nullable: true},
|
||||
type: {maxlength: 150, nullable: false},
|
||||
created_at: {maxlength: 0, nullable: false},
|
||||
created_by: {maxlength: 0, nullable: false},
|
||||
updated_at: {maxlength: 0, nullable: true},
|
||||
updated_by: {maxlength: 0, nullable: true}
|
||||
},
|
||||
tags: {
|
||||
id: {maxlength: 0, nullable: false},
|
||||
uuid: {maxlength: 36, nullable: false},
|
||||
name: {maxlength: 150, nullable: false},
|
||||
slug: {maxlength: 150, nullable: false},
|
||||
description: {maxlength: 200, nullable: true},
|
||||
parent_id: {maxlength: 0, nullable: true},
|
||||
meta_title: {maxlength: 150, nullable: true},
|
||||
meta_description: {maxlength: 200, nullable: true},
|
||||
created_at: {maxlength: 0, nullable: false},
|
||||
created_by: {maxlength: 0, nullable: false},
|
||||
updated_at: {maxlength: 0, nullable: true},
|
||||
updated_by: {maxlength: 0, nullable: true}
|
||||
},
|
||||
posts_tags: {
|
||||
id: {maxlength: 0, nullable: false},
|
||||
post_id: {maxlength: 0, nullable: false},
|
||||
tag_id: {maxlength: 0, nullable: false}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
up = function () {
|
||||
|
||||
return when.all([
|
||||
|
||||
knex.schema.createTable('posts', function (t) {
|
||||
t.increments().primary();
|
||||
t.string('uuid', constraints.posts.uuid.maxlength).notNull();
|
||||
t.string('title', constraints.posts.title.maxlength).notNull();
|
||||
t.string('slug', constraints.posts.slug.maxlength).notNull().unique();
|
||||
t.text('markdown', 'medium').nullable(); // max-length 16777215
|
||||
t.text('html', 'medium').nullable(); // max-length 16777215
|
||||
t.text('image').nullable(); // max-length 2000
|
||||
t.bool('featured').notNull().defaultTo(false);
|
||||
t.bool('page').notNull().defaultTo(false);
|
||||
t.string('status', constraints.posts.status.maxlength).notNull().defaultTo('draft');
|
||||
t.string('language', constraints.posts.language.maxlength).notNull().defaultTo('en_US');
|
||||
t.string('meta_title', constraints.posts.meta_title.maxlength).nullable();
|
||||
t.string('meta_description', constraints.posts.meta_description.maxlength).nullable();
|
||||
t.integer('author_id').notNull();
|
||||
t.dateTime('created_at').notNull();
|
||||
t.integer('created_by').notNull();
|
||||
t.dateTime('updated_at').nullable();
|
||||
t.integer('updated_by').nullable();
|
||||
t.dateTime('published_at').nullable();
|
||||
t.integer('published_by').nullable();
|
||||
}),
|
||||
|
||||
knex.schema.createTable('users', function (t) {
|
||||
t.increments().primary();
|
||||
t.string('uuid', constraints.users.uuid.maxlength).notNull();
|
||||
t.string('name', constraints.users.name.maxlength).notNull();
|
||||
t.string('slug', constraints.users.slug.maxlength).notNull().unique();
|
||||
t.string('password', constraints.users.password.maxlength).notNull();
|
||||
t.string('email', constraints.users.email.maxlength).notNull().unique();
|
||||
t.text('image').nullable(); // max-length 2000
|
||||
t.text('cover').nullable(); // max-length 2000
|
||||
t.string('bio', constraints.users.bio.maxlength).nullable();
|
||||
t.text('website').nullable(); // max-length 2000
|
||||
t.text('location').nullable(); // max-length 65535
|
||||
t.text('accessibility').nullable(); // max-length 65535
|
||||
t.string('status', constraints.users.status.maxlength).notNull().defaultTo('active');
|
||||
t.string('language', constraints.users.language.maxlength).notNull().defaultTo('en_US');
|
||||
t.string('meta_title', constraints.users.meta_title.maxlength).nullable();
|
||||
t.string('meta_description', constraints.users.meta_description.maxlength).nullable();
|
||||
t.dateTime('last_login').nullable();
|
||||
t.dateTime('created_at').notNull();
|
||||
t.integer('created_by').notNull();
|
||||
t.dateTime('updated_at').nullable();
|
||||
t.integer('updated_by').nullable();
|
||||
}),
|
||||
|
||||
knex.schema.createTable('roles', function (t) {
|
||||
t.increments().primary();
|
||||
t.string('uuid', constraints.roles.uuid.maxlength).notNull();
|
||||
t.string('name', constraints.roles.name.maxlength).notNull();
|
||||
t.string('description', constraints.roles.description.maxlength).nullable();
|
||||
t.dateTime('created_at').notNull();
|
||||
t.integer('created_by').notNull();
|
||||
t.dateTime('updated_at').nullable();
|
||||
t.integer('updated_by').nullable();
|
||||
}),
|
||||
|
||||
knex.schema.createTable('roles_users', function (t) {
|
||||
t.increments().primary();
|
||||
t.integer('role_id').notNull();
|
||||
t.integer('user_id').notNull();
|
||||
}),
|
||||
|
||||
knex.schema.createTable('permissions', function (t) {
|
||||
t.increments().primary();
|
||||
t.string('uuid', constraints.permissions.uuid.maxlength).notNull();
|
||||
t.string('name', constraints.permissions.name.maxlength).notNull();
|
||||
t.string('object_type', constraints.permissions.object_type.maxlength).notNull();
|
||||
t.string('action_type', constraints.permissions.action_type.maxlength).notNull();
|
||||
t.integer('object_id').nullable();
|
||||
t.dateTime('created_at').notNull();
|
||||
t.integer('created_by').notNull();
|
||||
t.dateTime('updated_at').nullable();
|
||||
t.integer('updated_by').nullable();
|
||||
}),
|
||||
|
||||
knex.schema.createTable('permissions_users', function (t) {
|
||||
t.increments().primary();
|
||||
t.integer('user_id').notNull();
|
||||
t.integer('permission_id').notNull();
|
||||
}),
|
||||
|
||||
knex.schema.createTable('permissions_roles', function (t) {
|
||||
t.increments().primary();
|
||||
t.integer('role_id').notNull();
|
||||
t.integer('permission_id').notNull();
|
||||
}),
|
||||
|
||||
knex.schema.createTable('settings', function (t) {
|
||||
t.increments().primary();
|
||||
t.string('uuid', constraints.settings.uuid.maxlength).notNull();
|
||||
t.string('key', constraints.settings.key.maxlength).notNull().unique();
|
||||
t.text('value').nullable(); // max-length 65535
|
||||
t.string('type', constraints.settings.type.maxlength).notNull().defaultTo('core');
|
||||
t.dateTime('created_at').notNull();
|
||||
t.integer('created_by').notNull();
|
||||
t.dateTime('updated_at').nullable();
|
||||
t.integer('updated_by').nullable();
|
||||
}),
|
||||
knex.schema.createTable('tags', function (t) {
|
||||
t.increments().primary();
|
||||
t.string('uuid', constraints.tags.uuid.maxlength).notNull();
|
||||
t.string('name', constraints.tags.name.maxlength).notNull();
|
||||
t.string('slug', constraints.tags.slug.maxlength).notNull().unique();
|
||||
t.string('description', constraints.tags.description.maxlength).nullable();
|
||||
t.integer('parent_id').nullable();
|
||||
t.string('meta_title', constraints.tags.meta_title.maxlength).nullable();
|
||||
t.string('meta_description', constraints.tags.meta_description.maxlength).nullable();
|
||||
t.dateTime('created_at').notNull();
|
||||
t.integer('created_by').notNull();
|
||||
t.dateTime('updated_at').nullable();
|
||||
t.integer('updated_by').nullable();
|
||||
})
|
||||
]).then(function () {
|
||||
return knex.schema.createTable('posts_tags', function (t) {
|
||||
t.increments().primary();
|
||||
t.integer('post_id').notNull().unsigned().references('id').inTable('posts');
|
||||
t.integer('tag_id').notNull().unsigned().references('id').inTable('tags');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
down = function () {
|
||||
return when.all([
|
||||
knex.schema.dropTableIfExists('posts_tags'),
|
||||
knex.schema.dropTableIfExists('roles_users'),
|
||||
knex.schema.dropTableIfExists('permissions_users'),
|
||||
knex.schema.dropTableIfExists('permissions_roles'),
|
||||
knex.schema.dropTableIfExists('users')
|
||||
|
||||
]).then(function () {
|
||||
return when.all([
|
||||
knex.schema.dropTableIfExists('roles'),
|
||||
knex.schema.dropTableIfExists('settings'),
|
||||
knex.schema.dropTableIfExists('permissions'),
|
||||
knex.schema.dropTableIfExists('tags'),
|
||||
knex.schema.dropTableIfExists('posts')
|
||||
]);
|
||||
});
|
||||
};
|
||||
|
||||
exports.up = up;
|
||||
exports.down = down;
|
||||
exports.constraints = constraints;
|
|
@ -1,15 +1,24 @@
|
|||
var _ = require('underscore'),
|
||||
when = require('when'),
|
||||
series = require('when/sequence'),
|
||||
errors = require('../../errorHandling'),
|
||||
client = require('../../models/base').client,
|
||||
knex = require('../../models/base').knex,
|
||||
sequence = require('when/sequence'),
|
||||
|
||||
defaultSettings = require('../default-settings'),
|
||||
Settings = require('../../models/settings').Settings,
|
||||
fixtures = require('../fixtures'),
|
||||
schema = require('../schema'),
|
||||
|
||||
initialVersion = '000',
|
||||
defaultDatabaseVersion;
|
||||
schemaTables = _.keys(schema),
|
||||
defaultDatabaseVersion,
|
||||
|
||||
init,
|
||||
reset,
|
||||
migrateUp,
|
||||
migrateUpFreshDb,
|
||||
getTables;
|
||||
|
||||
// Default Database Version
|
||||
// The migration version number according to the hardcoded default settings
|
||||
|
@ -63,180 +72,199 @@ function setDatabaseVersion() {
|
|||
.update({ 'value': defaultDatabaseVersion });
|
||||
}
|
||||
|
||||
function createTable(table) {
|
||||
return knex.schema.createTable(table, function (t) {
|
||||
var column,
|
||||
columnKeys = _.keys(schema[table]);
|
||||
_.each(columnKeys, function (key) {
|
||||
if (schema[table][key].hasOwnProperty('maxlength')) {
|
||||
column = t[schema[table][key].type](key, schema[table][key].maxlength);
|
||||
} else {
|
||||
column = t[schema[table][key].type](key);
|
||||
}
|
||||
if (schema[table][key].hasOwnProperty('nullable') && schema[table][key].nullable === true) {
|
||||
column.nullable();
|
||||
} else {
|
||||
column.notNullable();
|
||||
}
|
||||
if (schema[table][key].hasOwnProperty('primary') && schema[table][key].primary === true) {
|
||||
column.primary();
|
||||
}
|
||||
if (schema[table][key].hasOwnProperty('unique') && schema[table][key].unique) {
|
||||
column.unique();
|
||||
}
|
||||
if (schema[table][key].hasOwnProperty('unsigned') && schema[table][key].unsigned) {
|
||||
column.unsigned();
|
||||
}
|
||||
if (schema[table][key].hasOwnProperty('references') && schema[table][key].hasOwnProperty('inTable')) {
|
||||
//check if table exists?
|
||||
column.references(schema[table][key].references);
|
||||
column.inTable(schema[table][key].inTable);
|
||||
}
|
||||
if (schema[table][key].hasOwnProperty('defaultTo')) {
|
||||
column.defaultTo(schema[table][key].defaultTo);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function deleteTable(table) {
|
||||
return knex.schema.dropTableIfExists(table);
|
||||
}
|
||||
|
||||
function getDeleteCommands(oldTables, newTables) {
|
||||
var deleteTables = _.difference(oldTables, newTables);
|
||||
if (!_.isEmpty(deleteTables)) {
|
||||
return _.map(deleteTables, function (table) {
|
||||
return function () {
|
||||
return deleteTable(table);
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getAddCommands(oldTables, newTables) {
|
||||
var addTables = _.difference(newTables, oldTables);
|
||||
if (!_.isEmpty(addTables)) {
|
||||
return _.map(addTables, function (table) {
|
||||
return function () {
|
||||
return createTable(table);
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getTablesFromSqlite3() {
|
||||
return knex.raw("select * from sqlite_master where type = 'table'").then(function (response) {
|
||||
return _.reject(_.pluck(response[0], 'tbl_name'), function (name) {
|
||||
return name === 'sqlite_sequence';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Basic suppport for PgSQL
|
||||
// Attention: not officially tested/supported
|
||||
function getTablesFromPgSQL() {
|
||||
return knex.raw("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'");
|
||||
}
|
||||
|
||||
function getTablesFromMySQL() {
|
||||
return knex.raw("show tables").then(function (response) {
|
||||
return _.flatten(_.map(response[0], function (entry) {
|
||||
return _.values(entry);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
// Check for whether data is needed to be bootstrapped or not
|
||||
init = function () {
|
||||
var self = this;
|
||||
// There are 4 possibilities:
|
||||
// 1. The database exists and is up-to-date
|
||||
// 2. The database exists but is out of date
|
||||
// 3. The database exists but the currentVersion setting does not or cannot be understood
|
||||
// 4. The database has not yet been created
|
||||
return getDatabaseVersion().then(function (databaseVersion) {
|
||||
var defaultVersion = getDefaultDatabaseVersion();
|
||||
if (databaseVersion === defaultVersion) {
|
||||
// 1. The database exists and is up-to-date
|
||||
return when.resolve();
|
||||
}
|
||||
if (databaseVersion < defaultVersion) {
|
||||
// 2. The database exists but is out of date
|
||||
// Migrate to latest version
|
||||
return self.migrateUp().then(function () {
|
||||
// Finally update the databases current version
|
||||
return setDatabaseVersion();
|
||||
});
|
||||
}
|
||||
if (databaseVersion > defaultVersion) {
|
||||
// 3. The database exists but the currentVersion setting does not or cannot be understood
|
||||
// In this case we don't understand the version because it is too high
|
||||
errors.logErrorAndExit(
|
||||
'Your database is not compatible with this version of Ghost',
|
||||
'You will need to create a new database'
|
||||
);
|
||||
}
|
||||
}, function (err) {
|
||||
if (err.message || err === 'Settings table does not exist') {
|
||||
// 4. The database has not yet been created
|
||||
// Bring everything up from initial version.
|
||||
return self.migrateUpFreshDb();
|
||||
}
|
||||
// 3. The database exists but the currentVersion setting does not or cannot be understood
|
||||
// In this case the setting was missing or there was some other problem
|
||||
errors.logErrorAndExit('There is a problem with the database', err.message || err);
|
||||
});
|
||||
};
|
||||
|
||||
// ### Reset
|
||||
// Delete all tables from the database in reverse order
|
||||
reset = function () {
|
||||
var tables = [];
|
||||
tables = _.map(schemaTables, function (table) {
|
||||
return function () {
|
||||
return deleteTable(table);
|
||||
};
|
||||
}).reverse();
|
||||
|
||||
return sequence(tables);
|
||||
};
|
||||
|
||||
// Only do this if we have no database at all
|
||||
migrateUpFreshDb = function () {
|
||||
var tables = [];
|
||||
tables = _.map(schemaTables, function (table) {
|
||||
return function () {
|
||||
return createTable(table);
|
||||
};
|
||||
});
|
||||
|
||||
return sequence(tables).then(function () {
|
||||
// Load the fixtures
|
||||
return fixtures.populateFixtures().then(function () {
|
||||
// Initialise the default settings
|
||||
return Settings.populateDefaults();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Migrate from a specific version to the latest
|
||||
migrateUp = function () {
|
||||
return getTables().then(function (oldTables) {
|
||||
var deleteCommands = getDeleteCommands(oldTables, schemaTables),
|
||||
addCommands = getAddCommands(oldTables, schemaTables),
|
||||
commands = [];
|
||||
if (!_.isEmpty(deleteCommands)) {
|
||||
commands = commands.concat(deleteCommands);
|
||||
}
|
||||
if (!_.isEmpty(addCommands)) {
|
||||
commands = commands.concat(addCommands);
|
||||
}
|
||||
if (!_.isEmpty(commands)) {
|
||||
return sequence(commands);
|
||||
}
|
||||
return;
|
||||
});
|
||||
};
|
||||
|
||||
getTables = function () {
|
||||
if (client === 'sqlite3') {
|
||||
return getTablesFromSqlite3();
|
||||
}
|
||||
if (client === 'mysql') {
|
||||
return getTablesFromMySQL();
|
||||
}
|
||||
if (client === 'pg') {
|
||||
return getTablesFromPgSQL();
|
||||
}
|
||||
return when.reject("No support for database client " + client);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getDatabaseVersion: getDatabaseVersion,
|
||||
// Check for whether data is needed to be bootstrapped or not
|
||||
init: function () {
|
||||
var self = this;
|
||||
|
||||
// There are 4 possibilities:
|
||||
// 1. The database exists and is up-to-date
|
||||
// 2. The database exists but is out of date
|
||||
// 3. The database exists but the currentVersion setting does not or cannot be understood
|
||||
// 4. The database has not yet been created
|
||||
return getDatabaseVersion().then(function (databaseVersion) {
|
||||
var defaultVersion = getDefaultDatabaseVersion();
|
||||
|
||||
if (databaseVersion === defaultVersion) {
|
||||
// 1. The database exists and is up-to-date
|
||||
return when.resolve();
|
||||
}
|
||||
|
||||
if (databaseVersion < defaultVersion) {
|
||||
// 2. The database exists but is out of date
|
||||
return self.migrateUpFromVersion(databaseVersion);
|
||||
}
|
||||
|
||||
if (databaseVersion > defaultVersion) {
|
||||
// 3. The database exists but the currentVersion setting does not or cannot be understood
|
||||
// In this case we don't understand the version because it is too high
|
||||
errors.logErrorAndExit(
|
||||
'Your database is not compatible with this version of Ghost',
|
||||
'You will need to create a new database'
|
||||
);
|
||||
}
|
||||
|
||||
}, function (err) {
|
||||
if (err.message || err === 'Settings table does not exist') {
|
||||
// 4. The database has not yet been created
|
||||
// Bring everything up from initial version.
|
||||
return self.migrateUpFreshDb();
|
||||
}
|
||||
|
||||
// 3. The database exists but the currentVersion setting does not or cannot be understood
|
||||
// In this case the setting was missing or there was some other problem
|
||||
errors.logErrorAndExit('There is a problem with the database', err.message || err);
|
||||
});
|
||||
},
|
||||
|
||||
// ### Reset
|
||||
// Migrate from where we are down to nothing.
|
||||
reset: function () {
|
||||
var self = this;
|
||||
|
||||
return getDatabaseVersion().then(function (databaseVersion) {
|
||||
// bring everything down from the current version
|
||||
return self.migrateDownFromVersion(databaseVersion);
|
||||
}, function () {
|
||||
// If the settings table doesn't exist, bring everything down from initial version.
|
||||
return self.migrateDownFromVersion(initialVersion);
|
||||
});
|
||||
},
|
||||
|
||||
// Only do this if we have no database at all
|
||||
migrateUpFreshDb: function () {
|
||||
var migration = require('./' + initialVersion);
|
||||
return migration.up().then(function () {
|
||||
// Load the fixtures
|
||||
return fixtures.populateFixtures().then(function () {
|
||||
// Initialise the default settings
|
||||
return Settings.populateDefaults();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Migrate from a specific version to the latest
|
||||
migrateUpFromVersion: function (version, max) {
|
||||
var versions = [],
|
||||
maxVersion = max || this.getVersionAfter(getDefaultDatabaseVersion()),
|
||||
currVersion = version,
|
||||
tasks = [];
|
||||
|
||||
// Aggregate all the versions we need to do migrations for
|
||||
while (currVersion !== maxVersion) {
|
||||
versions.push(currVersion);
|
||||
currVersion = this.getVersionAfter(currVersion);
|
||||
}
|
||||
|
||||
// Aggregate all the individual up calls to use in the series(...) below
|
||||
tasks = _.map(versions, function (taskVersion) {
|
||||
return function () {
|
||||
try {
|
||||
var migration = require('./' + taskVersion);
|
||||
return migration.up();
|
||||
} catch (e) {
|
||||
errors.logError(e);
|
||||
return when.reject(e);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Run each migration in series
|
||||
return series(tasks).then(function () {
|
||||
// Finally update the databases current version
|
||||
return setDatabaseVersion();
|
||||
});
|
||||
},
|
||||
|
||||
migrateDownFromVersion: function (version) {
|
||||
var self = this,
|
||||
versions = [],
|
||||
minVersion = this.getVersionBefore(initialVersion),
|
||||
currVersion = version,
|
||||
tasks = [];
|
||||
|
||||
// Aggregate all the versions we need to do migrations for
|
||||
while (currVersion !== minVersion) {
|
||||
versions.push(currVersion);
|
||||
currVersion = this.getVersionBefore(currVersion);
|
||||
}
|
||||
|
||||
// Aggregate all the individual up calls to use in the series(...) below
|
||||
tasks = _.map(versions, function (taskVersion) {
|
||||
return function () {
|
||||
try {
|
||||
var migration = require('./' + taskVersion);
|
||||
return migration.down();
|
||||
} catch (e) {
|
||||
errors.logError(e);
|
||||
return self.migrateDownFromVersion(initialVersion);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Run each migration in series
|
||||
return series(tasks);
|
||||
},
|
||||
|
||||
// Get the following version based on the current
|
||||
getVersionAfter: function (currVersion) {
|
||||
|
||||
var currVersionNum = parseInt(currVersion, 10),
|
||||
nextVersion;
|
||||
|
||||
// Default to initialVersion if not parsed
|
||||
if (isNaN(currVersionNum)) {
|
||||
currVersionNum = parseInt(initialVersion, 10);
|
||||
}
|
||||
|
||||
currVersionNum += 1;
|
||||
|
||||
nextVersion = String(currVersionNum);
|
||||
// Pad with 0's until 3 digits
|
||||
while (nextVersion.length < 3) {
|
||||
nextVersion = "0" + nextVersion;
|
||||
}
|
||||
|
||||
return nextVersion;
|
||||
},
|
||||
|
||||
getVersionBefore: function (currVersion) {
|
||||
var currVersionNum = parseInt(currVersion, 10),
|
||||
prevVersion;
|
||||
|
||||
if (isNaN(currVersionNum)) {
|
||||
currVersionNum = parseInt(initialVersion, 10);
|
||||
}
|
||||
|
||||
currVersionNum -= 1;
|
||||
|
||||
prevVersion = String(currVersionNum);
|
||||
// Pad with 0's until 3 digits
|
||||
while (prevVersion.length < 3) {
|
||||
prevVersion = "0" + prevVersion;
|
||||
}
|
||||
|
||||
return prevVersion;
|
||||
}
|
||||
init: init,
|
||||
reset: reset,
|
||||
migrateUp: migrateUp,
|
||||
migrateUpFreshDb: migrateUpFreshDb
|
||||
};
|
117
core/server/data/schema.js
Normal file
117
core/server/data/schema.js
Normal file
|
@ -0,0 +1,117 @@
|
|||
var db = {
|
||||
posts: {
|
||||
id: {type: 'increments', nullable: false, primary: true},
|
||||
uuid: {type: 'string', maxlength: 36, nullable: false},
|
||||
title: {type: 'string', maxlength: 150, nullable: false},
|
||||
slug: {type: 'string', maxlength: 150, nullable: false, unique: true},
|
||||
markdown: {type: 'text', maxlength: 16777215, nullable: true},
|
||||
html: {type: 'text', maxlength: 16777215, nullable: true},
|
||||
image: {type: 'text', maxlength: 2000, nullable: true},
|
||||
featured: {type: 'bool', nullable: false, defaultTo: false},
|
||||
page: {type: 'bool', nullable: false, defaultTo: false},
|
||||
status: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'draft'},
|
||||
language: {type: 'string', maxlength: 6, nullable: false, defaultTo: 'en_US'},
|
||||
meta_title: {type: 'string', maxlength: 150, nullable: true},
|
||||
meta_description: {type: 'string', maxlength: 200, nullable: true},
|
||||
author_id: {type: 'integer', nullable: false},
|
||||
created_at: {type: 'dateTime', nullable: false},
|
||||
created_by: {type: 'integer', nullable: false},
|
||||
updated_at: {type: 'dateTime', nullable: true},
|
||||
updated_by: {type: 'integer', nullable: true},
|
||||
published_at: {type: 'dateTime', nullable: true},
|
||||
published_by: {type: 'integer', nullable: true}
|
||||
},
|
||||
users: {
|
||||
id: {type: 'increments', nullable: false, primary: true},
|
||||
uuid: {type: 'string', maxlength: 36, nullable: false},
|
||||
name: {type: 'string', maxlength: 150, nullable: false, unique: true},
|
||||
slug: {type: 'string', maxlength: 150, nullable: false},
|
||||
password: {type: 'string', maxlength: 60, nullable: false},
|
||||
email: {type: 'string', maxlength: 254, nullable: false, unique: true},
|
||||
image: {type: 'text', maxlength: 2000, nullable: true},
|
||||
cover: {type: 'text', maxlength: 2000, nullable: true},
|
||||
bio: {type: 'string', maxlength: 200, nullable: true},
|
||||
website: {type: 'text', maxlength: 2000, nullable: true},
|
||||
location: {type: 'text', maxlength: 65535, nullable: true},
|
||||
accessibility: {type: 'text', maxlength: 65535, nullable: true},
|
||||
status: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'active'},
|
||||
language: {type: 'string', maxlength: 6, nullable: false, defaultTo: 'en_US'},
|
||||
meta_title: {type: 'string', maxlength: 150, nullable: true},
|
||||
meta_description: {type: 'string', maxlength: 200, nullable: true},
|
||||
last_login: {type: 'dateTime', nullable: true},
|
||||
created_at: {type: 'dateTime', nullable: false},
|
||||
created_by: {type: 'integer', nullable: false},
|
||||
updated_at: {type: 'dateTime', nullable: true},
|
||||
updated_by: {type: 'integer', nullable: true}
|
||||
},
|
||||
roles: {
|
||||
id: {type: 'increments', nullable: false, primary: true},
|
||||
uuid: {type: 'string', maxlength: 36, nullable: false},
|
||||
name: {type: 'string', maxlength: 150, nullable: false},
|
||||
description: {type: 'string', maxlength: 200, nullable: true},
|
||||
created_at: {type: 'dateTime', nullable: false},
|
||||
created_by: {type: 'integer', nullable: false},
|
||||
updated_at: {type: 'dateTime', nullable: true},
|
||||
updated_by: {type: 'integer', nullable: true}
|
||||
},
|
||||
roles_users: {
|
||||
id: {type: 'increments', nullable: false, primary: true},
|
||||
role_id: {type: 'integer', nullable: false},
|
||||
user_id: {type: 'integer', nullable: false}
|
||||
},
|
||||
permissions: {
|
||||
id: {type: 'increments', nullable: false, primary: true},
|
||||
uuid: {type: 'string', maxlength: 36, nullable: false},
|
||||
name: {type: 'string', maxlength: 150, nullable: false},
|
||||
object_type: {type: 'string', maxlength: 150, nullable: false},
|
||||
action_type: {type: 'string', maxlength: 150, nullable: false},
|
||||
object_id: {type: 'integer', nullable: true},
|
||||
created_at: {type: 'dateTime', nullable: false},
|
||||
created_by: {type: 'integer', nullable: false},
|
||||
updated_at: {type: 'dateTime', nullable: true},
|
||||
updated_by: {type: 'integer', nullable: true}
|
||||
},
|
||||
permissions_users: {
|
||||
id: {type: 'increments', nullable: false, primary: true},
|
||||
user_id: {type: 'integer', nullable: false},
|
||||
permission_id: {type: 'integer', nullable: false}
|
||||
},
|
||||
permissions_roles: {
|
||||
id: {type: 'increments', nullable: false, primary: true},
|
||||
role_id: {type: 'integer', nullable: false},
|
||||
permission_id: {type: 'integer', nullable: false}
|
||||
},
|
||||
settings: {
|
||||
id: {type: 'increments', nullable: false, primary: true},
|
||||
uuid: {type: 'string', maxlength: 36, nullable: false},
|
||||
key: {type: 'string', maxlength: 150, nullable: false, unique: true},
|
||||
value: {type: 'text', maxlength: 65535, nullable: true},
|
||||
type: {type: 'string', maxlength: 150, nullable: false, defaultTo: 'core'},
|
||||
created_at: {type: 'dateTime', nullable: false},
|
||||
created_by: {type: 'integer', nullable: false},
|
||||
updated_at: {type: 'dateTime', nullable: true},
|
||||
updated_by: {type: 'integer', nullable: true}
|
||||
},
|
||||
tags: {
|
||||
id: {type: 'increments', nullable: false, primary: true},
|
||||
uuid: {type: 'string', maxlength: 36, nullable: false},
|
||||
name: {type: 'string', maxlength: 150, nullable: false},
|
||||
slug: {type: 'string', maxlength: 150, nullable: false, unique: true},
|
||||
description: {type: 'string', maxlength: 200, nullable: true},
|
||||
parent_id: {type: 'integer', nullable: true},
|
||||
meta_title: {type: 'string', maxlength: 150, nullable: true},
|
||||
meta_description: {type: 'string', maxlength: 200, nullable: true},
|
||||
created_at: {type: 'dateTime', nullable: false},
|
||||
created_by: {type: 'integer', nullable: false},
|
||||
updated_at: {type: 'dateTime', nullable: true},
|
||||
updated_by: {type: 'integer', nullable: true}
|
||||
},
|
||||
posts_tags: {
|
||||
id: {type: 'increments', nullable: false, primary: true},
|
||||
post_id: {type: 'integer', nullable: false, unsigned: true, references: 'id', inTable: 'posts'},
|
||||
tag_id: {type: 'integer', nullable: false, unsigned: true, references: 'id', inTable: 'tags'}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = db;
|
|
@ -48,9 +48,8 @@ describe("Import", function () {
|
|||
it("imports data from 000", function (done) {
|
||||
var exportData;
|
||||
|
||||
// initialise database to version 000 - confusingly we have to set the max version to be one higher
|
||||
// than the migration version we want. Could just use migrate from fresh here... but this is more explicit
|
||||
migration.migrateUpFromVersion('000', '001').then(function () {
|
||||
// migrate to current version
|
||||
migration.migrateUp().then(function () {
|
||||
// Load the fixtures
|
||||
return fixtures.populateFixtures();
|
||||
}).then(function () {
|
||||
|
|
Loading…
Add table
Reference in a new issue