start the new topics route and view
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
from flask import Flask, session, request, render_template
|
from flask import Flask, session, request, render_template
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from .models import Avatars, Users, PostHistory, Posts, MOTD, BadgeUploads, Sessions
|
from .models import Avatars, Users, PostHistory, Posts, MOTD, BadgeUploads, Sessions
|
||||||
from .auth import digest
|
from .auth import digest, is_logged_in
|
||||||
from .constants import (
|
from .constants import (
|
||||||
PermissionLevel, permission_level_string,
|
PermissionLevel, permission_level_string,
|
||||||
InfoboxKind, InfoboxHTMLClass,
|
InfoboxKind, InfoboxHTMLClass,
|
||||||
@@ -221,6 +221,11 @@ def create_app():
|
|||||||
with open('.git/refs/heads/main') as f:
|
with open('.git/refs/heads/main') as f:
|
||||||
commit = f.read().strip()
|
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
|
@app.context_processor
|
||||||
def inject_constants():
|
def inject_constants():
|
||||||
return {
|
return {
|
||||||
@@ -239,6 +244,7 @@ def create_app():
|
|||||||
return {
|
return {
|
||||||
'get_motds': MOTD.get_all,
|
'get_motds': MOTD.get_all,
|
||||||
'get_time_now': lambda: int(time.time()),
|
'get_time_now': lambda: int(time.time()),
|
||||||
|
'is_logged_in': is_logged_in,
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.template_filter('ts_datetime')
|
@app.template_filter('ts_datetime')
|
||||||
@@ -268,23 +274,23 @@ def create_app():
|
|||||||
def basename_noext(subj):
|
def basename_noext(subj):
|
||||||
return os.path.splitext(os.path.basename(subj))[0]
|
return os.path.splitext(os.path.basename(subj))[0]
|
||||||
|
|
||||||
@app.errorhandler(404)
|
# @app.errorhandler(404)
|
||||||
def _handle_404(e):
|
# def _handle_404(e):
|
||||||
if request.path.startswith('/hyperapi/'):
|
# if request.path.startswith('/hyperapi/'):
|
||||||
return '<h1>not found</h1>', e.code
|
# return '<h1>not found</h1>', e.code
|
||||||
elif request.path.startswith('/api/'):
|
# elif request.path.startswith('/api/'):
|
||||||
return {'error': 'not found'}, e.code
|
# return {'error': 'not found'}, e.code
|
||||||
else:
|
# else:
|
||||||
return render_template('common/404.html'), e.code
|
# return render_template('common/404.html'), e.code
|
||||||
|
#
|
||||||
@app.errorhandler(413)
|
# @app.errorhandler(413)
|
||||||
def _handle_413(e):
|
# def _handle_413(e):
|
||||||
if request.path.startswith('/hyperapi/'):
|
# if request.path.startswith('/hyperapi/'):
|
||||||
return '<h1>request body too large</h1>', e.code
|
# return '<h1>request body too large</h1>', e.code
|
||||||
elif request.path.startswith('/api/'):
|
# elif request.path.startswith('/api/'):
|
||||||
return {'error': 'body too large'}, e.code
|
# return {'error': 'body too large'}, e.code
|
||||||
else:
|
# else:
|
||||||
return render_template('common/413.html'), e.code
|
# return render_template('common/413.html'), e.code
|
||||||
|
|
||||||
# this only happens at build time but
|
# this only happens at build time but
|
||||||
# build time is when updates are done anyway
|
# build time is when updates are done anyway
|
||||||
|
|||||||
16
app/auth.py
16
app/auth.py
@@ -1,4 +1,7 @@
|
|||||||
|
from flask import session, flash
|
||||||
|
from .models import Sessions
|
||||||
from argon2 import PasswordHasher
|
from argon2 import PasswordHasher
|
||||||
|
import time
|
||||||
|
|
||||||
ph = PasswordHasher()
|
ph = PasswordHasher()
|
||||||
|
|
||||||
@@ -10,3 +13,16 @@ def verify(expected, given):
|
|||||||
return ph.verify(expected, given)
|
return ph.verify(expected, given)
|
||||||
except:
|
except:
|
||||||
return False
|
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
|
||||||
|
|||||||
@@ -111,24 +111,16 @@ class Topics(Model):
|
|||||||
q = """
|
q = """
|
||||||
SELECT
|
SELECT
|
||||||
topics.id, topics.name, topics.slug, topics.description, topics.is_locked,
|
topics.id, topics.name, topics.slug, topics.description, topics.is_locked,
|
||||||
users.username AS latest_thread_username,
|
COUNT(DISTINCT threads.id) as threads_count,
|
||||||
users.display_name AS latest_thread_display_name,
|
COUNT(posts.id) AS posts_count,
|
||||||
threads.title AS latest_thread_title,
|
MAX(posts.created_at) as latest_post_timestamp
|
||||||
threads.slug AS latest_thread_slug,
|
|
||||||
threads.created_at AS latest_thread_created_at
|
|
||||||
FROM
|
FROM
|
||||||
topics
|
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
|
LEFT JOIN
|
||||||
users on users.id = threads.user_id
|
threads ON threads.topic_id = topics.id
|
||||||
ORDER BY
|
LEFT JOIN
|
||||||
topics.sort_order ASC"""
|
posts ON posts.thread_id = threads.id
|
||||||
|
GROUP BY topics.id ORDER BY topics.sort_order ASC"""
|
||||||
return db.query(q)
|
return db.query(q)
|
||||||
|
|
||||||
def get_threads(self, per_page, page, sort_by = 'activity'):
|
def get_threads(self, per_page, page, sort_by = 'activity'):
|
||||||
|
|||||||
6
app/routes/app.py
Normal file
6
app/routes/app.py
Normal file
@@ -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'))
|
||||||
17
app/routes/topics.py
Normal file
17
app/routes/topics.py
Normal file
@@ -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('/<slug>')
|
||||||
|
def topic(slug):
|
||||||
|
t = Topics.find({'slug': slug})
|
||||||
|
if t:
|
||||||
|
return 'yes'
|
||||||
|
return 'no'
|
||||||
21
app/templates/base.html
Normal file
21
app/templates/base.html
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" type="image/png" href="/static/favicon.png">
|
||||||
|
<link rel="stylesheet" href="{{ "/static/css/style.css" | cachebust }}">
|
||||||
|
{% if self.title() -%}
|
||||||
|
<title>{{ config.SITE_NAME }} - {% block title -%}{%- endblock -%}</title>
|
||||||
|
{%- else -%}
|
||||||
|
<title>{{ config.SITE_NAME }}</title>
|
||||||
|
{%- endif -%}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="wrapper">
|
||||||
|
{%- include 'common/topnav.html' -%}
|
||||||
|
{%- block content -%}{%- endblock -%}
|
||||||
|
{%- include 'common/footer.html' -%}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
9
app/templates/common/footer.html
Normal file
9
app/templates/common/footer.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<footer class="plank secondary-bg bottom">
|
||||||
|
<span>Pyrom commit <a href="{{ "https://git.poto.cafe/yagich/pyrom/commit/" + __commit }}">{{ __commit[:8] }}</a></span>
|
||||||
|
<ul class="horizontal">
|
||||||
|
{#-
|
||||||
|
<li><a href="#">Contact</a></li>
|
||||||
|
<li><a href="#">Guides</a></li>
|
||||||
|
-#}
|
||||||
|
</ul>
|
||||||
|
</footer>
|
||||||
3
app/templates/common/macros.html
Normal file
3
app/templates/common/macros.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{% macro timestamp(unix_ts) -%}
|
||||||
|
<span class="timestamp" data-utc="{{ unix_ts }}">{{ unix_ts | ts_datetime('%Y-%m-%d %H:%M')}} <abbr title="Server Time">ST</abbr></span>
|
||||||
|
{%- endmacro %}
|
||||||
15
app/templates/common/topnav.html
Normal file
15
app/templates/common/topnav.html
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<nav id="header" class="plank top">
|
||||||
|
<a class="site-title" href=#>Porom</a>
|
||||||
|
<span>anti-social media</span>
|
||||||
|
{%- if is_logged_in() -%}
|
||||||
|
no
|
||||||
|
{%- else -%}
|
||||||
|
<form class="horizontal wrap">
|
||||||
|
<input type="hidden" name="return_to" value="{{request.path}}">
|
||||||
|
<input type="text" placeholder="Username">
|
||||||
|
<input type="password" placeholder="Password">
|
||||||
|
<input type="submit" value="Log in">
|
||||||
|
<a href="#" class="linkbutton">Register</a>
|
||||||
|
</form>
|
||||||
|
{%- endif -%}
|
||||||
|
</nav>
|
||||||
23
app/templates/topics/topics.html
Normal file
23
app/templates/topics/topics.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{% from 'common/macros.html' import timestamp %}
|
||||||
|
{%- extends 'base.html' -%}
|
||||||
|
{%- block content -%}
|
||||||
|
{%- for topic in topics -%}
|
||||||
|
<div class="topic-info plank">
|
||||||
|
<div class="title-container">
|
||||||
|
<a class="info" href="{{url_for('topics.topic', slug=topic.slug)}}">{{topic.name}}</a>
|
||||||
|
</div>
|
||||||
|
<div>{{topic.description}}</div>
|
||||||
|
<ul class="horizontal">
|
||||||
|
<li>{{topic.threads_count}} {{"thread" | pluralize(topic.threads_count)}}</li>
|
||||||
|
<li>{{topic.posts_count}} {{"post" | pluralize(topic.posts_count)}}</li>
|
||||||
|
</ul>
|
||||||
|
<div>
|
||||||
|
{%- if topic.latest_post_timestamp -%}
|
||||||
|
Latest post at: {{timestamp(topic.latest_post_timestamp)}}
|
||||||
|
{%- else -%}
|
||||||
|
No posts yet
|
||||||
|
{%- endif -%}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{%- endfor -%}
|
||||||
|
{%- endblock -%}
|
||||||
@@ -76,7 +76,6 @@ body {
|
|||||||
--medium-padding: calc(var(--base-padding) * 2);
|
--medium-padding: calc(var(--base-padding) * 2);
|
||||||
--big-padding: calc(var(--base-padding) * 3);
|
--big-padding: calc(var(--base-padding) * 3);
|
||||||
--huge-padding: calc(var(--base-padding) * 4);
|
--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));
|
--code-bg-color: hsl(from var(--bg-color-primary) h calc(s * 0.2) calc(l * 0.2));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user