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:
parent
5c90ef286c
commit
639c0d0627
7 changed files with 335 additions and 482 deletions
|
@ -396,4 +396,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'),
|
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 + ")";
|
||||||
}
|
}
|
||||||
|
|
|
@ -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],
|
||||||
|
|
|
@ -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'),
|
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
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) {
|
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 () {
|
||||||
|
|
Loading…
Reference in a new issue