Compare commits
4
Commits
7f7ac559ac
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b28cf38e99 | ||
|
|
8823fa808a
|
||
|
|
1e27d7629f
|
||
|
|
cefa98cdec
|
@@ -1,11 +0,0 @@
|
|||||||
source "https://rubygems.org"
|
|
||||||
|
|
||||||
gem "base64"
|
|
||||||
gem "erb"
|
|
||||||
gem "json"
|
|
||||||
gem "net-http"
|
|
||||||
gem "sequel"
|
|
||||||
gem "sinatra"
|
|
||||||
gem "uri"
|
|
||||||
gem "xxhash"
|
|
||||||
gem "zlib"
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
GEM
|
|
||||||
remote: https://rubygems.org/
|
|
||||||
specs:
|
|
||||||
base64 (0.3.0)
|
|
||||||
bigdecimal (3.2.2)
|
|
||||||
erb (5.0.1)
|
|
||||||
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)
|
|
||||||
logger (>= 1.6.0)
|
|
||||||
rack (>= 3.0.0, < 4)
|
|
||||||
rack-session (2.1.1)
|
|
||||||
base64 (>= 0.1.0)
|
|
||||||
rack (>= 3.0.0)
|
|
||||||
ruby2_keywords (0.0.5)
|
|
||||||
sequel (5.93.0)
|
|
||||||
bigdecimal
|
|
||||||
sinatra (4.1.1)
|
|
||||||
logger (>= 1.6.0)
|
|
||||||
mustermann (~> 3.0)
|
|
||||||
rack (>= 3.0.0, < 4)
|
|
||||||
rack-protection (= 4.1.1)
|
|
||||||
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
|
|
||||||
erb
|
|
||||||
json
|
|
||||||
net-http
|
|
||||||
sequel
|
|
||||||
sinatra
|
|
||||||
uri
|
|
||||||
xxhash
|
|
||||||
zlib
|
|
||||||
|
|
||||||
BUNDLED WITH
|
|
||||||
2.6.9
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
# Infinsweeper Game.
|
# Infinsweeper Game.
|
||||||
|
|
||||||
It's like minesweeper, but with an infinite board.
|
It's like minesweeper, but with an infinite board.
|
||||||
|
|
||||||
The static web-app is here: [https://portfolio.syedm.dev/infinsweeper/](https://portfolio.syedm.dev/infinsweeper/)
|
The web-app is here: [infinsweeper.syedm.dev](https://infinsweeper.syedm.dev/)
|
||||||
(This version is without any signing up or logging in.)
|
|
||||||
|
|
||||||
## Working
|
## Working
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
set pipe_path /tmp/infin
|
|
||||||
set oldsum (cat (find . \( -name '*.rb' -o -name '*.js' \) | sort) | md5sum)
|
|
||||||
if not test -e infinsweeper.db
|
|
||||||
sqlite3 infinsweeper.db < schema.sql
|
|
||||||
end
|
|
||||||
pkill ruby
|
|
||||||
ruby main.rb -p8080 &
|
|
||||||
echo "" > $pipe_path
|
|
||||||
while true
|
|
||||||
sleep 1
|
|
||||||
set newsum (cat (find . \( -name '*.rb' -o -name '*.js' \) | sort) | md5sum)
|
|
||||||
echo $newsum $oldsum
|
|
||||||
if test "$oldsum" != "$newsum"
|
|
||||||
set oldsum $newsum
|
|
||||||
pkill ruby
|
|
||||||
ruby main.rb -p8080 &
|
|
||||||
end
|
|
||||||
if set line (cat $pipe_path)
|
|
||||||
echo $line
|
|
||||||
if test "$line" = "die"
|
|
||||||
pkill ruby
|
|
||||||
echo "" > $pipe_path
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
if test "$line" = "open"
|
|
||||||
echo "" > $pipe_path
|
|
||||||
xdg-open http://localhost:8080
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Generated
+27
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1790185690,
|
||||||
|
"narHash": "sha256-xJ+X4hBtOcAFGBOe5nAMyMUeF9foJBmIOu3NjBqBycU=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "4975466d324710c576dc11ad614684e6bd8cad8e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
{
|
||||||
|
description = "Infinsweeper";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{ nixpkgs, ... }:
|
||||||
|
let
|
||||||
|
system = "x86_64-linux";
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
|
||||||
|
ruby = pkgs.ruby.withPackages (
|
||||||
|
p: with p; [
|
||||||
|
sqlite3
|
||||||
|
sequel
|
||||||
|
rackup
|
||||||
|
puma
|
||||||
|
sinatra
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
app = pkgs.stdenv.mkDerivation {
|
||||||
|
pname = "infinsweeper";
|
||||||
|
version = "0.1.0";
|
||||||
|
src = ./.;
|
||||||
|
buildInputs = [ ruby ];
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out
|
||||||
|
cp -r . $out/
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
server = pkgs.writeShellApplication {
|
||||||
|
name = "infinsweeper";
|
||||||
|
runtimeInputs = [ ruby ];
|
||||||
|
text = ''
|
||||||
|
set -euo pipefail
|
||||||
|
export DB="''${INFINSWEEPER_DB:-/var/lib/infinsweeper.db}"
|
||||||
|
exec ruby ${app}/main.rb
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages.${system}.default = app;
|
||||||
|
|
||||||
|
apps.${system}.default = {
|
||||||
|
type = "app";
|
||||||
|
program = "${server}/bin/infinsweeper";
|
||||||
|
};
|
||||||
|
|
||||||
|
devShells.${system}.default = pkgs.mkShell {
|
||||||
|
packages = [
|
||||||
|
ruby
|
||||||
|
pkgs.sqlite
|
||||||
|
];
|
||||||
|
|
||||||
|
env.INFINSWEEPER_DB = "./infinsweeper.db";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
require "xxhash"
|
|
||||||
|
|
||||||
# Game logic main class
|
# Game logic main class
|
||||||
class GameLogic
|
class GameLogic
|
||||||
attr_reader :pos
|
attr_reader :pos
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
|
<button class="account-button" id="reset-button">RESET GAME</button>
|
||||||
<button class="account-button" id="logout-button">LOGOUT</button>
|
<button class="account-button" id="logout-button">LOGOUT</button>
|
||||||
<button class="account-button" id="login-button">LOGIN</button>
|
<button class="account-button" id="login-button">LOGIN</button>
|
||||||
<button class="account-button" id="signup-button">SIGN UP</button>
|
<button class="account-button" id="signup-button">SIGN UP</button>
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
module Logman
|
module Logman
|
||||||
def self.log(log)
|
def self.log(log)
|
||||||
file, line = caller(1, 1)[0].split(":")
|
file, line = caller(1, 1)[0].split(":")
|
||||||
File.write("log/main.log", "[#{Time.now}] {#{file}:#{line}} #{log}\n", mode: "a") if ENV_HASH["ENV"] == "dev"
|
File.write("log/main.log", "[#{Time.now}] {#{file}:#{line}} #{log}\n", mode: "a") if ENV["ENV"] == "dev"
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.imp(log)
|
def self.imp(log)
|
||||||
File.write("log/imp.log", "[#{Time.now}] #{log}\n", mode: "a") if ENV_HASH["ENV"] == "dev"
|
File.write("log/imp.log", "[#{Time.now}] #{log}\n", mode: "a") if ENV["ENV"] == "dev"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
# Mailer module
|
# Mailer module
|
||||||
|
|
||||||
module Mail
|
module Mail
|
||||||
def send(to, subject, body)
|
def send(to, subject, body)
|
||||||
from_email = "infinsweeper@syedm.dev"
|
from_email = "infinsweeper@domain.com"
|
||||||
from_name = "InfinSweeper"
|
from_name = "InfinSweeper"
|
||||||
to = Array(to).map { |addr| { email_address: { address: addr, name: "" } } }
|
to = Array(to).map { |addr| { email_address: { address: addr, name: "" } } }
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ module Mail
|
|||||||
|
|
||||||
uri = URI("https://api.zeptomail.com/v1.1/email")
|
uri = URI("https://api.zeptomail.com/v1.1/email")
|
||||||
req = Net::HTTP::Post.new(uri)
|
req = Net::HTTP::Post.new(uri)
|
||||||
req["Authorization"] = "Zoho-enczapikey #{ENV_HASH["ZOHO_PASS"]}"
|
req["Authorization"] = "Zoho-enczapikey #{ENV["ZOHO_PASS"]}"
|
||||||
req["Content-Type"] = "application/json"
|
req["Content-Type"] = "application/json"
|
||||||
req.body = payload.to_json
|
req.body = payload.to_json
|
||||||
|
|
||||||
@@ -27,5 +28,6 @@ module Mail
|
|||||||
|
|
||||||
return if response.is_a?(Net::HTTPSuccess)
|
return if response.is_a?(Net::HTTPSuccess)
|
||||||
Logman.imp "[ZeptoMail ERROR] #{response.body}"
|
Logman.imp "[ZeptoMail ERROR] #{response.body}"
|
||||||
|
raise "Email failed."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,31 +5,42 @@ require "net/http"
|
|||||||
require "sequel"
|
require "sequel"
|
||||||
require "sinatra"
|
require "sinatra"
|
||||||
require "uri"
|
require "uri"
|
||||||
require "xxhash"
|
|
||||||
require "zlib"
|
require "zlib"
|
||||||
|
|
||||||
ALPHANUM = [*"0".."9", *"A".."Z", *"a".."z", "-", "_"].freeze
|
ALPHANUM = [*"0".."9", *"A".."Z", *"a".."z", "-", "_"].freeze
|
||||||
|
|
||||||
env_data = File.exist?(".env") ? File.read(".env") : ""
|
|
||||||
ENV_HASH = {}
|
|
||||||
|
|
||||||
env_data.each_line do |line|
|
|
||||||
next unless (match = line.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/))
|
|
||||||
ENV_HASH[match[1]] = match[2]
|
|
||||||
end
|
|
||||||
|
|
||||||
load "logman.rb"
|
load "logman.rb"
|
||||||
# Logman.log ENV_HASH.inspect
|
# Logman.log ENV.inspect
|
||||||
|
|
||||||
db_file = File.expand_path("infinsweeper.db")
|
db_file = File.expand_path(ENV.fetch("INFINSWEEPER_DB", "infinsweeper.db"))
|
||||||
|
puts db_file
|
||||||
DB = Sequel.connect("sqlite:///#{db_file}", single_threaded: false)
|
DB = Sequel.connect("sqlite:///#{db_file}", single_threaded: false)
|
||||||
DB.run("PRAGMA foreign_keys = ON;")
|
DB.run("PRAGMA foreign_keys = ON;")
|
||||||
|
DB.create_table? :Players do
|
||||||
|
String :username, primary_key: true
|
||||||
|
String :email, unique: true
|
||||||
|
File :digest
|
||||||
|
File :data
|
||||||
|
String :activation_code
|
||||||
|
String :new_pass_code
|
||||||
|
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
|
||||||
|
end
|
||||||
|
DB.create_table? :SignedInUsers do
|
||||||
|
String :code, primary_key: true
|
||||||
|
String :player
|
||||||
|
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
|
||||||
|
DateTime :last_used_at, default: Sequel::CURRENT_TIMESTAMP
|
||||||
|
foreign_key [:player], :Players, key: :username, on_delete: :cascade
|
||||||
|
end
|
||||||
$active_users = DB[:SignedInUsers].all.map { |x| [x[:code], x[:player]] }.to_h
|
$active_users = DB[:SignedInUsers].all.map { |x| [x[:code], x[:player]] }.to_h
|
||||||
|
|
||||||
load "mailer.rb"
|
load "mailer.rb"
|
||||||
load "players.rb"
|
load "players.rb"
|
||||||
load "session.rb"
|
load "session.rb"
|
||||||
|
|
||||||
|
set :port, ENV.fetch("PORT", 5161)
|
||||||
|
set :bind, '0.0.0.0'
|
||||||
|
set :host_authorization, { permitted_hosts: ["infinsweeper.syedm.dev"] }
|
||||||
set :public_folder, "public"
|
set :public_folder, "public"
|
||||||
|
|
||||||
get "/" do
|
get "/" do
|
||||||
@@ -57,6 +68,8 @@ post "/signup" do
|
|||||||
return { "message" => "Unauthorized (invalid CSRF token)!" }.to_json
|
return { "message" => "Unauthorized (invalid CSRF token)!" }.to_json
|
||||||
end
|
end
|
||||||
session["csrf_token"] = Array.new(32) { ALPHANUM.sample }.join
|
session["csrf_token"] = Array.new(32) { ALPHANUM.sample }.join
|
||||||
|
status 401
|
||||||
|
return { "message" => "New signups are disabled right now.", "csrf_token" => session["csrf_token"] }.to_json
|
||||||
if data["email"].nil? || data["pass"].nil? || data["username"].nil?
|
if data["email"].nil? || data["pass"].nil? || data["username"].nil?
|
||||||
status 400
|
status 400
|
||||||
return { "message" => "Bad request made!", "csrf_token" => session["csrf_token"] }.to_json
|
return { "message" => "Bad request made!", "csrf_token" => session["csrf_token"] }.to_json
|
||||||
@@ -104,11 +117,10 @@ post "/login" do
|
|||||||
return { "message" => "Bad request made!", "csrf_token" => session["csrf_token"] }.to_json
|
return { "message" => "Bad request made!", "csrf_token" => session["csrf_token"] }.to_json
|
||||||
end
|
end
|
||||||
login_status = session.login(data["username"], data["pass"])
|
login_status = session.login(data["username"], data["pass"])
|
||||||
|
status login_status[0]
|
||||||
if login_status[0] == 200
|
if login_status[0] == 200
|
||||||
status 200
|
|
||||||
return { "message" => login_status[1], "success" => "true", "csrf_token" => session["csrf_token"] }.to_json
|
return { "message" => login_status[1], "success" => "true", "csrf_token" => session["csrf_token"] }.to_json
|
||||||
else
|
else
|
||||||
status login_status[0]
|
|
||||||
return { "message" => login_status[1], "csrf_token" => session["csrf_token"] }.to_json
|
return { "message" => login_status[1], "csrf_token" => session["csrf_token"] }.to_json
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+51
-8
@@ -18,15 +18,15 @@ module Players
|
|||||||
raise ArgumentError, "Password must be at least 8 characters and of valid format." unless
|
raise ArgumentError, "Password must be at least 8 characters and of valid format." unless
|
||||||
pass.match?(/\A[a-zA-Z0-9_.!?@#$%^&*()+=-]+\z/) && pass.length >= 8
|
pass.match?(/\A[a-zA-Z0-9_.!?@#$%^&*()+=-]+\z/) && pass.length >= 8
|
||||||
|
|
||||||
digest = XXhash.xxh32(pass, ENV_HASH["SALT"])
|
digest = Zlib.crc32(pass, ENV["SALT"])
|
||||||
code = ENV_HASH["ENV"] == "prod" ? Array.new(24) { ALPHANUM.sample }.join : "!"
|
code = ENV["ENV"] == "prod" ? Array.new(24) { ALPHANUM.sample }.join : "!"
|
||||||
|
|
||||||
DB[
|
DB[
|
||||||
"insert into Players (username, digest, email, activation_code) values (?, ?, ?, ?)",
|
"insert into Players (username, digest, email, activation_code) values (?, ?, ?, ?)",
|
||||||
username, digest, email, code
|
username, digest, email, code
|
||||||
].insert
|
].insert
|
||||||
|
|
||||||
send_email(:new, email, username, code) if ENV_HASH["ENV"] == "prod"
|
send_email(:new, email, username, code) if ENV["ENV"] == "prod"
|
||||||
|
|
||||||
[200, "Successfully signed up!"]
|
[200, "Successfully signed up!"]
|
||||||
rescue ArgumentError => e
|
rescue ArgumentError => e
|
||||||
@@ -51,13 +51,13 @@ module Players
|
|||||||
|
|
||||||
Logman.log "Pass req: #{email} & #{code}"
|
Logman.log "Pass req: #{email} & #{code}"
|
||||||
|
|
||||||
true
|
send_email(:pass_req, email, code)
|
||||||
|
|
||||||
# send_email(:pass_req, email, code)
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.pass_reset(new_pass, code)
|
def self.pass_reset(pass, code)
|
||||||
digest = XXhash.xxh32(new_pass, ENV_HASH["SALT"])
|
digest = Zlib.crc32(pass, ENV["SALT"])
|
||||||
DB["update Players set digest = ?, new_pass_code = ? 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
|
end
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ module Players
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.authorized?(username, pass)
|
def self.authorized?(username, pass)
|
||||||
digest = XXhash.xxh32(pass, ENV_HASH["SALT"])
|
digest = Zlib.crc32(pass, ENV["SALT"])
|
||||||
Logman.log "Authorized: #{username} & #{digest}"
|
Logman.log "Authorized: #{username} & #{digest}"
|
||||||
player = self[username]
|
player = self[username]
|
||||||
player && player[:digest].to_i == digest.to_i ? player : false
|
player && player[:digest].to_i == digest.to_i ? player : false
|
||||||
@@ -90,6 +90,49 @@ module Players
|
|||||||
player && player[:activation_code] == "!"
|
player && player[:activation_code] == "!"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.send_email(type, email, *args)
|
||||||
|
case type
|
||||||
|
when :new
|
||||||
|
username, code = args
|
||||||
|
|
||||||
|
Mail.send(
|
||||||
|
email,
|
||||||
|
"Verify your InfinSweeper account",
|
||||||
|
<<~HTML
|
||||||
|
<h2>Welcome to InfinSweeper, #{ERB::Util.html_escape(username)}!</h2>
|
||||||
|
<p>Thanks for signing up.</p>
|
||||||
|
<p>
|
||||||
|
Please verify your email by clicking
|
||||||
|
<a href="https://your-domain.com/verify/#{ERB::Util.url_encode(code)}">
|
||||||
|
this link
|
||||||
|
</a>.
|
||||||
|
</p>
|
||||||
|
HTML
|
||||||
|
)
|
||||||
|
|
||||||
|
when :pass_req
|
||||||
|
code = args.first
|
||||||
|
|
||||||
|
Mail.send(
|
||||||
|
email,
|
||||||
|
"Reset your InfinSweeper password",
|
||||||
|
<<~HTML
|
||||||
|
<h2>Password reset</h2>
|
||||||
|
<p>You requested a password reset for your InfinSweeper account.</p>
|
||||||
|
<p>
|
||||||
|
<a href="https://your-domain.com/reset_password/#{ERB::Util.url_encode(code)}">
|
||||||
|
Click here to reset your password
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p>If you didn't request this, you can safely ignore this email.</p>
|
||||||
|
HTML
|
||||||
|
)
|
||||||
|
|
||||||
|
else
|
||||||
|
raise ArgumentError, "Unknown email type: #{type.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
Thread.new do
|
Thread.new do
|
||||||
loop do
|
loop do
|
||||||
unverified.each do |player|
|
unverified.each do |player|
|
||||||
|
|||||||
@@ -18,4 +18,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
*/
|
*/
|
||||||
window.game = new GameController();
|
window.game = new GameController();
|
||||||
window.game.init();
|
window.game.init();
|
||||||
|
|
||||||
|
document.getElementById("reset-button").addEventListener("click", () => {
|
||||||
|
window.game.reset();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ export default class GameController {
|
|||||||
* @param {number} tries - Number of times the game has been reset
|
* @param {number} tries - Number of times the game has been reset
|
||||||
*/
|
*/
|
||||||
async reset(tries = 0) {
|
async reset(tries = 0) {
|
||||||
|
this.bus.emit("clear_save");
|
||||||
this._clearGameState();
|
this._clearGameState();
|
||||||
this._generateNewSeed();
|
this._generateNewSeed();
|
||||||
await this.start(tries);
|
await this.start(tries);
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ export default class Saver {
|
|||||||
this.bus.on("load_save", this.load.bind(this));
|
this.bus.on("load_save", this.load.bind(this));
|
||||||
/** @listens EventBus#delete_save */
|
/** @listens EventBus#delete_save */
|
||||||
this.bus.on("delete_save", this.deleteSave.bind(this));
|
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 */
|
/** @listens EventBus#start_autosaver */
|
||||||
this.bus.on("start_autosaver", this.startAutosaver.bind(this));
|
this.bus.on("start_autosaver", this.startAutosaver.bind(this));
|
||||||
/** @listens EventBus#stop_autosaver */
|
/** @listens EventBus#stop_autosaver */
|
||||||
@@ -154,6 +156,14 @@ export default class Saver {
|
|||||||
this.delete_save = true;
|
this.delete_save = true;
|
||||||
this.stopAutosaver();
|
this.stopAutosaver();
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @function clearSave
|
||||||
|
* @description clears the save
|
||||||
|
*/
|
||||||
|
clearSave() {
|
||||||
|
localStorage.removeItem("save");
|
||||||
|
this.delete_save = false;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @function loadFromFile
|
* @function loadFromFile
|
||||||
* @description Loads the game data from a file
|
* @description Loads the game data from a file
|
||||||
|
|||||||
+45
-67
@@ -3,6 +3,36 @@ class Sessions
|
|||||||
def initialize(request, response)
|
def initialize(request, response)
|
||||||
@request = request
|
@request = request
|
||||||
@response = response
|
@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
|
end
|
||||||
|
|
||||||
def signed_in?
|
def signed_in?
|
||||||
@@ -21,7 +51,6 @@ class Sessions
|
|||||||
end
|
end
|
||||||
|
|
||||||
def login(username, pass)
|
def login(username, pass)
|
||||||
Logman.log "Logging in: #{username} & #{pass} #{ENV_HASH["SALT"]}"
|
|
||||||
player = Players.authorized?(username, pass)
|
player = Players.authorized?(username, pass)
|
||||||
if player
|
if player
|
||||||
code = Array.new(24) { ALPHANUM.sample }.join
|
code = Array.new(24) { ALPHANUM.sample }.join
|
||||||
@@ -36,48 +65,21 @@ class Sessions
|
|||||||
return [200, "Remember to verify your email!"] unless Players.verified?(username)
|
return [200, "Remember to verify your email!"] unless Players.verified?(username)
|
||||||
[200, "Signed in successfully!"]
|
[200, "Signed in successfully!"]
|
||||||
else
|
else
|
||||||
[200, "Couldn't sign you in (Username or password incorrect)!"]
|
[401, "Couldn't sign you in (Username or password incorrect)!"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: Use .all here
|
|
||||||
def []=(key, val)
|
|
||||||
session = @request.cookies["session"]
|
|
||||||
session = session.nil? ? "{}" : Zlib::Inflate.inflate(Base64.decode64(session))
|
|
||||||
session = JSON.parse(session)
|
|
||||||
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 JSON::ParserError, Zlib::Error
|
|
||||||
@response.delete_cookie("session")
|
|
||||||
rescue Sequel::Error => e
|
|
||||||
Logman.log "DB Error: #{e.message} when updating last_used_at for #{uid}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def [](key)
|
def [](key)
|
||||||
session = @request.cookies["session"]
|
@session[key]
|
||||||
session = session.nil? ? "{}" : Zlib::Inflate.inflate(Base64.decode64(session))
|
end
|
||||||
session = JSON.parse(session)
|
|
||||||
begin
|
def []=(key, val)
|
||||||
uid = session["user"]
|
@session[key] = val
|
||||||
DB["UPDATE SignedInUsers SET last_used_at = CURRENT_TIMESTAMP WHERE code = ?", uid].update if uid
|
save
|
||||||
rescue Sequel::Error => e
|
end
|
||||||
Logman.log "DB Error: #{e.message} when updating last_used_at for #{uid}"
|
|
||||||
end
|
def all
|
||||||
session[key]
|
@session
|
||||||
rescue JSON::ParserError, Zlib::Error
|
|
||||||
@response.delete_cookie("session")
|
|
||||||
""
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def message=(val)
|
def message=(val)
|
||||||
@@ -85,7 +87,7 @@ class Sessions
|
|||||||
value: val,
|
value: val,
|
||||||
path: "/",
|
path: "/",
|
||||||
expires: Time.now + 360 * 24 * 60 * 60,
|
expires: Time.now + 360 * 24 * 60 * 60,
|
||||||
secure: ENV_HASH["ENV"] == "prod",
|
secure: ENV["ENV"] == "prod",
|
||||||
samesite: :strict)
|
samesite: :strict)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -97,33 +99,9 @@ class Sessions
|
|||||||
@request.env["HTTP_X_CSRF_TOKEN"] == self["csrf_token"]
|
@request.env["HTTP_X_CSRF_TOKEN"] == self["csrf_token"]
|
||||||
end
|
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)
|
def delete(key)
|
||||||
session = @request.cookies["session"]
|
@session.delete(key)
|
||||||
session = session.nil? ? "{}" : Zlib::Inflate.inflate(Base64.decode64(session))
|
save
|
||||||
session = JSON.parse(session)
|
|
||||||
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_HASH["ENV"] == "prod",
|
|
||||||
samesite: :strict)
|
|
||||||
rescue JSON::ParserError, Zlib::Error
|
|
||||||
@response.delete_cookie("session")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Thread.new do
|
Thread.new do
|
||||||
@@ -140,7 +118,7 @@ class Sessions
|
|||||||
Logman.log "Thread DB error: #{e.message} on #{session[:code]} for #{session[:player]}"
|
Logman.log "Thread DB error: #{e.message} on #{session[:code]} for #{session[:player]}"
|
||||||
end
|
end
|
||||||
$active_users.delete(session[:code])
|
$active_users.delete(session[:code])
|
||||||
puts "Auto-logged out: #{session[:player]} (expired session)"
|
Logman.log "Auto-logged out: #{session[:player]} (expired session)"
|
||||||
end
|
end
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
Logman.log "Thread error: #{e.message}"
|
Logman.log "Thread error: #{e.message}"
|
||||||
|
|||||||
Reference in New Issue
Block a user