├── .gitignore ├── index.js ├── knexfile.js ├── migrations ├── 20170504155313_create_user_table.js └── 20170504165046_encrypt_user_password.js ├── package.json ├── public ├── app.js └── index.html └── store.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const bodyParser = require('body-parser') 3 | const store = require('./store') 4 | const app = express() 5 | app.use(express.static('public')) 6 | app.use(bodyParser.json()) 7 | app.post('/createUser', (req, res) => { 8 | store 9 | .createUser({ 10 | username: req.body.username, 11 | password: req.body.password 12 | }) 13 | .then(() => res.sendStatus(200)) 14 | }) 15 | app.post('/login', (req, res) => { 16 | store 17 | .authenticate({ 18 | username: req.body.username, 19 | password: req.body.password 20 | }) 21 | .then(({ success }) => { 22 | if (success) res.sendStatus(200) 23 | else res.sendStatus(401) 24 | }) 25 | }) 26 | app.listen(7555, () => { 27 | console.log('Server running on http://localhost:7555') 28 | }) 29 | -------------------------------------------------------------------------------- /knexfile.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | client: 'mysql', 3 | connection: { 4 | user: 'root', 5 | password: 'password', 6 | database: 'tutorial_node_database' 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /migrations/20170504155313_create_user_table.js: -------------------------------------------------------------------------------- 1 | exports.up = function (knex) { 2 | return knex.schema.createTable('user', function (t) { 3 | t.increments('id').primary() 4 | t.string('username').notNullable() 5 | t.string('password').notNullable() 6 | t.timestamps(false, true) 7 | }) 8 | } 9 | 10 | exports.down = function (knex) { 11 | return knex.schema.dropTableIfExists('user') 12 | } 13 | -------------------------------------------------------------------------------- /migrations/20170504165046_encrypt_user_password.js: -------------------------------------------------------------------------------- 1 | const { saltHashPassword } = require('../store') 2 | 3 | exports.up = async function up (knex) { 4 | await knex.schema.table('user', t => { 5 | t.string('salt').notNullable() 6 | t.string('encrypted_password').notNullable() 7 | }) 8 | const users = await knex('user') 9 | await Promise.all(users.map(convertPassword)) 10 | await knex.schema.table('user', t => { 11 | t.dropColumn('password') 12 | }) 13 | 14 | function convertPassword (user) { 15 | const { salt, hash } = saltHashPassword(user.password) 16 | return knex('user') 17 | .where({ id: user.id }) 18 | .update({ 19 | salt, 20 | encrypted_password: hash 21 | }) 22 | } 23 | } 24 | 25 | exports.down = function down (knex) { 26 | return knex.schema.table('user', t => { 27 | t.dropColumn('salt') 28 | t.dropColumn('encrypted_password') 29 | t.string('password').notNullable() 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorial-node-database", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/roberttod/tutorial-node-database.git" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/roberttod/tutorial-node-database/issues" 17 | }, 18 | "homepage": "https://github.com/roberttod/tutorial-node-database#readme", 19 | "dependencies": { 20 | "body-parser": "^1.17.1", 21 | "express": "^4.15.2", 22 | "knex": "^0.13.0", 23 | "mysql": "^2.13.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /public/app.js: -------------------------------------------------------------------------------- 1 | const CreateUser = document.querySelector('.CreateUser') 2 | CreateUser.addEventListener('submit', (e) => { 3 | e.preventDefault() 4 | const username = CreateUser.querySelector('.username').value 5 | const password = CreateUser.querySelector('.password').value 6 | post('/createUser', { username, password }) 7 | }) 8 | 9 | const Login = document.querySelector('.Login') 10 | Login.addEventListener('submit', (e) => { 11 | e.preventDefault() 12 | const username = Login.querySelector('.username').value 13 | const password = Login.querySelector('.password').value 14 | post('/login', { username, password }) 15 | .then(({ status }) => { 16 | if (status === 200) alert('login success') 17 | else alert('login failed') 18 | }) 19 | }) 20 | 21 | function post (path, data) { 22 | return window.fetch(path, { 23 | method: 'POST', 24 | headers: { 25 | 'Accept': 'application/json', 26 | 'Content-Type': 'application/json' 27 | }, 28 | body: JSON.stringify(data) 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Node database tutorial 5 | 6 | 7 |
8 |

Login

9 | 10 | 11 | 12 |
13 |
14 |

Create account

15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /store.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto') 2 | const knex = require('knex')(require('./knexfile')) 3 | 4 | module.exports = { 5 | createUser ({ username, password }) { 6 | console.log(`Add user ${username}`) 7 | const { salt, hash } = saltHashPassword({ password }) 8 | return knex('user').insert({ 9 | salt, 10 | encrypted_password: hash, 11 | username 12 | }) 13 | }, 14 | authenticate ({ username, password }) { 15 | console.log(`Authenticating user ${username}`) 16 | return knex('user').where({ username }) 17 | .then(([user]) => { 18 | if (!user) return { success: false } 19 | const { hash } = saltHashPassword({ 20 | password, 21 | salt: user.salt 22 | }) 23 | return { success: hash === user.encrypted_password } 24 | }) 25 | } 26 | } 27 | 28 | function saltHashPassword ({ 29 | password, 30 | salt = randomString() 31 | }) { 32 | const hash = crypto 33 | .createHmac('sha512', salt) 34 | .update(password) 35 | return { 36 | salt, 37 | hash: hash.digest('hex') 38 | } 39 | } 40 | 41 | function randomString () { 42 | return crypto.randomBytes(4).toString('hex') 43 | } 44 | --------------------------------------------------------------------------------