diff --git a/db/sqlite.py b/db/sqlite.py index 3ba8d41..cc44416 100644 --- a/db/sqlite.py +++ b/db/sqlite.py @@ -340,5 +340,52 @@ class DB: # User already exists, probably return False + def get_users(self): + return self._db().execute(''' + select user_id, name, join_time, role, banned_until + from users + ''', + ) + + def set_forum_name(self, forum_id, name): + return self.change_one(''' + update forums + set name = ? + where forum_id = ? + ''', + (name, forum_id) + ) + + def set_forum_description(self, forum_id, description): + return self.change_one(''' + update forums + set description = ? + where forum_id = ? + ''', + (description, forum_id) + ) + + def add_forum(self, name, description): + db = self._db() + db.execute(''' + insert into forums(name, description) + values (?, ?) + ''', + (name, description) + ) + db.commit() + + def change_one(self, query, values): + db = self._db() + c = db.cursor() + c.execute(query, values) + if c.rowcount > 0: + db.commit() + return True + return False + + def query(self, q): + return self._db().execute(q) + def _db(self): return sqlite3.connect(self.conn) diff --git a/main.py b/main.py index e477f9d..55d7a22 100644 --- a/main.py +++ b/main.py @@ -308,6 +308,67 @@ def register(): answer = answer, ) +@app.route('/admin/') +def admin(): + user = get_user() + if user is None: + return redirect(url_for('login')) + if not user.is_admin(): + return '

Forbidden

', 403 + + return render_template( + 'admin/index.html', + title = 'Admin panel', + forums = db.get_forums(), + users = db.get_users(), + ) + +@app.route('/admin/query/', methods = ['GET', 'POST']) +def admin_query(): + user = get_user() + if user is None: + return redirect(url_for('login')) + if not user.is_admin(): + return '

Forbidden

', 403 + + try: + rows = db.query(request.form['q']) if request.method == 'POST' else [] + except Exception as e: + flash(e, 'error') + rows = [] + return render_template( + 'admin/query.html', + title = 'Query', + rows = rows, + ) + +@app.route('/admin/forum//edit//', methods = ['POST']) +def admin_edit_forum(forum_id, what): + try: + if what == 'description': + res = db.set_forum_description(forum_id, request.form['description'].replace('\r', '')) + elif what == 'name': + res = db.set_forum_name(forum_id, request.form['name']) + else: + flash(f'Unknown property "{what}"', 'error') + res = None + if res is True: + flash(f'Updated {what}', 'success') + elif res is False: + flash(f'Failed to update {what}', 'error') + except Exception as e: + flash(e, 'error') + return redirect(url_for('admin')) + +@app.route('/admin/forum/new/', methods = ['POST']) +def admin_new_forum(): + try: + db.add_forum(request.form['name'], request.form['description'].replace('\r', '')) + flash('Added forum', 'success') + except Exception as e: + flash(str(e), 'error') + return redirect(url_for('admin')) + class Comment: def __init__(self, id, author_id, author, text, create_time, modify_time, parent_id): @@ -407,6 +468,9 @@ def utility_processor(): # This shouldn't be reachable, but it's still better to return something return "incredibly long ago" + def format_time(t): + return datetime.utcfromtimestamp(t / 10 ** 9) + def minimd(text): # Replace angle brackets to prevent XSS # Also replace ampersands to prevent surprises. @@ -420,6 +484,7 @@ def utility_processor(): return { 'format_since': format_since, + 'format_time': format_time, 'minimd': minimd, } diff --git a/schema.txt b/schema.txt index 596bfca..af2fee7 100644 --- a/schema.txt +++ b/schema.txt @@ -5,7 +5,8 @@ create table users ( email varchar(254), about text not null default '', join_time integer not null, - role integer not null default 0 + role integer not null default 0, + banned_until integer ); create table threads ( @@ -34,10 +35,9 @@ create table comments ( ); create table forums ( - forum_id integer unique not null primary key autoincrement, + forum_id integer unique not null primary key autoincrement, name varchar(64) not null, - description text, - allowed_roles_mask integer not null + description text not null default '' ); -- Both of these speed up searches significantly if there are many threads or comments. diff --git a/templates/admin/base.html b/templates/admin/base.html new file mode 100644 index 0000000..a864555 --- /dev/null +++ b/templates/admin/base.html @@ -0,0 +1,45 @@ +{# Don't use the default theme to emphasize this page is special -#} + + +{{ title }} + + + + + +

{{ title }}

+

+Admin panel +Home page +

+{%- for category, msg in get_flashed_messages(True) -%} +

{{ msg }}

+{%- endfor %} +{%- block content %}{% endblock -%} + diff --git a/templates/admin/index.html b/templates/admin/index.html new file mode 100644 index 0000000..d6a3765 --- /dev/null +++ b/templates/admin/index.html @@ -0,0 +1,101 @@ +{% extends 'admin/base.html' -%} +{% block content -%} +

Query

+

⚠ Only use queries if you know what you're doing ⚠

+
+ + +
+

Configuration

+ + + + + + + + + +
Name + + +
Registrations enabled + + +
+

Forums

+ + + + + + + +{% for id, name, description, _, _, _ in forums %} + + + + + +{% endfor %} +
IDNameDescriptionActions
{{ id }} +
+ + +
+
+
+ + +
+
Remove
+

Add forum

+
+ + + + + + + + + +
Name
Description
+ +
+

Users

+ + + + + + + + + +{%- for id, name, join_date, role, banned_until in users -%} + + + + + + + + +{%- endfor -%} +
IDNameJoin dateRoleBanned untilActions
{{ id }}{{ name }}{{ format_time(join_date) }}{{ ['user', 'moderator', 'admin'][role] if role >= 0 and role <= 2 else role }}{{ '' if banned_until is none else format_time(banned_until) }} +
+ + + +
+
+ + +
+
+{%- endblock %} diff --git a/templates/admin/query.html b/templates/admin/query.html new file mode 100644 index 0000000..8c89a92 --- /dev/null +++ b/templates/admin/query.html @@ -0,0 +1,17 @@ +{% extends 'admin/base.html' -%} +{% block content -%} +
+ + +
+ +{%- for r in rows -%} + +{%- for c in r -%} + +{%- endfor -%} + +{%- endfor -%} +
{{ c }}
+{%- endblock -%} + diff --git a/templates/base.html b/templates/base.html index 662d81d..4c165a3 100644 --- a/templates/base.html +++ b/templates/base.html @@ -12,6 +12,10 @@ {% if user is not none %} {{ user.name }} | + {% if user.is_admin() %} + Admin panel + | + {% endif %} Logout {% else %} Register diff --git a/templates/index.html b/templates/index.html index 6725f52..32ac9be 100644 --- a/templates/index.html +++ b/templates/index.html @@ -10,7 +10,7 @@

{{ name }}

-

{{ description }}

+

{{ minimd(description) | safe }}

{% if t_id %} @@ -21,6 +21,6 @@ No threads {% endif %} - {% endfor %} + {%- endfor -%} {% endblock %} diff --git a/test/init_db.txt b/test/init_db.txt index b77856d..94ad0c0 100644 --- a/test/init_db.txt +++ b/test/init_db.txt @@ -12,15 +12,16 @@ insert into users (name, password, email, join_time) values ( "bar@foo.baz", 0 ); -insert into users (name, password, join_time) values ( +insert into users (name, password, join_time, role) values ( "bazzers", -- e "$argon2id$v=19$m=65536,t=3,p=4$9v5fS2ktxTinNEbIGUOoFQ$LMdEuAuuTCJ7utOE88+nXn7o6R/DEKY8ZA6wV+YkVGQ", - 0 + 0, + 2 ); -insert into forums (name, description, allowed_roles_mask) - values ("Earth", "The totality of all space and time; all that is, has been, and will be.", 1); +insert into forums (name, description) + values ("Earth", "The totality of all space and time; all that is, has been, and will be."); insert into threads (author_id, forum_id, create_time, modify_time, update_time, title, text) values (1, 1, 0, 0, 0, "Hello, world!",