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..d13ed3e 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,9 @@ python init_db python main ``` +### zero downtime restarts: +- launch new burgercat server +- close previous server + ### 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 +- please check that your PR does not break anything \ No newline at end of file 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..279ff4a 100644 --- a/main +++ b/main @@ -1,696 +1,752 @@ -#!/usr/bin/python3 -import os -import configparser -import sqlite3 -import time -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 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 - -# read config file -config = configparser.ConfigParser() -config.read("config.ini") - -PORT = config["config"]["PORT"] -SECRET_KEY = config["config"]["SECRET_KEY"] -UPLOAD_FOLDER = config["config"]["UPLOAD_FOLDER"] -UPLOAD_LIMIT = config["config"]["UPLOAD_LIMIT"] -PASSWORD_REQUIREMENT = config["config"]["PASSWORD_REQUIREMENT"] - -app = Quart(__name__) -app.config["SECRET_KEY"] = SECRET_KEY -app.config["MAX_CONTENT_LENGTH"] = int(UPLOAD_LIMIT) * 1000 * 1000 - -if SECRET_KEY == "placeholder": - print("[WARNING] Secret key is not set") - -if not os.path.exists(UPLOAD_FOLDER): - print("[WARNING] Upload folder doesn't exist, creating") - os.mkdir(UPLOAD_FOLDER) - -if not os.path.exists("database.db"): - print("[ERROR] No database exists, please run init_db") - exit() - - -def makeStrSafe(url): - return str(urllib.parse.quote(url)).replace("%20", " ") - - -def get_db_connection(): - conn = sqlite3.connect("database.db") - conn.row_factory = sqlite3.Row - return conn - - -def get_user(id): - conn = get_db_connection() - post = conn.execute("SELECT * FROM users WHERE id = ?", - (id,)).fetchone() - conn.close() - if post is None: - return "error" - return post - - -def get_comments(id): - conn = get_db_connection() - post = conn.execute("SELECT * FROM comments WHERE post_id = ?", - (id,)).fetchall() - conn.close() - if post is None: - return "error" - 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): - conn = get_db_connection() - post = conn.execute("SELECT * FROM posts WHERE id = ?", - (id,)).fetchone() - conn.close() - if post is None: - return "error" - return post - - -app.jinja_env.globals.update(getUser=get_user) - - -def check_username_taken(username): - conn = get_db_connection() - post = conn.execute("SELECT * FROM users WHERE lower(username) = ?", - (username.lower(),)).fetchone() - conn.close() - if post is None: - return "error" - return post["id"] - - -def get_session(id): - conn = get_db_connection() - post = conn.execute("SELECT * FROM sessions WHERE session = ?", - (id,)).fetchone() - conn.close() - if post is None: - 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"} - -def allowed_file(filename): - return '.' in filename and \ - filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS - -@app.route("/", methods=("GET", "POST")) -async 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() - conn.close() - - 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) - 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 - - -@app.route("/@", methods=("GET", "POST")) -async def user(pageusername): - usersession = request.cookies.get("session_DO_NOT_SHARE") - - checkusername = check_username_taken(pageusername) - - if not checkusername == "error": - pageuser = get_user(checkusername) - 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) - else: - return await 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): - pageuser = get_user(userid) - addhtml = """ - - """ - - if not pageuser == "error": - return addhtml + pageuser["htmldescription"] - else: - return """""", 404 - -@app.route("/@/edit", methods=("GET", "POST")) -async def edituser(pageusername): - usersession = request.cookies.get("session_DO_NOT_SHARE") - - checkusername = check_username_taken(pageusername) - - if not checkusername == "error": - pageuser = get_user(checkusername) - if usersession: - userCookie = get_session(usersession) - 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("