diff --git a/.gitignore b/.gitignore index 75f20af..1149575 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ database.db -uploads -__pycache__ \ No newline at end of file +uploads \ No newline at end of file diff --git a/README.md b/README.md index d484197..4c66f85 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ burgercat: burger social media -now back again! - ### self hosting: -this guide assumes you have git, python3, and redis installed on your server +this guide assumes you have git and python3 installed ``` git clone https://codeberg.org/burger-software/burgercat @@ -12,7 +10,6 @@ python init_db python main ``` -### contribution guidelines: -- please check that your PR does not break anything -- unless absolutely nessecary, avoid adding dependecies -- for consistency, use quotation marks, not apostrophes when possible \ No newline at end of file +### zero downtime restarts: +- launch new burgercat server +- close previous server diff --git a/config.ini b/config.ini index 596f2a9..b4471fb 100644 --- a/config.ini +++ b/config.ini @@ -3,4 +3,4 @@ PORT = 8080 SECRET_KEY = placeholder UPLOAD_FOLDER = uploads PASSWORD_REQUIREMENT = 12 -UPLOAD_LIMIT = 12 \ No newline at end of file +UPLOAD_LIMIT = 4 diff --git a/hashpass.py b/hashpass.py deleted file mode 100644 index d64f4fe..0000000 --- a/hashpass.py +++ /dev/null @@ -1,4 +0,0 @@ -from werkzeug.security import generate_password_hash, check_password_hash - -passwordthing = input("insert pass: ") -print(generate_password_hash(passwordthing)) \ No newline at end of file diff --git a/main b/main index 15521d5..a5c1997 100644 --- a/main +++ b/main @@ -7,16 +7,14 @@ import json import secrets import datetime import socket -import threading -import subprocess -import asyncio -from hypercorn.config import Config -from hypercorn.asyncio import serve from itertools import groupby +from waitress import serve from werkzeug.utils import secure_filename from werkzeug.security import generate_password_hash, check_password_hash -from quart import Quart, render_template, request, url_for, flash, redirect, session, make_response, send_from_directory, stream_with_context, Response, request, jsonify, websocket -from apscheduler.schedulers.background import BackgroundScheduler +from werkzeug.middleware.proxy_fix import ProxyFix +from flask import Flask, render_template, request, url_for, flash, redirect, session, make_response, send_from_directory, stream_with_context, Response, request +from flask_limiter import Limiter +from flask_limiter.util import get_remote_address # read config file config = configparser.ConfigParser() @@ -28,10 +26,20 @@ UPLOAD_FOLDER = config["config"]["UPLOAD_FOLDER"] UPLOAD_LIMIT = config["config"]["UPLOAD_LIMIT"] PASSWORD_REQUIREMENT = config["config"]["PASSWORD_REQUIREMENT"] -app = Quart(__name__) +app = Flask(__name__) app.config["SECRET_KEY"] = SECRET_KEY app.config["MAX_CONTENT_LENGTH"] = int(UPLOAD_LIMIT) * 1000 * 1000 +app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1) + +limiter = Limiter( + get_remote_address, + app = app, + default_limits = ["3 per second"], + storage_uri = "memory://", + strategy = "fixed-window" +) + if SECRET_KEY == "placeholder": print("[WARNING] Secret key is not set") @@ -74,16 +82,6 @@ def get_comments(id): return post -def get_messages(chatroomid, max): - conn = get_db_connection() - post = conn.execute("SELECT * FROM chatmessages WHERE chatroom_id = ? ORDER BY created DESC;", - (chatroomid,)).fetchmany(max + 1) - conn.close() - if post is None: - return "error" - return post - - app.jinja_env.globals.update(getComments=get_comments) def get_post(id): @@ -118,17 +116,16 @@ def get_session(id): return "error" return post -full_hash = subprocess.check_output(["git", "rev-parse", "HEAD"]).decode().strip() -short_hash = subprocess.check_output(["git", "rev-parse", "--short=8", "HEAD"]).decode().strip() -ALLOWED_EXTENSIONS = {"png", "apng", "jpg", "jpeg", "gif", "svg", "webp", "jxl"} +ALLOWED_EXTENSIONS = {"png", "apng", "jpg", "jpeg", "gif", "svg", "webp"} def allowed_file(filename): return '.' in filename and \ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS + @app.route("/", methods=("GET", "POST")) -async def main(): +def main(): usersession = request.cookies.get("session_DO_NOT_SHARE") conn = get_db_connection() posts = conn.execute("SELECT * FROM posts ORDER BY created DESC;").fetchall() @@ -137,99 +134,13 @@ async def main(): if usersession: userCookie = get_session(usersession) user = get_user(userCookie["id"]) - return await render_template("main.html", userdata=user, posts=posts, full_hash=full_hash, short_hash=short_hash) + return render_template("main.html", userdata=user, posts=posts) else: - return await render_template("main.html", posts=posts, full_hash=full_hash, short_hash=short_hash) - -@app.route("/chat", methods=("GET", "POST")) -async def chat(): - usersession = request.cookies.get("session_DO_NOT_SHARE") - if usersession: - userCookie = get_session(usersession) - user = get_user(userCookie["id"]) - return await render_template("chat.html", userdata=user) - else: - return await render_template("chat.html") - -@app.route("/api/chat/listrooms") -async def chatlistrooms(): - conn = get_db_connection() - rooms = conn.execute("SELECT * FROM chatrooms ORDER BY id ASC;").fetchall() - conn.close() - - template = [] - - for room in rooms: - roomtemplate = { - "id": room["id"], - "name": room["roomname"] - } - template.append(roomtemplate) - - return(template), 200 - -@app.route("/api/chat/getmessages/") -async def chatget(roomid): - messages = get_messages(roomid, 150) - - template = [] - - for message in messages: - creatorid = message["creator"] - - creatortemplate = { - "id": message["creator"], - "username": get_user(creatorid)["username"] - } - - messagetemplate = { - "id": message["id"], - "content": message["content"], - "creator": creatortemplate, - "created": message["created"] - } - template.append(messagetemplate) - - return(template), 200 - - -@app.route("/api/chat/send/", methods=("GET", "POST")) -async def chatsend(roomid): - usersession = request.cookies.get("session_DO_NOT_SHARE") - if usersession: - if request.method == "POST": - - data = await request.get_json() - content = data["content"] - - userCookie = get_session(usersession) - user = get_user(userCookie["id"]) - - if not user["banned"] == "0": - return { - "error": "banned" - }, 403 - - chatMessageContent = { - "content": content, - "creator": user["username"], - "roomid": roomid, - "created": str(time.time()) - } - - #message_queue.append({"message": chatMessageContent}) - - conn = get_db_connection() - conn.execute("INSERT INTO chatmessages (content, chatroom_id, creator, created) VALUES (?, ?, ?, ?)", - (content, roomid, userCookie["id"], str(time.time()))) - conn.commit() - conn.close() - - return "success", 200 + return render_template("main.html", posts=posts) @app.route("/@", methods=("GET", "POST")) -async def user(pageusername): +def user(pageusername): usersession = request.cookies.get("session_DO_NOT_SHARE") checkusername = check_username_taken(pageusername) @@ -239,20 +150,20 @@ async def user(pageusername): if usersession: userCookie = get_session(usersession) user = get_user(userCookie["id"]) - return await render_template("user.html", userdata=user, createddate=datetime.datetime.utcfromtimestamp(int(str(pageuser["created"]).split(".")[0])).strftime("%Y-%m-%d"), pageuser=pageuser) + return render_template("user.html", userdata=user, createddate=datetime.datetime.utcfromtimestamp(int(str(pageuser["created"]).split(".")[0])).strftime("%Y-%m-%d"), pageuser=pageuser) else: - return await render_template("user.html", createddate=datetime.datetime.utcfromtimestamp(int(str(pageuser["created"]).split(".")[0])).strftime("%Y-%m-%d"), pageuser=pageuser) + return render_template("user.html", createddate=datetime.datetime.utcfromtimestamp(int(str(pageuser["created"]).split(".")[0])).strftime("%Y-%m-%d"), pageuser=pageuser) else: return """""", 404 @app.route("/api/page/", methods=("GET", "POST")) -async def apipageuser(userid): +def apipageuser(userid): pageuser = get_user(userid) addhtml = """ - """ + """ if not pageuser == "error": return addhtml + pageuser["htmldescription"] @@ -260,7 +171,7 @@ async def apipageuser(userid): return """""", 404 @app.route("/@/edit", methods=("GET", "POST")) -async def edituser(pageusername): +def edituser(pageusername): usersession = request.cookies.get("session_DO_NOT_SHARE") checkusername = check_username_taken(pageusername) @@ -272,9 +183,7 @@ async def edituser(pageusername): user = get_user(userCookie["id"]) if pageuser["username"] == user["username"]: if request.method == "POST": - requestData = await request.form - - code = requestData["code"].replace("Content-Security-Policy", "").replace(" - - - - + + + + + burgercat + + + + + + + +
+
+

{{ pageuser.username }}

+
+ {% if pageuser.administrator == 1 %} +

Administrator

+ {% endif %} +
+

Joined on {{ createddate }}

+ {% if userdata %} + {% if pageuser.id == userdata.id %} + edit page +

+ {% endif %} + {% endif %} +
+ +
+ + + \ No newline at end of file