diff --git a/.env.example b/.env.example index 94fd2df..df3d850 100644 --- a/.env.example +++ b/.env.example @@ -13,3 +13,8 @@ POSTGRES_DB=poke-admin # Test Variables API_URL=http://localhost:3000 + +# Admin User Variables +ADMIN_USER_ID=21247c46-90db-469d-a3fc-41febde52bf0 +ADMIN_USERNAME=admin +ADMIN_PASSWORD=password diff --git a/api/test/index.js b/api/test/index.js index a4e1ebf..59a3012 100644 --- a/api/test/index.js +++ b/api/test/index.js @@ -1,29 +1,28 @@ import axios from 'axios' -import bcrypt from 'bcrypt' import chai from 'chai' -import faker from 'faker' import noop from 'lodash/noop' import { chaiStruct } from 'chai-struct' import createApi from '../create-api' -import { knex, redis } from '../../database/connections' +import * as admin from '../../database/seeds/admin' +import { createKnex, createRedis } from '../../database/connections' chai.use(chaiStruct) let server +let knex +let redis let _knex let _redis let _trx before('start root transaction and insert test user into database', done => { + knex = createKnex() + redis = createRedis() knex.transaction(trx => { _trx = trx ;(async () => { try { - const { password: unhashed } = TEST_USER - const password = await bcrypt.hash(unhashed, 1) - await _trx - .insert({ ...TEST_USER, password }) - .into('users') + await admin.seed(trx) done() } catch (err) { @@ -74,10 +73,5 @@ export const client = axios.create({ baseURL: process.env.API_URL }) -export const TEST_USER = { - userId: faker.random.uuid(), - username: 'admin', - password: 'password' -} - -export { expect } from 'chai' +export const { expect } = chai +export const { user: TEST_USER } = admin diff --git a/client/lib/authorize.js b/client/lib/authorize.js new file mode 100644 index 0000000..8090802 --- /dev/null +++ b/client/lib/authorize.js @@ -0,0 +1,12 @@ +import noop from 'lodash/noop' + +export default function authorize(getInitialProps = noop) { + return ({ res, router, session, isServer, ...ctx }) => { + if (!session.user) { + return isServer + ? res.redirect('/login') + : router.replace('/login') + } + return getInitialProps({ res, router, session, isServer, ...ctx }) + } +} diff --git a/client/lib/index.js b/client/lib/index.js index 67ce920..80d2cde 100644 --- a/client/lib/index.js +++ b/client/lib/index.js @@ -1 +1,2 @@ +export { default as authorize } from './authorize' export { default as isServer } from './is-server' diff --git a/client/pages/index.jsx b/client/pages/index.jsx index aa4aafd..6e80525 100644 --- a/client/pages/index.jsx +++ b/client/pages/index.jsx @@ -1,4 +1,5 @@ -import { Consumer } from '../lib' +import { authorize } from '../lib' +import { Consumer } from '../services' export default function Index() { return ( @@ -11,3 +12,5 @@ export default function Index() { ) } + +Index.getInitialProps = authorize() diff --git a/database/connections.js b/database/connections.js index 7f15b12..05172a5 100644 --- a/database/connections.js +++ b/database/connections.js @@ -6,13 +6,13 @@ import camelKeys from './lib/camel-keys' promisifyAll(RedisClient.prototype) -export const knex = Knex({ +export const createKnex = () => Knex({ client: 'pg', connection: process.env.POSTGRES_URL, postProcessResponse: camelKeys, wrapIdentifier: (value, wrap) => wrap(snakeCase(value)) }) -export const redis = new RedisClient({ +export const createRedis = () => new RedisClient({ url: process.env.REDIS_URL }) diff --git a/database/seeds/__dev__.js b/database/seeds/__dev__.js new file mode 100644 index 0000000..de209a8 --- /dev/null +++ b/database/seeds/__dev__.js @@ -0,0 +1,19 @@ +import path from 'path' +import { createKnex, createRedis } from '../connections' + +(async () => { + const knex = createKnex() + const redis = createRedis() + try { + await redis.flushallAsync() + await knex.seed.run({ + directory: path.join(__dirname, 'dev/') + }) + await knex.destroy() + await redis.quitAsync() + } + catch (err) { + console.error(err) + process.exit(1) + } +})() diff --git a/database/seeds/admin.js b/database/seeds/admin.js new file mode 100644 index 0000000..4ab0431 --- /dev/null +++ b/database/seeds/admin.js @@ -0,0 +1,16 @@ +import bcrypt from 'bcrypt' + +export const user = { + userId: process.env.ADMIN_USER_ID, + username: process.env.ADMIN_USERNAME, + password: process.env.ADMIN_PASSWORD +} + +export async function seed(knex) { + const saltRounds = process.env.NODE_ENV === 'production' ? 12 : 1 + const password = await bcrypt.hash(user.password, saltRounds) + await knex.raw('truncate table users cascade') + await knex + .insert({ ...user, password }) + .into('users') +} diff --git a/database/seeds/dev/index.js b/database/seeds/dev/index.js new file mode 100644 index 0000000..f7d3ff5 --- /dev/null +++ b/database/seeds/dev/index.js @@ -0,0 +1,26 @@ +import chalk from 'chalk' +import * as admin from '../admin' + +export async function seed(knex) { + + const trySeed = (knex => async (script, description) => { + /* eslint-disable no-console */ + console.log(chalk.white(`Running seed script for ${description}...`)) + try { + await script.seed(knex) + console.log(chalk.green('done')) + } + catch (err) { + console.error(err) + console.error(chalk.red(`ERROR:Failed to run seed script for ${description}.`)) + process.exit(1) + } + })(knex) + + console.log('\n') + + await trySeed(admin, 'admin user') + await admin.seed(knex) + + console.log('\n') +} diff --git a/package-lock.json b/package-lock.json index 4b9475f..d567878 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2313,9 +2313,9 @@ } }, "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -3706,12 +3706,6 @@ } } }, - "faker": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/faker/-/faker-4.1.0.tgz", - "integrity": "sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8=", - "dev": true - }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", @@ -5472,6 +5466,16 @@ "v8flags": "^3.1.1" }, "dependencies": { + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, "debug": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz", @@ -8184,6 +8188,16 @@ "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-1.0.1.tgz", "integrity": "sha1-Iqk2kB4wKa/NdUfrRIfOtpejvwg=" }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, "packet-reader": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-0.3.1.tgz", diff --git a/package.json b/package.json index c67fa0f..97b9aef 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,10 @@ "db:up": "pg-bump up", "db:down": "pg-bump down", "db:cycle": "run-s -s db:down db:up", + "db:seed": "node -r dotenv/config -r esm database/seeds/__dev__.js", "build": "next build client/", "start": "node -r dotenv/config -r esm .", - "dev": "run-s -s db:cycle && nodemon .", + "dev": "run-s -s db:cycle db:seed && nodemon .", "lint": "eslint --fix .", "test:api": "mocha --opts api/test/mocha.opts", "tdd:api": "run-s -s db:cycle 'test:api -- -w -R min'", @@ -43,7 +44,7 @@ "text-summary" ], "include": [ - "**/*.js" + "api/**/*.js" ], "exclude": [ "**/test/**", @@ -142,6 +143,7 @@ "bcrypt": "3.0.4", "bluebird": "3.5.3", "boom": "7.3.0", + "chalk": "2.4.2", "connect-redis": "3.4.0", "dotenv": "6.2.0", "esm": "3.2.14", @@ -175,7 +177,6 @@ "eslint-plugin-promise": "4.0.1", "eslint-plugin-react": "7.12.4", "eslint-plugin-standard": "4.0.0", - "faker": "4.1.0", "husky": "1.3.1", "lint-staged": "8.1.5", "mocha": "6.0.2", diff --git a/server/index.js b/server/index.js index e4e7947..01860db 100644 --- a/server/index.js +++ b/server/index.js @@ -1,7 +1,9 @@ import createServer from './create-server' -import { knex, redis } from '../database/connections' +import { createKnex, createRedis } from '../database/connections' const dev = process.env.NODE_ENV !== 'production' +const knex = createKnex() +const redis = createRedis() ;(async () => { const server = await createServer({ dev, knex, redis })