diff --git a/files/__main__.py b/files/__main__.py
index 2ce76c7d1..0daa072f9 100644
--- a/files/__main__.py
+++ b/files/__main__.py
@@ -88,25 +88,29 @@ mail = Mail(app)
@app.before_request
def before_request():
-
with open('site_settings.json', 'r') as f:
app.config['SETTINGS'] = json.load(f)
- if request.host != app.config["SERVER_NAME"]: return {"error":"Unauthorized host provided."}, 401
- if request.headers.get("CF-Worker"): return {"error":"Cloudflare workers are not allowed to access this website."}, 401
+ if request.host != app.config["SERVER_NAME"]:
+ return {"error": "Unauthorized host provided."}, 401
- if not app.config['SETTINGS']['Bots'] and request.headers.get("Authorization"): abort(503)
+ if not app.config['SETTINGS']['Bots'] and request.headers.get("Authorization"):
+ abort(503)
g.db = db_session()
- ua = request.headers.get("User-Agent","").lower()
-
- if '; wv) ' in ua: g.webview = True
- else: g.webview = False
-
- if 'iphone' in ua or 'ipad' in ua or 'ipod' in ua or 'mac os' in ua or ' firefox/' in ua: g.inferior_browser = True
- else: g.inferior_browser = False
+ g.agent = request.headers.get("User-Agent")
+ if not g.agent:
+ return 'Please use a "User-Agent" header!', 403
+ ua = g.agent.lower()
+ g.webview = ('; wv) ' in ua)
+ g.inferior_browser = (
+ 'iphone' in ua or
+ 'ipad' in ua or
+ 'ipod' in ua or
+ 'mac os' in ua or
+ ' firefox/' in ua)
g.timestamp = int(time.time())
diff --git a/files/helpers/const.py b/files/helpers/const.py
index b6b217597..7878132f2 100644
--- a/files/helpers/const.py
+++ b/files/helpers/const.py
@@ -72,6 +72,8 @@ PUSHER_KEY = environ.get("PUSHER_KEY", "").strip()
DEFAULT_COLOR = environ.get("DEFAULT_COLOR", "fff").strip()
COLORS = {'ff66ac','805ad5','62ca56','38a169','80ffff','2a96f3','eb4963','ff0000','f39731','30409f','3e98a7','e4432d','7b9ae4','ec72de','7f8fa6', 'f8db58','8cdbe6', DEFAULT_COLOR}
+LOGGEDIN_ACTIVE_TIME = 15 * 60
+
AWARDS = {
"ghost": {
"kind": "ghost",
diff --git a/files/helpers/wrappers.py b/files/helpers/wrappers.py
index 8800c37a8..7875061cc 100644
--- a/files/helpers/wrappers.py
+++ b/files/helpers/wrappers.py
@@ -3,13 +3,19 @@ from .alerts import *
from files.helpers.const import *
from files.__main__ import db_session
from random import randint
+import user_agents
+import time
def get_logged_in_user():
- if not (hasattr(g, 'db') and g.db): g.db = db_session()
+ if hasattr(g, 'v'):
+ return g.v
+
+ if not (hasattr(g, 'db') and g.db):
+ g.db = db_session()
v = None
- token = request.headers.get("Authorization","").strip()
+ token = request.headers.get("Authorization", "").strip()
if token:
client = g.db.query(ClientAuth).filter(ClientAuth.access_token == token).one_or_none()
if client:
@@ -19,7 +25,7 @@ def get_logged_in_user():
lo_user = session.get("lo_user")
if lo_user:
id = int(lo_user)
- v = g.db.query(User).filter_by(id=id).one_or_none()
+ v = g.db.query(User).get(id)
if v:
nonce = session.get("login_nonce", 0)
if nonce < v.login_nonce or v.id != id: abort(401)
@@ -35,14 +41,40 @@ def get_logged_in_user():
v.client = None
-
- if request.method.lower() != "get" and app.config['SETTINGS']['Read-only mode'] and not (v and v.admin_level):
+ if request.method.lower() != "get" \
+ and app.config['SETTINGS']['Read-only mode'] \
+ and not (v and v.admin_level):
abort(403)
- if v and v.patron:
- if request.content_length and request.content_length > 16 * 1024 * 1024: abort(413)
- elif request.content_length and request.content_length > 8 * 1024 * 1024: abort(413)
+ if request.content_length and request.content_length > 8 * 1024 * 1024:
+ abort(413)
+ if not session.get("session_id"):
+ session.permanent = True
+ session["session_id"] = secrets.token_hex(49)
+
+ # Active User Counters
+ loggedin = cache.get(f'{SITE}_loggedin') or {}
+ loggedout = cache.get(f'{SITE}_loggedout') or {}
+
+ timestamp = int(time.time())
+ if v:
+ if session["session_id"] in loggedout:
+ del loggedout[session["session_id"]]
+ loggedin[v.id] = timestamp
+ else:
+ ua = str(user_agents.parse(g.agent))
+ if 'spider' not in ua.lower() and 'bot' not in ua.lower():
+ loggedout[session["session_id"]] = (timestamp, ua)
+
+ g.loggedin_counter = len([x for x in loggedin.values() \
+ if (timestamp - x) < LOGGEDIN_ACTIVE_TIME])
+ g.loggedout_counter = len([x for x in loggedout.values() \
+ if (timestamp - x[0]) < LOGGEDIN_ACTIVE_TIME])
+ cache.set(f'{SITE}_loggedin', loggedin)
+ cache.set(f'{SITE}_loggedout', loggedout)
+
+ g.v = v
return v
def check_ban_evade(v):
@@ -53,12 +85,10 @@ def check_ban_evade(v):
def auth_desired(f):
def wrapper(*args, **kwargs):
-
v = get_logged_in_user()
check_ban_evade(v)
- g.v = v
return make_response(f(*args, v=v, **kwargs))
wrapper.__name__ = f.__name__
@@ -68,13 +98,11 @@ def auth_desired(f):
def auth_required(f):
def wrapper(*args, **kwargs):
-
v = get_logged_in_user()
if not v: abort(401)
check_ban_evade(v)
- g.v = v
return make_response(f(*args, v=v, **kwargs))
wrapper.__name__ = f.__name__
@@ -84,9 +112,7 @@ def auth_required(f):
def is_not_permabanned(f):
def wrapper(*args, **kwargs):
-
v = get_logged_in_user()
-
if not v: abort(401)
check_ban_evade(v)
@@ -94,7 +120,6 @@ def is_not_permabanned(f):
if v.is_suspended_permanently:
return {"error": "Forbidden: you are permabanned."}, 403
- g.v = v
return make_response(f(*args, v=v, **kwargs))
wrapper.__name__ = f.__name__
@@ -106,14 +131,11 @@ def admin_level_required(x):
def wrapper_maker(f):
def wrapper(*args, **kwargs):
-
v = get_logged_in_user()
-
if not v: abort(401)
if v.admin_level < x: abort(403)
-
- g.v = v
+
return make_response(f(*args, v=v, **kwargs))
wrapper.__name__ = f.__name__
diff --git a/files/routes/admin.py b/files/routes/admin.py
index 25b521bb2..b0ed86d51 100644
--- a/files/routes/admin.py
+++ b/files/routes/admin.py
@@ -764,6 +764,25 @@ def users_list(v):
page=page,
)
+
+@app.get('/admin/loggedin')
+@admin_level_required(2)
+def loggedin_list(v):
+ ids = [x for x, val in cache.get(f'{SITE}_loggedin').items() \
+ if (time.time() - val) < LOGGEDIN_ACTIVE_TIME]
+ users = g.db.query(User).filter(User.id.in_(ids)) \
+ .order_by(User.admin_level.desc(), User.truecoins.desc()).all()
+ return render_template("admin/loggedin.html", v=v, users=users)
+
+
+@app.get('/admin/loggedout')
+@admin_level_required(2)
+def loggedout_list(v):
+ users = sorted([val[1] for x, val in cache.get(f'{SITE}_loggedout').items() \
+ if (time.time() - val[0]) < LOGGEDIN_ACTIVE_TIME])
+ return render_template("admin/loggedout.html", v=v, users=users)
+
+
@app.get("/admin/alt_votes")
@admin_level_required(2)
def alt_votes_get(v):
diff --git a/files/templates/admin/admin_home.html b/files/templates/admin/admin_home.html
index 34f83c4f5..17901e7be 100644
--- a/files/templates/admin/admin_home.html
+++ b/files/templates/admin/admin_home.html
@@ -8,7 +8,13 @@
{% block content %}
- Admin Tools
+Admin Tools
+
+
Content
diff --git a/files/templates/admin/loggedin.html b/files/templates/admin/loggedin.html
new file mode 100644
index 000000000..cfa98918c
--- /dev/null
+++ b/files/templates/admin/loggedin.html
@@ -0,0 +1,21 @@
+{% extends "settings2.html" %}
+
+{% block content %}
+
+
+
+ # |
+ Name |
+ Truescore |
+
+
+{% for user in users %}
+
+ {{loop.index}} |
+ {{user.username}} |
+ {{user.truecoins}} |
+
+{% endfor %}
+
+
+{% endblock %}
diff --git a/files/templates/admin/loggedout.html b/files/templates/admin/loggedout.html
new file mode 100644
index 000000000..a210ef024
--- /dev/null
+++ b/files/templates/admin/loggedout.html
@@ -0,0 +1,19 @@
+{% extends "settings2.html" %}
+
+{% block content %}
+
+
+
+ # |
+ Details |
+
+
+{% for user in users %}
+
+ {{loop.index}} |
+ {{user}} |
+
+{% endfor %}
+
+
+{% endblock %}
diff --git a/requirements.txt b/requirements.txt
index 271b48610..76f9e27e9 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -21,6 +21,7 @@ qrcode
redis
requests
SQLAlchemy
+user-agents
psycopg2-binary
pusher_push_notifications
pyenchant