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