154 lines
4.3 KiB
CoffeeScript
154 lines
4.3 KiB
CoffeeScript
# node libraries
|
|
redis = (require 'redis')
|
|
crypto = (require 'crypto')
|
|
logger = (require 'logging').default 'session'
|
|
|
|
# expiry times in days
|
|
EXPIRY =
|
|
# games expire 1 week after creation
|
|
game: 7
|
|
# logins expire 1 month after last action
|
|
login: 30
|
|
|
|
|
|
class FFTCGSESSION
|
|
constructor: ->
|
|
@db = redis.createClient
|
|
host: 'redis'
|
|
port: 6379
|
|
|
|
@db.on 'error', (err) ->
|
|
logger.error err.message
|
|
|
|
sessionKey: (digest) -> "session.#{digest}"
|
|
gameKey: (digest) -> "game.#{digest}"
|
|
|
|
start: (userid) ->
|
|
new Promise (resolve) =>
|
|
# hash userid
|
|
hmac = crypto.createHmac 'sha256', Math.random().toString()
|
|
hmac.update userid.toString()
|
|
digest = hmac.digest 'base64'
|
|
|
|
# push (hash, userid) into DB for the configured timespan
|
|
@db.setex (@sessionKey digest), EXPIRY.login * 86400, userid, =>
|
|
logger.info "OK '#{@sessionKey digest}' created"
|
|
# return cookie data
|
|
resolve
|
|
value: digest
|
|
properties:
|
|
expires: EXPIRY.login
|
|
|
|
destroy: (digest) ->
|
|
new Promise (resolve, reject) =>
|
|
# delete hash immediately
|
|
@db.del (@sessionKey digest), (err, res) =>
|
|
if res == 0
|
|
reject null
|
|
else
|
|
logger.info "OK '#{@sessionKey digest}' deleted"
|
|
resolve null
|
|
|
|
check: (digest) ->
|
|
new Promise (resolve, reject) =>
|
|
# refresh expiry timer on digest
|
|
@db.expire (@sessionKey digest), EXPIRY.login * 86400, (err, res) =>
|
|
if res == 0
|
|
reject null
|
|
|
|
else
|
|
@db.get (@sessionKey digest), (err, res) =>
|
|
logger.debug "OK '#{@sessionKey digest}' resumed"
|
|
resolve res
|
|
|
|
newGame: (userid) ->
|
|
new Promise (resolve, reject) =>
|
|
# generate hash
|
|
hmac = crypto.createHmac 'sha256', Math.random().toString()
|
|
hmac.update userid.toString()
|
|
digest = hmac.digest 'base64'
|
|
|
|
# insert game key
|
|
@db.hsetnx (@gameKey digest), 'owner', userid, (err, res) =>
|
|
if res == 0
|
|
@db.del (@gameKey digest)
|
|
reject null
|
|
|
|
else
|
|
@db.expire (@gameKey digest), EXPIRY.game * 86400, (err, res) =>
|
|
if res == 0
|
|
@db.del (@gameKey digest)
|
|
reject null
|
|
|
|
else
|
|
# add game to active set
|
|
@db.sadd (@gameKey 'active'), (@gameKey digest), (err, res) =>
|
|
# return game ID
|
|
logger.info "OK '#{@gameKey digest}' created"
|
|
resolve digest
|
|
|
|
getGames: ->
|
|
# function to return all active gameKeys
|
|
activeGameKeys = (set, cursor) =>
|
|
# start iteration
|
|
set ?= new Set()
|
|
cursor ?= '0'
|
|
|
|
return new Promise (resolve, reject) =>
|
|
# scan "active" gameKey
|
|
@db.sscan (@gameKey 'active'), cursor, 'COUNT', '100', (err, res) ->
|
|
|
|
# add to results set
|
|
cursor = res[0]
|
|
for key in res[1]
|
|
set.add key
|
|
|
|
if cursor == '0'
|
|
# done on cursor = 0
|
|
resolve set
|
|
else
|
|
# recursive call (resolve one step deeper)
|
|
allGames set, cursor
|
|
.then (set) ->
|
|
resolve set
|
|
|
|
activeGameKeys().then (set) ->
|
|
logger.info "game count: #{Array.from(set).length}"
|
|
|
|
joinGame: (digest, userid) ->
|
|
new Promise (resolve, reject) =>
|
|
# refresh expiry timer on digest
|
|
@db.expire (@gameKey digest), EXPIRY.game * 86400, (err, res) =>
|
|
if res == 0
|
|
reject null
|
|
|
|
else
|
|
# insert opponent value
|
|
@db.hsetnx (@gameKey digest), 'opponent', userid, (err, res) =>
|
|
if res == 0
|
|
reject null
|
|
|
|
else
|
|
# return game ID
|
|
logger.info "OK '#{@gameKey digest}' joined"
|
|
resolve digest
|
|
|
|
updateGame: (digest, state) ->
|
|
new Promise (resolve, reject) =>
|
|
# refresh expiry timer on digest
|
|
@db.expire (@gameKey digest), EXPIRY.game * 86400, (err, res) =>
|
|
if res == 0
|
|
reject null
|
|
|
|
else
|
|
# update state value
|
|
@db.hset (@gameKey digest), 'state', (JSON.stringify state), (err, res) =>
|
|
if res == 0
|
|
reject null
|
|
|
|
else
|
|
# return game ID
|
|
logger.info "OK '#{@gameKey digest}' updated"
|
|
resolve digest
|
|
|
|
module.exports = new FFTCGSESSION
|