Add initial database migration

This commit is contained in:
Timothy Warren 2016-02-24 14:01:10 -05:00
parent a57b86257c
commit 8dac92464d
4 changed files with 138 additions and 6 deletions

4
.gitignore vendored
View File

@ -58,3 +58,7 @@ public/generated/*
# Don't commit environment file # Don't commit environment file
.env .env
# Don't commit database migration config file or dev database
knexfile.js
dev.sqlite3

54
app/migration_helpers.js Normal file
View File

@ -0,0 +1,54 @@
'use strict';
/**
* Migration helper methods
* @type {Object}
*/
module.exports = {
get_db_type(knex) {
switch(knex.client.config.client) {
case 'postgresql':
case 'postgres':
case 'pg':
return 'pg';
default:
return knex.client.config.client;
}
},
pg_timestamp_update_function () {
return `CREATE OR REPLACE FUNCTION progblog_update_modified_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ language 'plpgsql';`;
},
pg_create_timestamp_update_trigger(table) {
return `CREATE TRIGGER update_${table}_modtime
BEFORE UPDATE ON "${table}"
FOR EACH ROW
EXECUTE PROCEDURE progblog_update_modified_column();`;
},
pg_delete_timestamp_update_trigger(table) {
return `DROP TRIGGER IF EXISTS update_${table}_modtime ON "${table}";`;
},
sqlite3_create_timestamp_update_trigger(table) {
return `CREATE TRIGGER IF NOT EXISTS [UpdateModified]
BEFORE UPDATE ON "${table}"
FOR EACH ROW
BEGIN
UPDATE "${table}" SET updated_at = CURRENT_TIMESTAMP WHERE id = old.id;
END`;
},
sqlite3_delete_timestamp_update_trigger() {
return `DROP TRIGGER IF EXISTS [UpdateModified]`;
}
};

View File

@ -0,0 +1,74 @@
'use strict';
const helpers = require('../app/migration_helpers');
exports.up = function(knex, Promise) {
const dbType = helpers.get_db_type(knex);
// Defer has the promise resulting from creating the initial tables
let defer = knex.schema.createTableIfNotExists('users', (table) => {
table.comment('User authentication table');
table.increments()
.unsigned();
table.string('user');
table.binary('password_hash');
table.timestamp('created_at')
.defaultTo(knex.fn.now());
table.timestamp('updated_at')
.defaultTo(knex.fn.now());
}).then(() => {
return knex.schema.createTableIfNotExists('posts', (table) => {
table.comment('Blog post table');
table.increments()
.unsigned();
table.integer('created_by')
.unsigned()
.references('users.id');
table.string('uri')
.index()
.comment('The uri segment of the blog post');
table.string('title')
.comment('The title of the blog post');
table.text('content')
.comment('The content of the blog post');
table.timestamp('created_at')
.defaultTo(knex.fn.now());
table.timestamp('updated_at')
.defaultTo(knex.fn.now());
});
});
if ('pg' === dbType) {
// Create the function to update
return knex.schema.raw(helpers.pg_timestamp_update_function())
// Create the tables
.then(() => defer)
// Add trigger for tables
.then(() => knex.schema.raw(helpers.pg_create_timestamp_update_trigger('users')))
.then(() => knex.schema.raw(helpers.pg_create_timestamp_update_trigger('posts')));
} else if ('sqlite3' === dbType) {
return defer.then(() => knex.schema.raw(helpers.sqlite3_create_timestamp_update_trigger('users')))
.then(() => knex.schema.raw(helpers.sqlite3_create_timestamp_update_trigger('posts')));
} else {
return defer;
}
};
exports.down = function(knex, Promise) {
const dbType = helpers.get_db_type(knex);
let defer = knex.schema.dropTableIfExists('posts')
.then(() => knex.schema.dropTableIfExists('users'));
if ('pg' === dbType) {
return knex.schema.raw(helpers.pg_delete_timestamp_update_trigger('users'))
.then(() => knex.schema.raw(helpers.pg_delete_timestamp_update_trigger('posts')))
.then(() => defer);
} else if ('sqlite3' === dbType) {
return knex.schema.raw(helpers.sqlite3_delete_timestamp_update_trigger('users'))
.then(() => knex.schema.raw(helpers.sqlite3_delete_timestamp_update_trigger('posts')))
.then(() => defer);
} else {
return defer;
}
};

View File

@ -17,13 +17,16 @@
"glob": "^6.0.4", "glob": "^6.0.4",
"helmet": "^1.1.0", "helmet": "^1.1.0",
"highlight.js": "^9.1.0", "highlight.js": "^9.1.0",
"knex": "^0.10.0",
"lodash": "^4.5.0", "lodash": "^4.5.0",
"marked": "^0.3.5", "marked": "^0.3.5",
"morgan": "~1.6.1", "morgan": "~1.6.1",
"nodemon": "^1.9.0", "nodemon": "^1.9.0",
"scrypt": "^6.0.1",
"sqlite3": "^3.1.1",
"winston": "^2.1.1" "winston": "^2.1.1"
}, },
"description": "An Opinionated Take on express with use of ES6 features", "description": "A simple blog with built-in code snippet functionality",
"devDependencies": { "devDependencies": {
"chai": "^3.4.1", "chai": "^3.4.1",
"chai-as-promised": "^5.2.0", "chai-as-promised": "^5.2.0",
@ -42,9 +45,6 @@
"pre-commit": "^1.1.2", "pre-commit": "^1.1.2",
"supertest": "^1.1.0" "supertest": "^1.1.0"
}, },
"directories": {
"lib": "./lib"
},
"engines": { "engines": {
"node": ">4.0.0" "node": ">4.0.0"
}, },
@ -63,7 +63,7 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/timw4mail/crispy-train.git" "url": "https://git.timshomepage.net/timw4mail/ProgBlog.git"
}, },
"scripts": { "scripts": {
"start": "nodemon server.js", "start": "nodemon server.js",