From ce9bca0a75748c2311abefca0e1f01c21469c017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lera=20Elvo=C3=A9?= Date: Sun, 12 Apr 2026 23:36:50 +0300 Subject: [PATCH] start the new topics route and view --- app/__init__.py | 42 ++++++++++++++++++-------------- app/auth.py | 16 ++++++++++++ app/models.py | 22 ++++++----------- app/routes/app.py | 6 +++++ app/routes/topics.py | 17 +++++++++++++ app/templates/base.html | 21 ++++++++++++++++ app/templates/common/footer.html | 9 +++++++ app/templates/common/macros.html | 3 +++ app/templates/common/topnav.html | 15 ++++++++++++ app/templates/topics/topics.html | 23 +++++++++++++++++ data/static/css/style.css | 1 - 11 files changed, 141 insertions(+), 34 deletions(-) create mode 100644 app/routes/app.py create mode 100644 app/routes/topics.py create mode 100644 app/templates/base.html create mode 100644 app/templates/common/footer.html create mode 100644 app/templates/common/macros.html create mode 100644 app/templates/common/topnav.html create mode 100644 app/templates/topics/topics.html diff --git a/app/__init__.py b/app/__init__.py index 367735d..7483263 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,7 +1,7 @@ from flask import Flask, session, request, render_template from dotenv import load_dotenv from .models import Avatars, Users, PostHistory, Posts, MOTD, BadgeUploads, Sessions -from .auth import digest +from .auth import digest, is_logged_in from .constants import ( PermissionLevel, permission_level_string, InfoboxKind, InfoboxHTMLClass, @@ -221,6 +221,11 @@ def create_app(): with open('.git/refs/heads/main') as f: commit = f.read().strip() + from app.routes.app import bp as app_bp + from app.routes.topics import bp as topics_bp + app.register_blueprint(app_bp) + app.register_blueprint(topics_bp) + @app.context_processor def inject_constants(): return { @@ -239,6 +244,7 @@ def create_app(): return { 'get_motds': MOTD.get_all, 'get_time_now': lambda: int(time.time()), + 'is_logged_in': is_logged_in, } @app.template_filter('ts_datetime') @@ -268,23 +274,23 @@ def create_app(): def basename_noext(subj): return os.path.splitext(os.path.basename(subj))[0] - @app.errorhandler(404) - def _handle_404(e): - if request.path.startswith('/hyperapi/'): - return '

not found

', e.code - elif request.path.startswith('/api/'): - return {'error': 'not found'}, e.code - else: - return render_template('common/404.html'), e.code - - @app.errorhandler(413) - def _handle_413(e): - if request.path.startswith('/hyperapi/'): - return '

request body too large

', e.code - elif request.path.startswith('/api/'): - return {'error': 'body too large'}, e.code - else: - return render_template('common/413.html'), e.code + # @app.errorhandler(404) + # def _handle_404(e): + # if request.path.startswith('/hyperapi/'): + # return '

not found

', e.code + # elif request.path.startswith('/api/'): + # return {'error': 'not found'}, e.code + # else: + # return render_template('common/404.html'), e.code + # + # @app.errorhandler(413) + # def _handle_413(e): + # if request.path.startswith('/hyperapi/'): + # return '

request body too large

', e.code + # elif request.path.startswith('/api/'): + # return {'error': 'body too large'}, e.code + # else: + # return render_template('common/413.html'), e.code # this only happens at build time but # build time is when updates are done anyway diff --git a/app/auth.py b/app/auth.py index a4e7275..a3fb19a 100644 --- a/app/auth.py +++ b/app/auth.py @@ -1,4 +1,7 @@ +from flask import session, flash +from .models import Sessions from argon2 import PasswordHasher +import time ph = PasswordHasher() @@ -10,3 +13,16 @@ def verify(expected, given): return ph.verify(expected, given) except: return False + +def is_logged_in(): + if "pyrom_session_key" not in session: + return False + sess = Sessions.find({"key": session["pyrom_session_key"]}) + if not sess: + return False + if sess.expires_at < int(time.time()): + session.clear() + sess.delete() + # flash('Your session expired.;Please log in again.', InfoboxKind.INFO) + return False + return True diff --git a/app/models.py b/app/models.py index d2e0265..c6e0532 100644 --- a/app/models.py +++ b/app/models.py @@ -111,24 +111,16 @@ class Topics(Model): q = """ SELECT topics.id, topics.name, topics.slug, topics.description, topics.is_locked, - users.username AS latest_thread_username, - users.display_name AS latest_thread_display_name, - threads.title AS latest_thread_title, - threads.slug AS latest_thread_slug, - threads.created_at AS latest_thread_created_at + COUNT(DISTINCT threads.id) as threads_count, + COUNT(posts.id) AS posts_count, + MAX(posts.created_at) as latest_post_timestamp FROM topics - LEFT JOIN ( - SELECT - *, - row_number() OVER (PARTITION BY threads.topic_id ORDER BY threads.created_at DESC) as rn - FROM - threads - ) threads ON threads.topic_id = topics.id AND threads.rn = 1 LEFT JOIN - users on users.id = threads.user_id - ORDER BY - topics.sort_order ASC""" + threads ON threads.topic_id = topics.id + LEFT JOIN + posts ON posts.thread_id = threads.id + GROUP BY topics.id ORDER BY topics.sort_order ASC""" return db.query(q) def get_threads(self, per_page, page, sort_by = 'activity'): diff --git a/app/routes/app.py b/app/routes/app.py new file mode 100644 index 0000000..b2aabdf --- /dev/null +++ b/app/routes/app.py @@ -0,0 +1,6 @@ +from flask import Blueprint, redirect, url_for, render_template +bp = Blueprint('app', __name__, url_prefix = '/') + +@bp.get('/') +def index(): + return redirect(url_for('topics.all_topics')) diff --git a/app/routes/topics.py b/app/routes/topics.py new file mode 100644 index 0000000..c214baf --- /dev/null +++ b/app/routes/topics.py @@ -0,0 +1,17 @@ +from flask import Blueprint, redirect, url_for, render_template + +from ..models import Topics + +bp = Blueprint('topics', __name__, url_prefix = '/topics/') + +@bp.get('/') +def all_topics(): + topic_list = Topics.get_list() + return render_template('topics/topics.html', topics=topic_list) + +@bp.get('/') +def topic(slug): + t = Topics.find({'slug': slug}) + if t: + return 'yes' + return 'no' diff --git a/app/templates/base.html b/app/templates/base.html new file mode 100644 index 0000000..ccd5e5c --- /dev/null +++ b/app/templates/base.html @@ -0,0 +1,21 @@ + + + + + + + + {% if self.title() -%} + {{ config.SITE_NAME }} - {% block title -%}{%- endblock -%} + {%- else -%} + {{ config.SITE_NAME }} + {%- endif -%} + + +
+ {%- include 'common/topnav.html' -%} + {%- block content -%}{%- endblock -%} + {%- include 'common/footer.html' -%} +
+ + diff --git a/app/templates/common/footer.html b/app/templates/common/footer.html new file mode 100644 index 0000000..71d6cf4 --- /dev/null +++ b/app/templates/common/footer.html @@ -0,0 +1,9 @@ + diff --git a/app/templates/common/macros.html b/app/templates/common/macros.html new file mode 100644 index 0000000..8e10f9b --- /dev/null +++ b/app/templates/common/macros.html @@ -0,0 +1,3 @@ +{% macro timestamp(unix_ts) -%} +{{ unix_ts | ts_datetime('%Y-%m-%d %H:%M')}} ST +{%- endmacro %} diff --git a/app/templates/common/topnav.html b/app/templates/common/topnav.html new file mode 100644 index 0000000..d33b050 --- /dev/null +++ b/app/templates/common/topnav.html @@ -0,0 +1,15 @@ + diff --git a/app/templates/topics/topics.html b/app/templates/topics/topics.html new file mode 100644 index 0000000..8096a33 --- /dev/null +++ b/app/templates/topics/topics.html @@ -0,0 +1,23 @@ +{% from 'common/macros.html' import timestamp %} +{%- extends 'base.html' -%} +{%- block content -%} +{%- for topic in topics -%} +
+ +
{{topic.description}}
+
    +
  • {{topic.threads_count}} {{"thread" | pluralize(topic.threads_count)}}
  • +
  • {{topic.posts_count}} {{"post" | pluralize(topic.posts_count)}}
  • +
+
+ {%- if topic.latest_post_timestamp -%} + Latest post at: {{timestamp(topic.latest_post_timestamp)}} + {%- else -%} + No posts yet + {%- endif -%} +
+
+{%- endfor -%} +{%- endblock -%} diff --git a/data/static/css/style.css b/data/static/css/style.css index 9b28d21..02f1779 100644 --- a/data/static/css/style.css +++ b/data/static/css/style.css @@ -76,7 +76,6 @@ body { --medium-padding: calc(var(--base-padding) * 2); --big-padding: calc(var(--base-padding) * 3); --huge-padding: calc(var(--base-padding) * 4); - --input-height: calc(var(--base-padding) * 6); --code-bg-color: hsl(from var(--bg-color-primary) h calc(s * 0.2) calc(l * 0.2));