diff --git a/backend/db.coffee b/backend/db.coffee index 12757c9..03517e8 100644 --- a/backend/db.coffee +++ b/backend/db.coffee @@ -1,4 +1,4 @@ -# libraries +# node libraries bcrypt = (require 'bcrypt') sqlite3 = (require 'sqlite3').verbose() logger = (require 'logging').default 'db' @@ -28,10 +28,11 @@ FFTCGDB = (filename, truncate) -> user integer PRIMARY KEY, login text NOT NULL COLLATE NOCASE, pwdhash text NOT NULL, + settings text, UNIQUE(login) ); ''', (err) -> - FFTCGLOG.error err.message if err + logger.error err.message if err that.db.run 'DROP TABLE IF EXISTS decks;', (err) -> logger.error err.message if err @@ -46,7 +47,7 @@ FFTCGDB = (filename, truncate) -> ''', (err) -> logger.error err.message if err - logger.info 'recreated DB' + logger.info 'recreated sqlite3 db' return @@ -74,7 +75,7 @@ FFTCGDB::register = (login, password) -> # hash password bcrypt.hash password, saltRounds, (err, hash) -> if err - logger.info "reg: hash fail for name '#{login}'" + logger.warn "reg: hash fail for name '#{login}'" reject 'hash' # try creating row in users table @@ -117,7 +118,7 @@ FFTCGDB::login = (login, password) -> else bcrypt.compare password, row.pwdhash, (err, res) -> if err - logger.info "login: hash fail for name '#{login}'" + logger.warn "login: hash fail for name '#{login}'" reject 'hash' if res == true diff --git a/backend/package.json b/backend/package.json index 7816a8a..153f5b7 100644 --- a/backend/package.json +++ b/backend/package.json @@ -22,6 +22,7 @@ "fastify-static": "^1.1.0", "fastify-ws": "^1.0.0", "logging": "^3.2.0", + "redis": "^2.8.0", "sqlite3": "^4.0.4" } } diff --git a/backend/routes.coffee b/backend/routes.coffee index 22c2bbc..de46239 100644 --- a/backend/routes.coffee +++ b/backend/routes.coffee @@ -1,13 +1,15 @@ # node libraries -express = (require 'express') path = (require 'path') # my libraries FFTCGDB = (require './db') -logger = (require 'logging').default 'router' +FFTCGSESSION = (require './session') +logger = (require 'logging').default 'routes' -# open fftcg db -fftcgdb = new FFTCGDB path.resolve(__dirname, 'fftcg.db') +# open fftcg.db (persistent data) +fftcgdb = new FFTCGDB path.resolve(__dirname, 'fftcg.db'), true +# open session storage (volatile data) +session = new FFTCGSESSION module.exports = [ # test @@ -15,6 +17,7 @@ module.exports = [ method: 'POST' handler: (request, reply) -> logger.info 'Cookies', request.cookies + logger.info 'Body', request.body logger.info 'Query', request.query logger.info 'Params', request.params @@ -26,16 +29,16 @@ module.exports = [ url: '/user/register' method: 'POST' handler: (request, reply) -> - fftcgdb.register req.body.login, req.body.password + fftcgdb.register request.body.login, request.body.password .then (user) -> - # registration successful, return JSON status + # registration successful reply.send status: 'ok' user: user.user login: user.login .catch (err) -> - # registration failed, return JSON status + # registration failed reply.send status: 'fail' text: err @@ -44,20 +47,28 @@ module.exports = [ url: '/user/login' method: 'POST' handler: (request, reply) -> - fftcgdb.login req.body.login, req.body.password + session_id = request.cookies.session + logger.info session_id + session.action session_id .then (user) -> - # login successful, save stuff in cookie + logger.info user + .catch (err) -> + logger.error err + + fftcgdb.login request.body.login, request.body.password + .then (user) -> + # login successful reply.setCookie 'user', JSON.stringify user # return JSON status - res.json + reply.send status: 'ok' user: user.user login: user.login .catch (err) -> - # login failed, return JSON status - res.json + # login failed + reply.send status: 'fail' text: err , diff --git a/backend/server_old.coffee b/backend/server_old.coffee deleted file mode 100644 index 43e8460..0000000 --- a/backend/server_old.coffee +++ /dev/null @@ -1,40 +0,0 @@ -# node libraries -bodyParser = (require 'body-parser') -express = (require 'express') -sharedSession = (require 'express-socket.io-session') -helmet = (require 'helmet') -http = (require 'http') -path = (require 'path') -logger = (require 'logging').default 'FFTCG' - -# my libraries -FFTCGSOCKET = (require './socket') -FFTCGSESSION = (require './session') -FFTCGROUTER = (require './router') - -# express framework -app = express() -app.use helmet() -app.use bodyParser.urlencoded - extended: true - -# sessions -sessionMiddleware = FFTCGSESSION(app) -app.use sessionMiddleware - -# routes -app.use FFTCGROUTER - -# socket.io -web = http.Server app -socket = new FFTCGSOCKET web, sharedSession sessionMiddleware - -# Create server -web.listen 3001, -> - logger.info 'Listening on port 3001 ...' - -# Handle termination -process.on 'SIGINT', -> - socket.close() - logger.info 'shutting down after SIGINT' - process.exit() diff --git a/backend/session.coffee b/backend/session.coffee index 10bf6ad..922242a 100644 --- a/backend/session.coffee +++ b/backend/session.coffee @@ -1,22 +1,66 @@ # node libraries -expressSession = (require 'express-session') -RedisStore = (require 'connect-redis')(expressSession) +redis = (require 'redis') +crypto = (require 'crypto') +logger = (require 'logging').default 'session' -module.exports = (app) -> - session = - secret: 'keyboard cat' - store: new RedisStore - host: 'redis' - port: 6379 - cookie: - httpOnly: true - sameSite: 'strict' - proxy: true - resave: true - saveUninitialized: true +# expiry times in seconds +TIMES = + minute: 60 + hour: 60 * 60 + day: 60 * 60 * 24 + week: 60 * 60 * 24 * 7 + month: 60 * 60 * 24 * 7 * 4 - if app.get 'env' == 'production' - app.set 'trust proxy', 1 - session.cookie.secure = true +EXPIRY = + # games expire 1 week after last action + game: 1 * TIMES.week + # logins expire 1 month after last action + login: 1 * TIMES.month - expressSession session + +FFTCGSESSION = () -> + @db = redis.createClient 6379, 'redis' + + @db.on 'error', (err) -> + logger.error err.message + + return + +FFTCGSESSION::login = (login) -> + that = @ + + new Promise (resolve, reject) -> + hmac = crypto.createHmac 'sha256', Math.random().toString() + hmac.update login + digest = hmac.digest 'hex' + + that.db.setex digest, EXPIRY.login, login, (err) -> + if err + reject err + + else + resolve digest + + +FFTCGSESSION::action = (digest) -> + that = @ + + new Promise (resolve, reject) -> + that.db.get digest (err, res) -> + logger.info 'err', err, 'res', res + + if err + reject err + + else if res == 0 + resolve null + + else + that.db.expire digest, EXPIRY.login, (err, res) -> + if err + reject err + + else + resolve res + +module.exports = FFTCGSESSION diff --git a/backend/tmpfront/index.html b/backend/tmpfront/index.html index e387ced..ff97b29 100644 --- a/backend/tmpfront/index.html +++ b/backend/tmpfront/index.html @@ -17,12 +17,21 @@ ws.send('Ping') // Send the message 'Ping' to the server } - console.log(JSON.stringify(document.cookie)) - axios.post('/test',{ - x: 123 + axios.post('/user/register',{ + login: 'jmm', + password: '123' }) .then( (response) => { - console.log(response) + console.log('register', response) + }) + + axios.post('/user/login',{ + login: 'jmm', + password: '123' + }) + .then( (response) => { + console.log('login', response) + console.log('cookie', document.cookie) }) diff --git a/backend/yarn.lock b/backend/yarn.lock index b777243..4ad05af 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -586,6 +586,11 @@ dot-prop@^4.1.0: dependencies: is-obj "^1.0.0" +double-ended-queue@^2.1.0-0: + version "2.1.0-0" + resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c" + integrity sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw= + duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" @@ -1996,6 +2001,25 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" +redis-commands@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.4.0.tgz#52f9cf99153efcce56a8f86af986bd04e988602f" + integrity sha512-cu8EF+MtkwI4DLIT0x9P8qNTLFhQD4jLfxLR0cCNkeGzs87FN6879JOJwNQR/1zD7aSYNbU0hgsV9zGY71Itvw== + +redis-parser@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-2.6.0.tgz#52ed09dacac108f1a631c07e9b69941e7a19504b" + integrity sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs= + +redis@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/redis/-/redis-2.8.0.tgz#202288e3f58c49f6079d97af7a10e1303ae14b02" + integrity sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A== + dependencies: + double-ended-queue "^2.1.0-0" + redis-commands "^1.2.0" + redis-parser "^2.6.0" + regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"