2
1
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2023-12-13 21:00:40 +01:00

Add schema.js

closes #1398
closes #1399
closes #1400
- added schema.js with database version '000'
- refactored migration to use schema.js
- if new table is added to schema.js and databaseVersion is increased, table will be added
- if new table is deleted to schema.js and databaseVersion is increased, table will be deleted
- alter table from issue #1400 is delayed until knex supports column modification
- changed import pre checks to work again (will be refactored separately)
- added basic PostgreSQL support (Attention: not supported/tested)
- changed error handling in server.js
This commit is contained in:
Sebastian Gierlinger 2013-11-18 15:21:15 +01:00
parent 5c90ef286c
commit 639c0d0627
7 changed files with 335 additions and 482 deletions

View file

@ -396,4 +396,6 @@ when(ghost.init()).then(function () {
} }
}); });
}).otherwise(errors.logAndThrowError); }, function (err) {
errors.logErrorAndExit(err);
});

View file

@ -7,6 +7,7 @@ var Ghost = require('../../ghost'),
when = require('when'), when = require('when'),
nodefn = require('when/node/function'), nodefn = require('when/node/function'),
_ = require('underscore'), _ = require('underscore'),
schema = require('../data/schema'),
ghost = new Ghost(), ghost = new Ghost(),
db; db;
@ -75,8 +76,7 @@ db = {
.then(function (fileContents) { .then(function (fileContents) {
var importData, var importData,
error = "", error = "",
constraints = require('../data/migration/' + databaseVersion).constraints, tableKeys = _.keys(schema);
constraintkeys = _.keys(constraints);
// Parse the json data // Parse the json data
try { try {
@ -89,20 +89,20 @@ db = {
return when.reject(new Error("Import data does not specify version")); 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) { _.each(importData.data[constkey], function (elem) {
var prop; var prop;
for (prop in elem) { for (prop in elem) {
if (elem.hasOwnProperty(prop)) { if (elem.hasOwnProperty(prop)) {
if (constraints[constkey].hasOwnProperty(prop)) { if (schema[constkey].hasOwnProperty(prop)) {
if (elem.hasOwnProperty(prop)) { if (elem.hasOwnProperty(prop)) {
if (!_.isNull(elem[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 += 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 { } else {
if (!constraints[constkey][prop].nullable) { if (!schema[constkey][prop].nullable) {
error += error !== "" ? "<br>" : ""; error += error !== "" ? "<br>" : "";
error += "Property '" + prop + "' is not nullable (element:" + constkey + " / id:" + elem.id + ")"; error += "Property '" + prop + "' is not nullable (element:" + constkey + " / id:" + elem.id + ")";
} }

View file

@ -1,37 +1,13 @@
var when = require('when'), var when = require('when'),
_ = require('underscore'), _ = require('underscore'),
migration = require('../migration'), migration = require('../migration'),
client = require('../../models/base').client,
knex = require('../../models/base').knex, knex = require('../../models/base').knex,
schema = require('../schema'),
exporter; 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 () { exporter = function () {
var tablesToExport; var tablesToExport = _.keys(schema);
if (client === 'sqlite3') {
tablesToExport = getTablesFromSqlite3();
} else if (client === 'mysql') {
tablesToExport = getTablesFromMySQL();
} else {
return when.reject("No exporter for database client " + client);
}
return when.join(migration.getDatabaseVersion(), tablesToExport).then(function (results) { return when.join(migration.getDatabaseVersion(), tablesToExport).then(function (results) {
var version = results[0], var version = results[0],

View file

@ -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;

View file

@ -1,15 +1,24 @@
var _ = require('underscore'), var _ = require('underscore'),
when = require('when'), when = require('when'),
series = require('when/sequence'),
errors = require('../../errorHandling'), errors = require('../../errorHandling'),
client = require('../../models/base').client,
knex = require('../../models/base').knex, knex = require('../../models/base').knex,
sequence = require('when/sequence'),
defaultSettings = require('../default-settings'), defaultSettings = require('../default-settings'),
Settings = require('../../models/settings').Settings, Settings = require('../../models/settings').Settings,
fixtures = require('../fixtures'), fixtures = require('../fixtures'),
schema = require('../schema'),
initialVersion = '000', initialVersion = '000',
defaultDatabaseVersion; schemaTables = _.keys(schema),
defaultDatabaseVersion,
init,
reset,
migrateUp,
migrateUpFreshDb,
getTables;
// Default Database Version // Default Database Version
// The migration version number according to the hardcoded default settings // The migration version number according to the hardcoded default settings
@ -63,13 +72,93 @@ function setDatabaseVersion() {
.update({ 'value': defaultDatabaseVersion }); .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);
}));
});
}
module.exports = {
getDatabaseVersion: getDatabaseVersion,
// Check for whether data is needed to be bootstrapped or not // Check for whether data is needed to be bootstrapped or not
init: function () { init = function () {
var self = this; var self = this;
// There are 4 possibilities: // There are 4 possibilities:
// 1. The database exists and is up-to-date // 1. The database exists and is up-to-date
// 2. The database exists but is out of date // 2. The database exists but is out of date
@ -77,17 +166,18 @@ module.exports = {
// 4. The database has not yet been created // 4. The database has not yet been created
return getDatabaseVersion().then(function (databaseVersion) { return getDatabaseVersion().then(function (databaseVersion) {
var defaultVersion = getDefaultDatabaseVersion(); var defaultVersion = getDefaultDatabaseVersion();
if (databaseVersion === defaultVersion) { if (databaseVersion === defaultVersion) {
// 1. The database exists and is up-to-date // 1. The database exists and is up-to-date
return when.resolve(); return when.resolve();
} }
if (databaseVersion < defaultVersion) { if (databaseVersion < defaultVersion) {
// 2. The database exists but is out of date // 2. The database exists but is out of date
return self.migrateUpFromVersion(databaseVersion); // Migrate to latest version
return self.migrateUp().then(function () {
// Finally update the databases current version
return setDatabaseVersion();
});
} }
if (databaseVersion > defaultVersion) { if (databaseVersion > defaultVersion) {
// 3. The database exists but the currentVersion setting does not or cannot be understood // 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 // In this case we don't understand the version because it is too high
@ -96,147 +186,85 @@ module.exports = {
'You will need to create a new database' 'You will need to create a new database'
); );
} }
}, function (err) { }, function (err) {
if (err.message || err === 'Settings table does not exist') { if (err.message || err === 'Settings table does not exist') {
// 4. The database has not yet been created // 4. The database has not yet been created
// Bring everything up from initial version. // Bring everything up from initial version.
return self.migrateUpFreshDb(); return self.migrateUpFreshDb();
} }
// 3. The database exists but the currentVersion setting does not or cannot be understood // 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 // 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); errors.logErrorAndExit('There is a problem with the database', err.message || err);
}); });
}, };
// ### Reset // ### Reset
// Migrate from where we are down to nothing. // Delete all tables from the database in reverse order
reset: function () { reset = function () {
var self = this; var tables = [];
tables = _.map(schemaTables, function (table) {
return function () {
return deleteTable(table);
};
}).reverse();
return getDatabaseVersion().then(function (databaseVersion) { return sequence(tables);
// 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 // Only do this if we have no database at all
migrateUpFreshDb: function () { migrateUpFreshDb = function () {
var migration = require('./' + initialVersion); var tables = [];
return migration.up().then(function () { tables = _.map(schemaTables, function (table) {
return function () {
return createTable(table);
};
});
return sequence(tables).then(function () {
// Load the fixtures // Load the fixtures
return fixtures.populateFixtures().then(function () { return fixtures.populateFixtures().then(function () {
// Initialise the default settings // Initialise the default settings
return Settings.populateDefaults(); return Settings.populateDefaults();
}); });
}); });
}, };
// Migrate from a specific version to the latest // Migrate from a specific version to the latest
migrateUpFromVersion: function (version, max) { migrateUp = function () {
var versions = [], return getTables().then(function (oldTables) {
maxVersion = max || this.getVersionAfter(getDefaultDatabaseVersion()), var deleteCommands = getDeleteCommands(oldTables, schemaTables),
currVersion = version, addCommands = getAddCommands(oldTables, schemaTables),
tasks = []; commands = [];
if (!_.isEmpty(deleteCommands)) {
// Aggregate all the versions we need to do migrations for commands = commands.concat(deleteCommands);
while (currVersion !== maxVersion) {
versions.push(currVersion);
currVersion = this.getVersionAfter(currVersion);
} }
if (!_.isEmpty(addCommands)) {
// Aggregate all the individual up calls to use in the series(...) below commands = commands.concat(addCommands);
tasks = _.map(versions, function (taskVersion) {
return function () {
try {
var migration = require('./' + taskVersion);
return migration.up();
} catch (e) {
errors.logError(e);
return when.reject(e);
} }
}; if (!_.isEmpty(commands)) {
return sequence(commands);
}
return;
}); });
// 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 getTables = function () {
return series(tasks); if (client === 'sqlite3') {
}, return getTablesFromSqlite3();
// 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);
} }
if (client === 'mysql') {
currVersionNum += 1; return getTablesFromMySQL();
nextVersion = String(currVersionNum);
// Pad with 0's until 3 digits
while (nextVersion.length < 3) {
nextVersion = "0" + nextVersion;
} }
if (client === 'pg') {
return nextVersion; return getTablesFromPgSQL();
},
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;
} }
return when.reject("No support for database client " + client);
};
module.exports = {
getDatabaseVersion: getDatabaseVersion,
init: init,
reset: reset,
migrateUp: migrateUp,
migrateUpFreshDb: migrateUpFreshDb
}; };

117
core/server/data/schema.js Normal file
View 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;

View file

@ -48,9 +48,8 @@ describe("Import", function () {
it("imports data from 000", function (done) { it("imports data from 000", function (done) {
var exportData; var exportData;
// initialise database to version 000 - confusingly we have to set the max version to be one higher // migrate to current version
// than the migration version we want. Could just use migrate from fresh here... but this is more explicit migration.migrateUp().then(function () {
migration.migrateUpFromVersion('000', '001').then(function () {
// Load the fixtures // Load the fixtures
return fixtures.populateFixtures(); return fixtures.populateFixtures();
}).then(function () { }).then(function () {