From b3b86ecd9ade0a9600c54beb1aeddde45021f2e2 Mon Sep 17 00:00:00 2001 From: Syed Daanish Date: Sun, 22 Jun 2025 15:47:32 +0300 Subject: [PATCH] Fixes --- .gitignore | 1 + .rubocop.yml | 9 + Gemfile | 4 + Gemfile.lock | 10 + index.html | 57 +++++- logman.rb | 10 + mailer.rb | 31 ++++ main.rb | 145 ++++++--------- players.rb | 56 +++--- public/src/assets/fonts/changa.woff | Bin 0 -> 65864 bytes .../img/{logo_large.png => logo_lg.png} | Bin public/src/assets/style.css | 56 ++++++ public/src/js/accounts.js | 44 +++++ public/src/js/{index.js => game.js} | 0 public/src/js/game_renderer.js | 2 +- public/src/js/ui_renderer.js | 2 +- schema.sql | 6 +- session.rb | 171 ++++++++++++------ 18 files changed, 422 insertions(+), 182 deletions(-) create mode 100644 logman.rb create mode 100644 mailer.rb create mode 100644 public/src/assets/fonts/changa.woff rename public/src/assets/img/{logo_large.png => logo_lg.png} (100%) create mode 100644 public/src/js/accounts.js rename public/src/js/{index.js => game.js} (100%) diff --git a/.gitignore b/.gitignore index 11d8cc2..4424173 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.db tmp.* *.log +.env diff --git a/.rubocop.yml b/.rubocop.yml index ece38e3..b15522c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -16,6 +16,15 @@ Style/TrailingCommaInArrayLiteral: Style/FrozenStringLiteralComment: Enabled: false +Style/GlobalVars: + Enabled: false + +Style/MutableConstant: + Enabled: false + +Style/StringLiteralsInInterpolation: + Enabled: false + Layout/SpaceAroundOperators: Enabled: false diff --git a/Gemfile b/Gemfile index f1f87c4..c58a71b 100644 --- a/Gemfile +++ b/Gemfile @@ -11,3 +11,7 @@ gem "json" gem "base64" gem "zlib" + +gem "uri" + +gem "net-http" diff --git a/Gemfile.lock b/Gemfile.lock index b4f6a62..227a410 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,9 +3,12 @@ GEM specs: base64 (0.3.0) bigdecimal (3.2.2) + json (2.12.2) logger (1.7.0) mustermann (3.0.3) ruby2_keywords (~> 0.0.1) + net-http (0.6.0) + uri rack (3.1.16) rack-protection (4.1.1) base64 (>= 0.1.0) @@ -25,16 +28,23 @@ GEM rack-session (>= 2.0.0, < 3) tilt (~> 2.0) tilt (2.6.0) + uri (1.0.3) xxhash (0.6.0) + zlib (3.2.1) PLATFORMS ruby x86_64-linux DEPENDENCIES + base64 + json + net-http sequel sinatra + uri xxhash + zlib BUNDLED WITH 2.6.9 diff --git a/index.html b/index.html index 0eab6ad..50d74b5 100644 --- a/index.html +++ b/index.html @@ -1,16 +1,69 @@ - Infinsweeper + InfinSweeper + +
+ + + +
+ + +
+
- + + diff --git a/logman.rb b/logman.rb new file mode 100644 index 0000000..ee250fb --- /dev/null +++ b/logman.rb @@ -0,0 +1,10 @@ +# module for logging +module Logman + def self.log(log) + File.write("log/main.log", "[#{Time.now}] #{log}\n", mode: "a") + end + + def self.imp(log) + File.write("log/imp.log", "[#{Time.now}] #{log}\n", mode: "a") + end +end diff --git a/mailer.rb b/mailer.rb new file mode 100644 index 0000000..3632423 --- /dev/null +++ b/mailer.rb @@ -0,0 +1,31 @@ +# Mailer module +module Mail + def send(to, subject, body) + from_email = "noreply@infinsweeper.syedm.dev" + from_name = "InfinSweeper" + to = Array(to).map { |addr| { email_address: { address: addr, name: "" } } } + + payload = { + from: { + address: from_email, + name: from_name, + }, + to: to, + subject: subject, + htmlbody: body, + } + + uri = URI("https://api.zeptomail.com/v1.1/email") + req = Net::HTTP::Post.new(uri) + req["Authorization"] = "Zoho-enczapikey #{ENV_HASH["ZOHO_PASS"]}" + req["Content-Type"] = "application/json" + req.body = payload.to_json + + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + response = http.request(req) + + return if response.is_a?(Net::HTTPSuccess) + Logman.imp "[ZeptoMail ERROR] #{response.body}" + end +end diff --git a/main.rb b/main.rb index 2448fca..47e4b13 100644 --- a/main.rb +++ b/main.rb @@ -1,123 +1,93 @@ require "sinatra" require "json" - -require_relative "players" -require_relative "session" +require "base64" +require "zlib" +require "sequel" +require "xxhash" +require "net/http" +require "uri" ALPHANUM = [*"0".."9", *"A".."Z", *"a".."z", "-", "_"].freeze +env_data = File.read(".env") +ENV_HASH = {} + +env_data.each_line do |line| + if (match = line.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/)) + _, key, val = match + ENV_HASH[key] = val + end +end + +CODE_ENV = :dev + db_file = File.expand_path("infinsweeper.db") DB = Sequel.connect("sqlite:///#{db_file}", single_threaded: false) DB.run("PRAGMA foreign_keys = ON;") +$active_users = DB[:SignedInUsers].all.map { |x| [x[:code], x[:player]] }.to_h -signed_in_users = DB[:SignedInUsers].all.map { |x| [x[:code], x[:player]] }.to_h - -Thread.new do - loop do - now = Time.now - fifteen_days_ago = now - (60 * 60 * 24 * 15) - six_days_ago = now - (60 * 60 * 24 * 6) - old_sessions = (DB[:SignedInUsers].where { created_at < fifteen_days_ago }.all + - DB[:SignedInUsers].where { last_used_at < six_days_ago }.all).uniq { |s| s[:code] } - old_sessions.each do |session| - begin - DB[:SignedInUsers].where(code: session[:code]).delete - rescue StandardError => e - File.write("log/main.log", - "[#{Time.now}] Thread DB error: #{e.message} on #{session[:code]} for #{session[:player]}\n", - mode: "a") - end - signed_in_users.delete(session[:code]) - puts "Auto-logged out: #{session[:player]} (expired session)" - end - rescue StandardError => e - File.write("log/main.log", "[#{Time.now}] Thread error: #{e.message}\n", mode: "a") - ensure - sleep 60 * 60 * 24 - end -end +load "logman.rb" +load "mailer.rb" +load "players.rb" +load "session.rb" set :public_folder, "public" get "/" do + session = Sessions.new request, response + Logman.log session["message"] send_file "index.html" end get "/debug" do - return get_session_all(request, response).inspect + content_type :json + (Sessions.new request, response).all.to_json end -post "/new_player" do +post "/signup" do + session = Sessions.new request, response data = JSON.parse(request.body.read) if data["email"].nil? || data["pass"].nil? || data["username"].nil? status 400 return { "message" => "Bad request made!" }.to_json end - player = Players.mk_player(data["username"], data["email"], data["pass"]) - case player - when "Successfully registered!" - code = Array.new(24) { ALPHANUM.sample }.join - set_session(request, response, "user", code, code) - signed_in_users[code] = data["email"] - begin - DB["insert into SignedInUsers (code, player) values (?, ?)", code, data["email"]].insert - rescue Sequel::Error => e - File.write("log/main.log", "DB Error: #{e.message}\n", mode: "a") - status 500 - return { "message" => "Internal server error when signing you in!" }.to_json - end - status 200 - else - status 400 + signup_status = Players.mk_player(data["username"], data["email"], data["pass"]) + if signup_status[0] == 200 + login_status = session.login(data["username"], data["pass"]) + status login_status[0] + return { "message" => login_status[1] }.to_json end - return { "message" => player }.to_json + status signup_status[0] + return { "message" => signup_status[1] }.to_json end get "/verify/:code" do - if Players.verify(params[:code]) - status 200 - return { "message" => "Verified successfully!" }.to_json - else - status 400 - return { "message" => "Couldn't verify!" } - end + session = Sessions.new request, response + session["message"] = Players.verify(params[:code]) ? "Verified successfully!" : "Verification failed!" + redirect "/" end post "/login" do data = JSON.parse(request.body.read) - player = Players.authorized?(data["email"], data["pass"]) - if player - code = Array.new(24) { ALPHANUM.sample }.join - set_session(request, response, "user", code, code) - signed_in_users[code] = data["email"] - begin - DB["insert into SignedInUsers (code, player) values (?, ?)", code, data["email"]].insert - rescue Sequel::Error => e - File.write("log/main.log", "DB Error: #{e.message}\n", mode: "a") - status 500 - return { "message" => "Internal server error when signing you in!" }.to_json - end - status 200 - return { "message" => "Remember to verify your email!" }.to_json unless Players.verified?(data["email"]) - return { "message" => "Signed in successfully!" }.to_json - else - status 400 - return { "message" => "Couldn't sign you in!" }.to_json + session = Sessions.new request, response + uid = session["user"] + if $active_users[uid] && !session.logout(uid) + status 500 + return { "message" => "Internal server error when signing the existing session out!" }.to_json end + login_status = session.login(data["username"], data["pass"]) + status login_status[0] + return { "message" => login_status[1] }.to_json end post "/logout" do - uid = get_session(request, response, "user") - if signed_in_users[uid].nil? + session = Sessions.new request, response + uid = session["user"] + if $active_users[uid].nil? status 400 return { "message" => "Not signed in!" }.to_json end - signed_in_users.delete(uid) - rm_session(request, response, "user") - begin - DB["delete from SignedInUsers where code = ?", uid].delete - rescue Sequel::Error => e - File.write("log/main.log", "DB Error: #{e.message}\n", mode: "a") + unless session.logout(uid) status 500 return { "message" => "Internal server error when signing you out!" }.to_json end @@ -136,6 +106,10 @@ post "/forgot_password" do return { "message" => "Email sent successfully!" }.to_json end +get "/reset_password/:code" do + redirect "/?reset_code=#{params[:code]}" +end + post "/reset_password/:code" do data = JSON.parse(request.body.read) if data["pass"].nil? || params[:code].nil? @@ -152,14 +126,13 @@ post "/reset_password/:code" do end delete "/rm_player" do - uid = get_session(request, response, "user") - if uid.nil? || signed_in_users[uid].nil? + session = Sessions.new request, response + uid = session["user"] + if uid.nil? || $active_users[uid].nil? status 400 return { "message" => "Not signed in!" }.to_json end - if Players.rm_player(signed_in_users[uid]) - signed_in_users.delete(uid) - rm_session(request, response, "user") + if session.logout(uid) && Players.rm_player($active_users[uid]) status 200 return { "message" => "Sorry to see you go.." }.to_json else diff --git a/players.rb b/players.rb index 4b33596..4524a2c 100644 --- a/players.rb +++ b/players.rb @@ -1,21 +1,11 @@ -require "xxhash" -require "sequel" -require "json" - -ALPHANUM = [*"0".."9", *"A".."Z", *"a".."z", "-", "_"].freeze - # DataBase handler module module Players - db_file = File.expand_path("infinsweeper.db") - DB = Sequel.connect("sqlite:///#{db_file}", single_threaded: false) - DB.run("PRAGMA foreign_keys = ON;") - def self.list DB["select * from Players"].all end - def self.rm_player(email) - DB["delete from Players where email = ?", email].delete != 0 + def self.rm_player(username) + DB["delete from Players where username = ?", username].delete != 0 end def self.mk_player(username, email, pass) @@ -26,25 +16,24 @@ module Players pass.match?(/\A[a-zA-Z0-9_.!?@#$%^&*()+=-]+\z/) && pass.length >= 8 digest = XXhash.xxh32(pass, 1234) - - code = Array.new(24) { ALPHANUM.sample }.join + code = CODE_ENV == :prod ? Array.new(24) { ALPHANUM.sample }.join : "!" DB[ - "insert into Players (email, digest, username, activation_code) values (?, ?, ?, ?)", - email, digest, username, code + "insert into Players (username, digest, email, activation_code) values (?, ?, ?, ?)", + username, digest, email, code ].insert - send_email(:new, email, username, code) + send_email(:new, email, username, code) if CODE_ENV == :prod - "Successfully registered!" + [200, "Successfully signed up!"] rescue ArgumentError => e - e.message + [400, e.message] rescue Sequel::UniqueConstraintViolation - "Account already exists with this email or username!" + [400, "Account already exists with this username or username!"] end def self.verify(code) - DB["update Players set activation_code = ? where code = ?", "!", code].update != 0 + DB["update Players set activation_code = ? where activation_code = ?", "!", code].update != 0 end def self.unverified @@ -62,35 +51,36 @@ module Players def self.pass_reset(new_pass, code) digest = XXhash.xxh32(new_pass, 1234) - DB["update Players set digest = ? where new_pass_code = ?", digest, code].update != 0 + DB["update Players set digest = ?, new_pass_code = ? where new_pass_code = ?", digest, "", code].update != 0 end - def self.[](email) - DB["select * from Players where email = ?", email].first + def self.[](username) + DB["select * from Players where username = ?", username].first end - def self.[]=(email, data) - DB["update Players set data = ? where email = ?", data, email].update + def self.[]=(username, data) + DB["update Players set data = ? where username = ?", data, username].update end - def self.authorized?(email, pass) + def self.authorized?(username, pass) digest = XXhash.xxh32(pass, 1234) - player = self[email] + player = self[username] player && player[:digest].to_i == digest.to_i ? player : false end - def self.verified?(email) - player = self[email] - player && player[:code] == "!" + def self.verified?(username) + player = self[username] + Logger.log "Verified: #{player.inspect}\n" + player && player[:activation_code] == "!" end Thread.new do loop do unverified.each do |player| - rm_player(player[:email]) if player[:created_at] + 24 * 60 * 60 < Time.now + rm_player(player[:username]) if player[:created_at] + 24 * 60 * 60 < Time.now end rescue StandardError => e - File.write("log/main.log", "Thread error: #{e.message}\n", mode: "a") + Logger.log "Thread error: #{e.message}\n" ensure sleep 60 * 60 end diff --git a/public/src/assets/fonts/changa.woff b/public/src/assets/fonts/changa.woff new file mode 100644 index 0000000000000000000000000000000000000000..e41b363103c85c6cdde88aeaaf47d83d2dbcab5d GIT binary patch literal 65864 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 literal 0 HcmV?d00001 diff --git a/public/src/assets/img/logo_large.png b/public/src/assets/img/logo_lg.png similarity index 100% rename from public/src/assets/img/logo_large.png rename to public/src/assets/img/logo_lg.png diff --git a/public/src/assets/style.css b/public/src/assets/style.css index 4221350..4630f76 100644 --- a/public/src/assets/style.css +++ b/public/src/assets/style.css @@ -2,3 +2,59 @@ body { margin: 0; overflow: hidden; } + +@font-face { + font-family: "Changa"; + src: url("fonts/changa.woff") format("opentype"); + font-weight: normal; + font-style: normal; +} + +.header { + width: 100%; + height: 90px; + background-color: #1b262c; + border-bottom: 3px solid #90bdd9; + display: flex; + align-items: center; + justify-content: space-between; + font-family: "Changa"; +} + +.logo { + height: 50px; + position: relative; + top: 7px; +} + +.popup { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 500px; + height: 500px; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 9999; +} + +.popup-tab { + display: none; +} + +.header-right { + margin-right: 20px; +} + +.pixelart { + image-rendering: pixelated; + image-rendering: crisp-edges; + margin: 0 20px; +} + +#main-canvas { + height: calc(100vh - 90px); +} diff --git a/public/src/js/accounts.js b/public/src/js/accounts.js new file mode 100644 index 0000000..8c04f1b --- /dev/null +++ b/public/src/js/accounts.js @@ -0,0 +1,44 @@ +const login_form = document.getElementById("login-form"); +const login_button = document.getElementById("login-button"); + +login_button.onclick = () => { + document.getElementById("login").style.display = "block"; +}; + +login_form.onsubmit = async (e) => { + e.preventDefault(); + const username = login_form.username.value; + const pass = login_form.pass.value; + let response = await fetch("/login", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ username, pass }), + }); + response = await response.json(); + document.getElementById("login-info").innerText = response.message; +}; + +const signup_form = document.getElementById("signup-form"); +const signup_button = document.getElementById("signup-button"); + +signup_button.onclick = () => { + document.getElementById("signup").style.display = "block"; +}; + +signup_form.onsubmit = async (e) => { + e.preventDefault(); + const username = signup_form.username.value; + const email = signup_form.email.value; + const pass = signup_form.pass.value; + let response = await fetch("/signup", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ username, email, pass }), + }); + response = await response.json(); + document.getElementById("signup-info").innerText = response.message; +}; diff --git a/public/src/js/index.js b/public/src/js/game.js similarity index 100% rename from public/src/js/index.js rename to public/src/js/game.js diff --git a/public/src/js/game_renderer.js b/public/src/js/game_renderer.js index d78c89b..949f565 100644 --- a/public/src/js/game_renderer.js +++ b/public/src/js/game_renderer.js @@ -39,7 +39,7 @@ export default class GameRenderer { /** @type {HTMLCanvasElement} */ this.canvas = document.getElementById(CANVAS_ID); this.canvas.width = window.innerWidth; - this.canvas.height = window.innerHeight; + this.canvas.height = window.innerHeight - 90; /** @type {CanvasRenderingContext2D} */ this.ctx = this.canvas.getContext("2d"); this.ctx.imageSmoothingEnabled = false; diff --git a/public/src/js/ui_renderer.js b/public/src/js/ui_renderer.js index 2a2d310..f5ad28d 100644 --- a/public/src/js/ui_renderer.js +++ b/public/src/js/ui_renderer.js @@ -83,7 +83,7 @@ export default class UIRenderer extends GameRenderer { */ resize() { this.canvas.width = window.innerWidth; - this.canvas.height = window.innerHeight; + this.canvas.height = window.innerHeight - 90; this.ctx.imageSmoothingEnabled = false; } /** diff --git a/schema.sql b/schema.sql index e0e1cc6..8681d21 100644 --- a/schema.sql +++ b/schema.sql @@ -1,6 +1,6 @@ CREATE TABLE IF NOT EXISTS Players ( - email TEXT PRIMARY KEY, - username TEXT UNIQUE, + username TEXT PRIMARY KEY, + email TEXT UNIQUE, digest BLOB, data BLOB, activation_code TEXT, @@ -13,5 +13,5 @@ CREATE TABLE IF NOT EXISTS SignedInUsers ( player TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, last_used_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (player) REFERENCES Players (email) ON DELETE CASCADE + FOREIGN KEY (player) REFERENCES Players (username) ON DELETE CASCADE ); diff --git a/session.rb b/session.rb index f46e36e..d2ee380 100644 --- a/session.rb +++ b/session.rb @@ -1,62 +1,121 @@ -require "base64" -require "zlib" -require "json" - -def set_session(request, response, key, val, uid = nil) - session = request.cookies["session"] - session = session.nil? ? "{}" : Zlib::Inflate.inflate(Base64.decode64(session)) - session = JSON.parse(session) - session[key] = val - compressed = Zlib::Deflate.deflate(JSON.generate(session)) - encoded = Base64.encode64(compressed) - response.set_cookie("session", - value: encoded, - path: "/", - expires: Time.now + 360 * 24 * 60 * 60) - begin - DB["UPDATE SignedInUsers SET last_used_at = CURRENT_TIMESTAMP WHERE code = ?", uid].update if uid - rescue Sequel::Error => e - File.write("log/main.log", "DB Error: #{e.message} when updating last_used_at for #{uid}\n", mode: "a") +# class Sessions +class Sessions + def initialize(request, response) + @request = request + @response = response end -rescue JSON::ParserError, Zlib::Error - response.delete_cookie("session") -end -def get_session(request, response, key, uid = nil) - session = request.cookies["session"] - session = session.nil? ? "{}" : Zlib::Inflate.inflate(Base64.decode64(session)) - session = JSON.parse(session) - begin - DB["UPDATE SignedInUsers SET last_used_at = CURRENT_TIMESTAMP WHERE code = ?", uid].update if uid - rescue Sequel::Error => e - File.write("log/main.log", "DB Error: #{e.message} when updating last_used_at for #{uid}\n", mode: "a") + def signed_in?(code) + signed_in_users[code] end - session[key] -rescue JSON::ParserError, Zlib::Error - response.delete_cookie("session") - "" -end -def get_session_all(request, response) - session = request.cookies["session"] - session = session.nil? ? "{}" : Zlib::Inflate.inflate(Base64.decode64(session)) - JSON.parse(session) -rescue JSON::ParserError, Zlib::Error - response.delete_cookie("session") - "" -end + def logout(uid) + signed_in_users.delete(uid) + delete("user") + DB["delete from SignedInUsers where code = ?", uid].delete + true + rescue Sequel::Error => e + Logger.log "DB Error: #{e.message}\n" + false + end -def rm_session(request, response, key) - session = request.cookies["session"] - session = session.nil? ? "{}" : Zlib::Inflate.inflate(Base64.decode64(session)) - session = JSON.parse(session) - session.delete(key) - compressed = Zlib::Deflate.deflate(JSON.generate(session)) - encoded = Base64.encode64(compressed) - response.set_cookie("session", - value: encoded, - path: "/", - expires: Time.now + 360 * 24 * 60 * 60) -rescue JSON::ParserError, Zlib::Error - response.delete_cookie("session") + def login(username, pass) + player = Players.authorized?(username, pass) + if player + code = Array.new(24) { ALPHANUM.sample }.join + self["user", code] = code + signed_in_users[code] = username + begin + DB["insert into SignedInUsers (code, player) values (?, ?)", code, username].insert + rescue Sequel::Error => e + Logger.log "DB Error: #{e.message}\n" + return [500, "Internal server error when signing you in!"] + end + return [200, "Remember to verify your email!"] unless Players.verified?(username) + [200, "Signed in successfully!"] + else + [200, "Couldn't sign you in (Username or password incorrect)!"] + end + end + + def []=(key, uid, val) + session = @request.cookies["session"] + session = session.nil? ? "{}" : Zlib::Inflate.inflate(Base64.decode64(session)) + session = JSON.parse(session) + session[key] = val + compressed = Zlib::Deflate.deflate(JSON.generate(session)) + encoded = Base64.encode64(compressed) + @response.set_cookie("session", + value: encoded, + path: "/", + expires: Time.now + 360 * 24 * 60 * 60) + DB["UPDATE SignedInUsers SET last_used_at = CURRENT_TIMESTAMP WHERE code = ?", uid].update if uid + rescue JSON::ParserError, Zlib::Error + @response.delete_cookie("session") + rescue Sequel::Error => e + Logger.log "DB Error: #{e.message} when updating last_used_at for #{uid}\n" + end + + def [](key, uid: nil) + session = @request.cookies["session"] + session = session.nil? ? "{}" : Zlib::Inflate.inflate(Base64.decode64(session)) + session = JSON.parse(session) + begin + DB["UPDATE SignedInUsers SET last_used_at = CURRENT_TIMESTAMP WHERE code = ?", uid].update if uid + rescue Sequel::Error => e + Logger.log "DB Error: #{e.message} when updating last_used_at for #{uid}\n" + end + session[key] + rescue JSON::ParserError, Zlib::Error + @response.delete_cookie("session") + "" + end + + def all + session = @request.cookies["session"] + session = session.nil? ? "{}" : Zlib::Inflate.inflate(Base64.decode64(session)) + JSON.parse(session) + rescue JSON::ParserError, Zlib::Error + @response.delete_cookie("session") + {} + end + + def delete(key) + session = @request.cookies["session"] + session = session.nil? ? "{}" : Zlib::Inflate.inflate(Base64.decode64(session)) + session = JSON.parse(session) + session.delete(key) + compressed = Zlib::Deflate.deflate(JSON.generate(session)) + encoded = Base64.encode64(compressed) + @response.set_cookie("session", + value: encoded, + path: "/", + expires: Time.now + 360 * 24 * 60 * 60) + rescue JSON::ParserError, Zlib::Error + @response.delete_cookie("session") + end + + Thread.new do + loop do + now = Time.now + fifteen_days_ago = now - (60 * 60 * 24 * 15) + six_days_ago = now - (60 * 60 * 24 * 6) + old_sessions = (DB[:SignedInUsers].where { created_at < fifteen_days_ago }.all + + DB[:SignedInUsers].where { last_used_at < six_days_ago }.all).uniq { |s| s[:code] } + old_sessions.each do |session| + begin + DB[:SignedInUsers].where(code: session[:code]).delete + rescue StandardError => e + Logger.log "Thread DB error: #{e.message} on #{session[:code]} for #{session[:player]}\n" + end + signed_in_users.delete(session[:code]) + puts "Auto-logged out: #{session[:player]} (expired session)" + end + rescue StandardError => e + Logger.log "Thread error: #{e.message}\n" + ensure + sleep 60 * 60 * 24 + Logger.log "Thread sleeping\n" + end + end end