moved config to a json, which makes adding more variables easier, but perhaps otherwise adds complexity
This commit is contained in:
28
forum/change_admin_pw.sh
Normal file
28
forum/change_admin_pw.sh
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SQLITE=sqlite3
|
||||
PYTHON=python3
|
||||
|
||||
set -eu
|
||||
|
||||
if [[ -z "$DB" ]]; then
|
||||
echo set DB env to the Sqlite database.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read -p 'Admin username: ' username
|
||||
read -sp 'Admin password: ' password
|
||||
|
||||
|
||||
password=$($PYTHON tool.py password "$password")
|
||||
time=$($PYTHON -c 'import time; print(time.time_ns())')
|
||||
|
||||
$SQLITE "$DB" "
|
||||
UPDATE users
|
||||
SET password = '$password',
|
||||
role = 2,
|
||||
join_time = $time
|
||||
WHERE
|
||||
user = lower('$username')
|
||||
"
|
||||
|
||||
54
forum/db/config.py
Normal file
54
forum/db/config.py
Normal file
@@ -0,0 +1,54 @@
|
||||
import json
|
||||
import shutil
|
||||
|
||||
|
||||
class Config:
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
self.config_values = [
|
||||
"version",
|
||||
"server_name",
|
||||
"server_description",
|
||||
"secret_key",
|
||||
"captcha_key",
|
||||
"registration_enabled",
|
||||
"login_required",
|
||||
"threads_per_page",
|
||||
"user_css",
|
||||
]
|
||||
self._update_class(self._read_values())
|
||||
|
||||
def get_config(self):
|
||||
current = self._read_values()
|
||||
self._update_class(current)
|
||||
return self
|
||||
|
||||
def set_config(self, **kwargs):
|
||||
for key in kwargs:
|
||||
if key not in self.config_values:
|
||||
raise ValueError(f"Unknown config key {key}!")
|
||||
|
||||
current = self._read_values()
|
||||
current.update(kwargs)
|
||||
self._update_class(current)
|
||||
self._write_values(current)
|
||||
|
||||
def set_config_secrets(self, secret_key, captcha_key):
|
||||
current = self._read_values()
|
||||
current["secret_key"] = secret_key
|
||||
current["captcha_key"] = captcha_key
|
||||
self._update_class(current)
|
||||
self._write_values(current)
|
||||
|
||||
def _read_values(self):
|
||||
with open(self.path, "rt") as fp:
|
||||
return json.load(fp)
|
||||
|
||||
def _write_values(self, values):
|
||||
shutil.copy(self.path, self.path + ".bkp")
|
||||
with open(self.path, "wt") as fp:
|
||||
json.dump(values, fp, indent=2, sort_keys=True)
|
||||
|
||||
def _update_class(self, values):
|
||||
for item in values:
|
||||
setattr(self, item, values[item])
|
||||
@@ -1,4 +1,5 @@
|
||||
import sqlite3
|
||||
from db.config import Config
|
||||
|
||||
|
||||
class DB:
|
||||
@@ -6,16 +7,16 @@ class DB:
|
||||
self.conn = conn
|
||||
pass
|
||||
|
||||
def get_config(self):
|
||||
return (
|
||||
self._db()
|
||||
.execute(
|
||||
"""
|
||||
select version, name, description, secret_key, captcha_key, registration_enabled, login_required from config
|
||||
"""
|
||||
)
|
||||
.fetchone()
|
||||
)
|
||||
# ~ def get_config(self):
|
||||
# ~ return (
|
||||
# ~ self._db()
|
||||
# ~ .execute(
|
||||
# ~ """
|
||||
# ~ select version, name, description, secret_key, captcha_key, registration_enabled, login_required from config
|
||||
# ~ """
|
||||
# ~ )
|
||||
# ~ .fetchone()
|
||||
# ~ )
|
||||
|
||||
def get_forums(self):
|
||||
return self._db().execute(
|
||||
@@ -48,7 +49,7 @@ class DB:
|
||||
)
|
||||
|
||||
def get_thread_forum(self, thread_id):
|
||||
""" Returns forum_id of a thread """
|
||||
"""Returns forum_id of a thread"""
|
||||
return (
|
||||
self._db()
|
||||
.execute(
|
||||
@@ -104,7 +105,16 @@ class DB:
|
||||
|
||||
def get_thread(self, thread):
|
||||
db = self._db()
|
||||
title, text, author, author_id, create_time, modify_time, hidden, forum_id = db.execute(
|
||||
(
|
||||
title,
|
||||
text,
|
||||
author,
|
||||
author_id,
|
||||
create_time,
|
||||
modify_time,
|
||||
hidden,
|
||||
forum_id,
|
||||
) = db.execute(
|
||||
"""
|
||||
select title, text, name, author_id, create_time, modify_time, hidden, forum_id
|
||||
from threads, users
|
||||
@@ -139,7 +149,7 @@ class DB:
|
||||
modify_time,
|
||||
comments,
|
||||
hidden,
|
||||
forum_id
|
||||
forum_id,
|
||||
)
|
||||
|
||||
def get_thread_title(self, thread_id):
|
||||
@@ -525,14 +535,16 @@ class DB:
|
||||
Add a user if registrations are enabled.
|
||||
"""
|
||||
try:
|
||||
config = Config(os.getenv("CONF"))
|
||||
if not config.registration_enable:
|
||||
return None
|
||||
|
||||
db = self._db()
|
||||
c = db.cursor()
|
||||
c.execute(
|
||||
"""
|
||||
insert into users(name, password, join_time)
|
||||
select lower(?), ?, ?
|
||||
from config
|
||||
where registration_enabled = 1
|
||||
values lower(?), ?, ?
|
||||
""",
|
||||
(username, password, time),
|
||||
)
|
||||
@@ -616,25 +628,25 @@ class DB:
|
||||
)
|
||||
db.commit()
|
||||
|
||||
def set_config(
|
||||
self, server_name, server_description, registration_enabled, login_required
|
||||
):
|
||||
return self.change_one(
|
||||
"""
|
||||
update config
|
||||
set name = ?, description = ?, registration_enabled = ?, login_required = ?
|
||||
""",
|
||||
(server_name, server_description, registration_enabled, login_required),
|
||||
)
|
||||
# ~ def set_config(
|
||||
# ~ self, server_name, server_description, registration_enabled, login_required
|
||||
# ~ ):
|
||||
# ~ return self.change_one(
|
||||
# ~ """
|
||||
# ~ update config
|
||||
# ~ set name = ?, description = ?, registration_enabled = ?, login_required = ?
|
||||
# ~ """,
|
||||
# ~ (server_name, server_description, registration_enabled, login_required),
|
||||
# ~ )
|
||||
|
||||
def set_config_secrets(self, secret_key, captcha_key):
|
||||
return self.change_one(
|
||||
"""
|
||||
update config
|
||||
set secret_key = ?, captcha_key = ?
|
||||
""",
|
||||
(secret_key, captcha_key),
|
||||
)
|
||||
# ~ def set_config_secrets(self, secret_key, captcha_key):
|
||||
# ~ return self.change_one(
|
||||
# ~ """
|
||||
# ~ update config
|
||||
# ~ set secret_key = ?, captcha_key = ?
|
||||
# ~ """,
|
||||
# ~ (secret_key, captcha_key),
|
||||
# ~ )
|
||||
|
||||
def set_user_ban(self, user_id, until):
|
||||
return self.change_one(
|
||||
|
||||
@@ -3,6 +3,7 @@ PYTHON=python3
|
||||
SQLITE=sqlite3
|
||||
|
||||
export DB="/app/forum.db"
|
||||
export CONF="/app/forum.config"
|
||||
export SERVER=gunicorn
|
||||
export PID="forum.pid"
|
||||
export WORKERS=4
|
||||
@@ -15,36 +16,6 @@ fi
|
||||
|
||||
set -eu
|
||||
. /opt/venv/bin/activate
|
||||
if [[ -e "$DB" ]]; then
|
||||
$SQLITE -header "$DB" "SELECT version,name,description,registration_enabled,login_required FROM config"
|
||||
echo Database already exists
|
||||
else
|
||||
password=$($PYTHON tool.py password "$ADMINP")
|
||||
time=$($PYTHON -c 'import time; print(time.time_ns())')
|
||||
version=$($PYTHON tool.py version)
|
||||
|
||||
$SQLITE "$DB" -init schema.txt "insert into config (
|
||||
version,
|
||||
name,
|
||||
description,
|
||||
secret_key,
|
||||
captcha_key,
|
||||
registration_enabled,
|
||||
login_required
|
||||
)
|
||||
values (
|
||||
'$version',
|
||||
'Forum',
|
||||
'',
|
||||
'$(head -c 30 /dev/urandom | base64)',
|
||||
'$(head -c 30 /dev/urandom | base64)',
|
||||
0,
|
||||
1
|
||||
)"
|
||||
$SQLITE "$DB" "
|
||||
insert into users (name, password, role, join_time)
|
||||
values (lower('$ADMINU'), '$password', 2, $time)
|
||||
"
|
||||
fi
|
||||
sh ./init_forum.sh
|
||||
|
||||
exec "$SERVER" -w $WORKERS 'main:app' --pid="$PID" -b 0.0.0.0:5000
|
||||
|
||||
51
forum/init_forum.sh
Executable file
51
forum/init_forum.sh
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SQLITE=sqlite3
|
||||
PYTHON=python3
|
||||
|
||||
set -e
|
||||
|
||||
if [ -z "$DB" ]; then
|
||||
echo set DB env to point the Sqlite database.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$CONF" ]; then
|
||||
echo set CONF env to point the config file.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -e "$CONF" ]; then
|
||||
echo Config exists
|
||||
else
|
||||
version=$($PYTHON tool.py version)
|
||||
cat <<EOF > "$CONF"
|
||||
{
|
||||
"version": "$version",
|
||||
"server_name": "Forum",
|
||||
"server_description": "",
|
||||
"secret_key": "$(head -c 30 /dev/urandom | base64)",
|
||||
"captcha_key": "$(head -c 30 /dev/urandom | base64)",
|
||||
"registration_enabled": false,
|
||||
"login_required": true,
|
||||
"threads_per_page": 50,
|
||||
"user_css": ""
|
||||
}
|
||||
EOF
|
||||
echo "Config '$CONF' created" >&2
|
||||
|
||||
fi
|
||||
|
||||
if [ -e "$DB" ]; then
|
||||
echo Database already exists
|
||||
else
|
||||
password=$($PYTHON tool.py password "$ADMINP")
|
||||
time=$($PYTHON -c 'import time; print(time.time_ns())')
|
||||
|
||||
$SQLITE "$DB" -init schema.txt "
|
||||
insert into users (name, password, role, join_time)
|
||||
values (lower('$ADMINU'), '$password', 2, $time)
|
||||
"
|
||||
echo "Database '$DB' created" >&2
|
||||
fi
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SQLITE=sqlite3
|
||||
PYTHON=python3
|
||||
|
||||
set -e
|
||||
|
||||
make
|
||||
. ./venv/bin/activate
|
||||
|
||||
if [ $# -le 0 ]
|
||||
then
|
||||
echo "Usage: $0 <file> [--no-admin]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -e "$1" ]
|
||||
then
|
||||
echo "Database '$1' already exists" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$2" != --no-admin ]
|
||||
then
|
||||
read -p 'Admin username: ' username
|
||||
read -sp 'Admin password: ' password
|
||||
fi
|
||||
|
||||
password=$($PYTHON tool.py password "$password")
|
||||
time=$($PYTHON -c 'import time; print(time.time_ns())')
|
||||
|
||||
$SQLITE "$1" -init schema.txt "insert into config (
|
||||
version,
|
||||
name,
|
||||
description,
|
||||
secret_key,
|
||||
captcha_key,
|
||||
registration_enabled,
|
||||
login_required
|
||||
)
|
||||
values (
|
||||
'agreper-v0.1.1',
|
||||
'Agreper',
|
||||
'',
|
||||
'$(head -c 30 /dev/urandom | base64)',
|
||||
'$(head -c 30 /dev/urandom | base64)',
|
||||
0,
|
||||
0
|
||||
)"
|
||||
if [ "$2" != --no-admin ]
|
||||
then
|
||||
$SQLITE "$1" "
|
||||
insert into users (name, password, role, join_time)
|
||||
values (lower('$username'), '$password', 2, $time)
|
||||
"
|
||||
fi
|
||||
|
||||
echo "Database '$1' created" >&2
|
||||
@@ -1,9 +1,8 @@
|
||||
from version import VERSION
|
||||
# TODO put in config table
|
||||
THREADS_PER_PAGE = 50
|
||||
|
||||
from flask import Flask, render_template, session, request, redirect, url_for, flash, g
|
||||
from db.sqlite import DB
|
||||
from db.config import Config
|
||||
import os, sys, subprocess
|
||||
import passlib.hash, secrets
|
||||
import time
|
||||
@@ -13,33 +12,33 @@ import captcha, password, minimd
|
||||
|
||||
app = Flask(__name__)
|
||||
db = DB(os.getenv("DB"))
|
||||
|
||||
config = Config(os.getenv("CONF"))
|
||||
# This defaults to None, which allows CSRF attacks in FireFox
|
||||
# and older versions of Chrome.
|
||||
# 'Lax' is sufficient to prevent malicious POST requests.
|
||||
app.config["SESSION_COOKIE_SAMESITE"] = "Lax"
|
||||
app.config["SECRET_KEY"] = config.secret_key
|
||||
|
||||
|
||||
class Config:
|
||||
pass
|
||||
|
||||
|
||||
config = Config()
|
||||
(
|
||||
config.version,
|
||||
config.server_name,
|
||||
config.server_description,
|
||||
app.config["SECRET_KEY"],
|
||||
config.captcha_key,
|
||||
config.registration_enabled,
|
||||
config.login_required
|
||||
) = db.get_config()
|
||||
config.user_css = os.path.exists(os.path.join(app.static_folder, 'user.css'))
|
||||
# ~ class Config:
|
||||
# ~ pass
|
||||
# ~ config = Config()
|
||||
# ~ (
|
||||
# ~ config.version,
|
||||
# ~ config.server_name,
|
||||
# ~ config.server_description,
|
||||
# ~ app.config["SECRET_KEY"],
|
||||
# ~ config.captcha_key,
|
||||
# ~ config.registration_enabled,
|
||||
# ~ config.login_required
|
||||
# ~ ) = db.get_config()
|
||||
# ~ app.config['user_css'] = os.path.exists(os.path.join(app.static_folder, 'user.css'))
|
||||
# ~ config.threads_per_page = 50
|
||||
|
||||
if config.version != VERSION:
|
||||
print(f"Incompatible version {config.version} (expected {VERSION})")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class Role:
|
||||
USER = 0
|
||||
MODERATOR = 1
|
||||
@@ -50,11 +49,10 @@ class Role:
|
||||
def before_request():
|
||||
if config.login_required:
|
||||
user_id = session.get("user_id", -1)
|
||||
if user_id == -1 and request.endpoint not in ("login","static"):
|
||||
if user_id == -1 and request.endpoint not in ("login", "static"):
|
||||
return redirect(url_for("login"))
|
||||
|
||||
|
||||
|
||||
@app.after_request
|
||||
def after_request(response):
|
||||
# This forbids other sites from embedding this site in an iframe,
|
||||
@@ -80,10 +78,10 @@ def forum(forum_id):
|
||||
title, description = db.get_forum(forum_id)
|
||||
offset = int(request.args.get("p", 0))
|
||||
user_id = session.get("user_id", -1)
|
||||
threads = [*db.get_threads(forum_id, offset, THREADS_PER_PAGE + 1, user_id)]
|
||||
if len(threads) == THREADS_PER_PAGE + 1:
|
||||
threads = [*db.get_threads(forum_id, offset, config.threads_per_page + 1, user_id)]
|
||||
if len(threads) == config.threads_per_page + 1:
|
||||
threads.pop()
|
||||
next_page = offset + THREADS_PER_PAGE
|
||||
next_page = offset + config.threads_per_page
|
||||
else:
|
||||
next_page = None
|
||||
return render_template(
|
||||
@@ -95,7 +93,7 @@ def forum(forum_id):
|
||||
description=description,
|
||||
threads=threads,
|
||||
next_page=next_page,
|
||||
prev_page=max(offset - THREADS_PER_PAGE, 0) if offset > 0 else None,
|
||||
prev_page=max(offset - config.threads_per_page, 0) if offset > 0 else None,
|
||||
)
|
||||
|
||||
|
||||
@@ -111,7 +109,7 @@ def thread(thread_id):
|
||||
modify_time,
|
||||
comments,
|
||||
hidden,
|
||||
forum_id
|
||||
forum_id,
|
||||
) = db.get_thread(thread_id)
|
||||
forum_title, _ = db.get_forum(forum_id)
|
||||
|
||||
@@ -155,7 +153,7 @@ def comment(comment_id):
|
||||
parent_id=parent_id,
|
||||
thread_id=thread_id,
|
||||
forum_id=forum_id,
|
||||
forum_title=forum_title
|
||||
forum_title=forum_title,
|
||||
)
|
||||
|
||||
|
||||
@@ -532,11 +530,14 @@ def admin_edit_config():
|
||||
return user
|
||||
|
||||
try:
|
||||
db.set_config(
|
||||
request.form["server_name"],
|
||||
trim_text(request.form["server_description"]),
|
||||
"registration_enabled" in request.form,
|
||||
"login_required" in request.form,
|
||||
# db.set_config(
|
||||
config.set_config(
|
||||
server_name=request.form["server_name"],
|
||||
server_description=trim_text(request.form["server_description"]),
|
||||
registration_enabled="registration_enabled" in request.form,
|
||||
login_required="login_required" in request.form,
|
||||
threads_per_page=int(request.form["threads_per_page"]),
|
||||
user_css=request.form["user_css"],
|
||||
)
|
||||
flash("Updated config. Refresh the page to see the changes.", "success")
|
||||
restart()
|
||||
@@ -554,7 +555,8 @@ def admin_new_secrets():
|
||||
secret_key = secrets.token_urlsafe(30)
|
||||
captcha_key = secrets.token_urlsafe(30)
|
||||
try:
|
||||
db.set_config_secrets(secret_key, captcha_key)
|
||||
# ~ db.set_config_secrets(secret_key, captcha_key)
|
||||
config.set_config_secrets(secret_key, captcha_key)
|
||||
flash("Changed secrets. You will be logged out.", "success")
|
||||
restart()
|
||||
except Exception as e:
|
||||
@@ -776,7 +778,7 @@ def create_comment_tree(comments, user):
|
||||
|
||||
# Sort each comment based on create time
|
||||
def sort_time(l):
|
||||
l.sort(key=lambda c: c.modify_time, reverse=True)
|
||||
l.sort(key=lambda c: c.modify_time, reverse=False)
|
||||
for c in l:
|
||||
sort_time(c.children)
|
||||
|
||||
|
||||
@@ -14,8 +14,9 @@ RE_PLAINURL = re.compile(
|
||||
r"(?P<pre>^|\s|\n)(?P<url>https?://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-]))(?P<post>\s|\n|$)"
|
||||
)
|
||||
|
||||
|
||||
def html(text):
|
||||
text = RE_PLAINURL.sub(r'\g<pre>[\g<url>](\g<url>)\g<post>', text)
|
||||
text = RE_PLAINURL.sub(r"\g<pre>[\g<url>](\g<url>)\g<post>", text)
|
||||
return markdown2.markdown(text)
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
SERVER="$1"
|
||||
if [ -z "$SERVER" ]
|
||||
then
|
||||
echo "SERVER is not set" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo Restarting service >&2
|
||||
case "$SERVER" in
|
||||
dev)
|
||||
touch main.py
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
create table config (
|
||||
version text not null,
|
||||
name text not null,
|
||||
description text not null,
|
||||
secret_key text not null,
|
||||
captcha_key text not null,
|
||||
registration_enabled boolean not null,
|
||||
login_required boolean not null
|
||||
);
|
||||
--create table config (
|
||||
-- version text not null,
|
||||
-- name text not null,
|
||||
-- description text not null,
|
||||
-- secret_key text not null,
|
||||
-- captcha_key text not null,
|
||||
-- registration_enabled boolean not null,
|
||||
-- login_required boolean not null
|
||||
--);
|
||||
|
||||
create table users (
|
||||
user_id integer unique not null primary key autoincrement,
|
||||
|
||||
@@ -4,16 +4,17 @@
|
||||
<title>{{ title }}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta content="utf-8" http-equiv="encoding">
|
||||
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||
<link rel=stylesheet href="{{ url_for('static', filename='theme.css') }}">
|
||||
{%- if config.server_name -%}
|
||||
<link rel=stylesheet href="{{ url_for('static', filename='user.css') }}">
|
||||
{%- if config.user_css -%}
|
||||
<link rel=stylesheet href="{{ url_for('static', filename=config.user_css) }}">
|
||||
{%- endif -%}
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{ title }}</h1>
|
||||
<p>
|
||||
<a href="{{ url_for('admin') }}">Admin panel</a><span> | </span>
|
||||
<a href="{{ url_for('index') }}">Home page</a>
|
||||
<span> « </span><a href="{{ url_for('index') }}">Forum Home</a>
|
||||
<span> | </span><a href="{{ url_for('admin') }}">Admin panel</a>
|
||||
</p>
|
||||
{%- for category, msg in get_flashed_messages(True) -%}
|
||||
<p class="flash {{ category }}">{{ msg }}</p>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</tr>
|
||||
{%- for id, name, join_date, role, banned_until in users -%}
|
||||
<tr>
|
||||
<td>{{ id }}</td>
|
||||
<td><a href="{{ url_for('user_info', user_id = id) }}">{{ id }}</a></td>
|
||||
<td>{{ name }}</td>
|
||||
<td>{{ format_time(join_date) }}</td>
|
||||
<td>
|
||||
@@ -61,7 +61,7 @@
|
||||
</tr>
|
||||
{% for id, name, description, _, _, _ in forums %}
|
||||
<tr>
|
||||
<td>{{ id }}</td>
|
||||
<td><a href="{{ url_for('forum', forum_id = id) }}">{{ id }}</a></td>
|
||||
<td>
|
||||
<form method=post action="forum/{{ id }}/edit/name/">
|
||||
<input type=text name=name value="{{ name }}"</input>
|
||||
@@ -114,6 +114,14 @@
|
||||
<td>Login required</td>
|
||||
<td><input name=login_required type=checkbox {{ 'checked' if config.login_required else '' }}></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Number of threads per page</td>
|
||||
<td><input type=number name=threads_per_page value="{{ config.threads_per_page }}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>User defined CSS file in static/ folder</td>
|
||||
<td><input type=text name=user_css value="{{ config.user_css }}"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<input type=submit value=Update>
|
||||
</form>
|
||||
@@ -129,8 +137,6 @@
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- -->
|
||||
<h2 class=admin_h2>Query</h2>
|
||||
<p>⚠ Only use queries if you know what you're doing ⚠</p>
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<meta content="utf-8" http-equiv="encoding">
|
||||
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||
<link rel=stylesheet href="{{ url_for('static', filename='theme.css') }}">
|
||||
{%- if config.server_name -%}
|
||||
<link rel=stylesheet href="{{ url_for('static', filename='user.css') }}">
|
||||
{%- if config.user_css -%}
|
||||
<link rel=stylesheet href="{{ url_for('static', filename=config.user_css) }}">
|
||||
{%- endif -%}
|
||||
</head>
|
||||
<body>
|
||||
@@ -50,5 +50,6 @@
|
||||
<p class="flash {{ category }}">{{ msg | safe }}</p>
|
||||
{%- endfor -%}
|
||||
{%- block content %}{% endblock -%}
|
||||
<a id="end"></a>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{% block content %}
|
||||
<form method="post" class=login>
|
||||
<table>
|
||||
<tr><td>Username</td><td><input type="text" name="username" required></td></tr>
|
||||
<tr><td>Username</td><td><input type="text" name="username" required autofocus></td></tr>
|
||||
<tr><td>Password</td><td><input type="password" name="password" required></td></tr>
|
||||
</table>
|
||||
<input type="submit" value="Login">
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
{%- from 'moderator.html' import moderate_thread with context %}
|
||||
|
||||
{%- block content %}
|
||||
<p><span> « </span><a href="{{ url_for('forum', forum_id = forum_id) }}">{{ forum_title }}</a></p>
|
||||
<p>
|
||||
<span> « </span><a href="{{ url_for('forum', forum_id = forum_id) }}">{{ forum_title }}</a>
|
||||
<span> » </span><a href="#end">Page end</a>
|
||||
</p>
|
||||
{%- if user is not none and user.is_moderator() -%}
|
||||
<p>{{ moderate_thread(thread_id, hidden) }}</p>
|
||||
{%- endif -%}
|
||||
@@ -15,4 +18,7 @@
|
||||
{%- for c in comments %}
|
||||
{{- render_comment(c, thread_id) }}
|
||||
{%- endfor %}
|
||||
<p>
|
||||
<span> » </span><a href="#top">Page top</a>
|
||||
</p>
|
||||
{%- endblock %}
|
||||
|
||||
@@ -25,6 +25,7 @@ if cmd == "password":
|
||||
print(password.hash(pwd))
|
||||
elif cmd == "version":
|
||||
from version import VERSION
|
||||
|
||||
print(VERSION)
|
||||
else:
|
||||
print("unknown command ", cmd)
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
|
||||
VERSION = "agreper-v0.1.1q1"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user