diff --git a/flake.nix b/flake.nix index bde477a..bc5428d 100644 --- a/flake.nix +++ b/flake.nix @@ -37,9 +37,7 @@ runtimeInputs = [ ruby ]; text = '' set -euo pipefail - data_dir="''${INFINSWEEPER_DATA_DIR:-/var/lib/infinsweeper}" - DB="$data_dir/infinsweeper.db" - mkdir -p "$data_dir" + export DB="''${INFINSWEEPER_DB:-/var/lib/infinsweeper.db}" exec ruby ${app}/main.rb ''; }; @@ -57,6 +55,8 @@ ruby pkgs.sqlite ]; + + env.INFINSWEEPER_DB = "./infinsweeper.db"; }; }; } diff --git a/index.erb b/index.erb index 33febfe..b46d96e 100644 --- a/index.erb +++ b/index.erb @@ -29,6 +29,7 @@ />
Thanks for signing up.
++ Please verify your email by clicking + + this link + . +
+ HTML + ) + + when :pass_req + code = args.first + + Mail.send( + email, + "Reset your InfinSweeper password", + <<~HTML +You requested a password reset for your InfinSweeper account.
++ + Click here to reset your password + +
+If you didn't request this, you can safely ignore this email.
+ HTML + ) + + else + raise ArgumentError, "Unknown email type: #{type.inspect}" + end + end + Thread.new do loop do unverified.each do |player| diff --git a/public/src/js/game.js b/public/src/js/game.js index 2e2034f..9902da0 100644 --- a/public/src/js/game.js +++ b/public/src/js/game.js @@ -18,4 +18,8 @@ document.addEventListener("DOMContentLoaded", () => { */ window.game = new GameController(); window.game.init(); + + document.getElementById("reset-button").addEventListener("click", () => { + window.game.reset(); + }); }); diff --git a/public/src/js/game_controller.js b/public/src/js/game_controller.js index 5d15507..73f56a6 100644 --- a/public/src/js/game_controller.js +++ b/public/src/js/game_controller.js @@ -147,6 +147,7 @@ export default class GameController { * @param {number} tries - Number of times the game has been reset */ async reset(tries = 0) { + this.bus.emit("clear_save"); this._clearGameState(); this._generateNewSeed(); await this.start(tries); diff --git a/public/src/js/saver.js b/public/src/js/saver.js index 615d4cf..3ea12dc 100644 --- a/public/src/js/saver.js +++ b/public/src/js/saver.js @@ -39,6 +39,8 @@ export default class Saver { this.bus.on("load_save", this.load.bind(this)); /** @listens EventBus#delete_save */ this.bus.on("delete_save", this.deleteSave.bind(this)); + /** @listens EventBus#clear_save */ + this.bus.on("clear_save", this.clearSave.bind(this)); /** @listens EventBus#start_autosaver */ this.bus.on("start_autosaver", this.startAutosaver.bind(this)); /** @listens EventBus#stop_autosaver */ @@ -154,6 +156,14 @@ export default class Saver { this.delete_save = true; this.stopAutosaver(); } + /** + * @function clearSave + * @description clears the save + */ + clearSave() { + localStorage.removeItem("save"); + this.delete_save = false; + } /** * @function loadFromFile * @description Loads the game data from a file diff --git a/session.rb b/session.rb index 81c8a11..f2ac0b5 100644 --- a/session.rb +++ b/session.rb @@ -3,6 +3,36 @@ class Sessions def initialize(request, response) @request = request @response = response + @session = load_session + end + + def save + compressed = Zlib::Deflate.deflate(JSON.generate(@session)) + encoded = Base64.strict_encode64(compressed) + @response.set_cookie( + "session", + value: encoded, + path: "/", + expires: Time.now + 360 * 24 * 60 * 60, + httponly: true, + secure: ENV["ENV"] == "prod", + samesite: :strict + ) + uid = @session["user"] + if uid + DB["UPDATE SignedInUsers SET last_used_at = CURRENT_TIMESTAMP WHERE code = ?", uid].update + end + rescue Sequel::Error => e + Logman.log "DB Error: #{e.message} when updating last_used_at for #{uid}" + end + + def load_session + cookie = @request.cookies["session"] + return {} unless cookie + JSON.parse(Zlib::Inflate.inflate(Base64.strict_decode64(cookie))) + rescue JSON::ParserError, Zlib::Error, ArgumentError + @response.delete_cookie("session") + {} end def signed_in? @@ -35,38 +65,21 @@ class Sessions 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)!"] + [401, "Couldn't sign you in (Username or password incorrect)!"] end end - def []=(key, val) - session = all - session[key] = val - Logman.log "Updated: #{key} to #{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, - httponly: true, - secure: ENV_HASH["ENV"] == "prod", - samesite: :strict) - uid = session["user"] - DB["UPDATE SignedInUsers SET last_used_at = CURRENT_TIMESTAMP WHERE code = ?", uid].update if uid - rescue Sequel::Error => e - Logman.log "DB Error: #{e.message} when updating last_used_at for #{uid}" - end - def [](key) - session = all - begin - uid = session["user"] - DB["UPDATE SignedInUsers SET last_used_at = CURRENT_TIMESTAMP WHERE code = ?", uid].update if uid - rescue Sequel::Error => e - Logman.log "DB Error: #{e.message} when updating last_used_at for #{uid}" - end - session[key] + @session[key] + end + + def []=(key, val) + @session[key] = val + save + end + + def all + @session end def message=(val) @@ -86,31 +99,9 @@ class Sessions @request.env["HTTP_X_CSRF_TOKEN"] == self["csrf_token"] 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 = all - session.delete(key) - Logman.log "Deleted: #{key}" - Logman.log session.inspect - 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, - httponly: true, - secure: ENV["ENV"] == "prod", - samesite: :strict) - rescue JSON::ParserError, Zlib::Error - @response.delete_cookie("session") + @session.delete(key) + save end Thread.new do