diff --git a/README.md b/README.md index 7492644..33063aa 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,30 @@ You can use `sample.env` as a starting point to fill out your config settings. C #### Bootstrap +------ + +Proposed change to use migrations to manage the schema using [node-pg-migrate](https://github.com/theoephraim/node-pg-migrate) + +Example Migrations are in the ``./migrations`` folder + +```shell +$ DATABASE_URL=myapp-development gulp db-migrate +``` + +``` +$ DATABASE_URL=myapp-development gulp db-rollback +``` + +This change would allow the schema to be managed and updated. + +One complexity I found was trying to figure out how to extract the database configuration in ``./src/server/config.js`` into a format that could be used in the gulpfile.js (where the NODE_ENV is not set in advance) + +The current gulp tasks ``db-migrate`` and ``db-rollback`` require the DATABASE_URL to be set on the command line, and won't run without it + +TODO: Do you have any thoughts on either using migrations or how to set the config + +------ + Once you clone the repo, install the dependencies, and set your configs, you will need to bootstrap the database with the initial tables. To bootstrap a NEW database, simply run: diff --git a/gulpfile.js b/gulpfile.js index 91b5dfd..9fdc1f6 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -171,6 +171,14 @@ gulp.task('bootstrap-db', shell.task([ 'export NODE_ENV=development; node src/server/repos/bootstrap.js' ])); +gulp.task('db-migrate', shell.task([ + 'export DATABASE_URL=' + process.env.DATABASE_URL + '; ./node_modules/node-pg-migrate/bin/pg-migrate up' +])); + +gulp.task('db-rollback', shell.task([ + 'export DATABASE_URL=' + process.env.DATABASE_URL + '; ./node_modules/node-pg-migrate/bin/pg-migrate down' +])); + gulp.task('certs', shell.task([ './make-self-signed-certs.sh' ])); diff --git a/migrations/1449786613021_create_auth_providers_lookup.js b/migrations/1449786613021_create_auth_providers_lookup.js new file mode 100644 index 0000000..8088952 --- /dev/null +++ b/migrations/1449786613021_create_auth_providers_lookup.js @@ -0,0 +1,21 @@ +exports.up = function(pgm) { + pgm.createTable( + 'auth_providers_lookup', { + id: { + type: 'serial', + primaryKey: true + }, + name: { + type: 'VARCHAR(255)', + unique: true, + notNull: true + } + }); + + pgm.sql( 'insert into auth_providers_lookup (name) VALUES (\'google\')' ); + pgm.sql( 'insert into auth_providers_lookup (name) VALUES (\'facebook\')' ); +}; + +exports.down = function(pgm) { + pgm.dropTable('auth_providers_lookup'); +}; diff --git a/migrations/1449786614903_create_users.js b/migrations/1449786614903_create_users.js new file mode 100644 index 0000000..9c02489 --- /dev/null +++ b/migrations/1449786614903_create_users.js @@ -0,0 +1,52 @@ +exports.up = function(pgm) { + + pgm.createTable( + 'users', { + id: { + type: 'serial', + primaryKey: true + }, + auth_provider_lookup_id_fkey: { + type: 'integer', + notNull: true, + references: 'auth_providers_lookup(id)' + }, + auth_provider_user_id: { + type: 'VARCHAR(255)', + notNull: true + }, + email_address: { + type: 'VARCHAR(255)', + notNull: false + }, + first_name: { + type: 'VARCHAR(255)', + notNull: false + }, + last_name: { + type: 'VARCHAR(255)', + notNull: false + }, + bio: { + type: 'VARCHAR(1024)', + notNull: false + }, + created: { + type: 'timestamp', + default: pgm.func('current_timestamp') + } + }); + + pgm.createIndex('users', ['email_address', 'auth_provider_user_id'], { + name: 'email_providerid_idx', + unique: true + }); +}; + +exports.down = function(pgm) { + pgm.dropIndex('users', ['email_address', 'auth_provider_user_id'], { + name: 'email_providerid_idx', + unique: true + }); + pgm.dropTable('users'); +}; diff --git a/migrations/1449789678567_create_items.js b/migrations/1449789678567_create_items.js new file mode 100644 index 0000000..c337226 --- /dev/null +++ b/migrations/1449789678567_create_items.js @@ -0,0 +1,31 @@ +exports.up = function(pgm) { + pgm.createTable( + 'items', { + id: { + type: 'serial', + primaryKey: true + }, + user_id_fkey: { + type: 'integer', + notNull: true, + references: 'users(id)' + }, + title: { + type: 'VARCHAR(255)', + notNull: false + }, + description: { + type: 'VARCHAR(255)', + notNull: false + }, + created: { + type: 'timestamp', + default: pgm.func('current_timestamp') + } + }); +}; + + +exports.down = function(pgm) { + pgm.dropTable('items'); +}; diff --git a/migrations/1449792379667_create_session.js b/migrations/1449792379667_create_session.js new file mode 100644 index 0000000..cb21b56 --- /dev/null +++ b/migrations/1449792379667_create_session.js @@ -0,0 +1,23 @@ +exports.up = function(pgm) { + + +//** Note this session table script follow the format as defined at https://github.com/voxpelli/node-connect-pg-simple/blob/master/table.sql +// The node-connect-pg-simple module uses this to persist sessions to our Postgres Database +// Postgres as the session store was used for Base as a +// convenient (insofar as not needing developers to go through the setup of another DB) +// and fast option for session storage +// Other options such as Redis may present faster alternatives... +var createSessionTable = 'CREATE TABLE \"session\" ( ' + + ' \"sid\" varchar NOT NULL COLLATE \"default\", ' + + ' \"sess\" json NOT NULL, ' + + ' \"expire\" timestamp(6) NOT NULL ' + +') ' + +'WITH (OIDS=FALSE); ' + +'ALTER TABLE \"session\" ADD CONSTRAINT \"session_pkey\" PRIMARY KEY (\"sid\") NOT DEFERRABLE INITIALLY IMMEDIATE;'; + + pgm.sql(createSessionTable); +}; + +exports.down = function(pgm) { + pgm.dropTable('session'); +}; diff --git a/package.json b/package.json index e811ddf..49e79f8 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ "bluebird": "^2.9.25", "body-parser": "^1.13.1", "bootstrap": "^3.3.2", + "browserslist": "~0.5.0", + "caniuse-db": "~1.0.30000278", "connect-pg-simple": "^3.0.1", "cookie-parser": "^1.3.5", "csurf": "^1.8.3", @@ -30,7 +32,9 @@ "lodash": "^3.4.0", "method-override": "^2.3.3", "moment": "^2.10.3", + "node-pg-migrate": "0.0.9", "node-uuid": "^1.4.3", + "num2fraction": "~1.1.0", "passport": "^0.2.2", "passport-facebook": "^2.0.0", "passport-google": "^0.3.0", @@ -38,6 +42,7 @@ "passport-http-bearer": "^1.0.1", "pg": "^4.3.0", "pg-query": "^0.11.0", + "postcss": "~5.0.2", "rangy": "^1.3.0-beta.2", "react": "^0.13.2", "react-bootstrap": "^0.20.3", @@ -47,11 +52,7 @@ "rewire": "^2.3.4", "ssl-root-cas": "^1.1.10", "superagent": "^0.21.0", - "validator": "^3.40.1", - "browserslist": "~0.5.0", - "num2fraction": "~1.1.0", - "caniuse-db": "~1.0.30000278", - "postcss": "~5.0.2" + "validator": "^3.40.1" }, "devDependencies": { "assert": "^1.3.0",