we are so back
This commit is contained in:
parent
8ca281b1d3
commit
e6359db97d
|
@ -1,5 +1,7 @@
|
||||||
burgercat: burger social media
|
burgercat: burger social media
|
||||||
|
|
||||||
|
now back again!
|
||||||
|
|
||||||
### self hosting:
|
### self hosting:
|
||||||
this guide assumes you have git, python3, and redis installed on your server
|
this guide assumes you have git, python3, and redis installed on your server
|
||||||
|
|
||||||
|
@ -10,10 +12,6 @@ python init_db
|
||||||
python main
|
python main
|
||||||
```
|
```
|
||||||
|
|
||||||
### zero downtime restarts:
|
|
||||||
- launch new burgercat server
|
|
||||||
- close previous server
|
|
||||||
|
|
||||||
### contribution guidelines:
|
### contribution guidelines:
|
||||||
- please check that your PR does not break anything
|
- please check that your PR does not break anything
|
||||||
- unless absolutely nessecary, avoid adding dependecies
|
- unless absolutely nessecary, avoid adding dependecies
|
||||||
|
|
|
@ -3,5 +3,4 @@ PORT = 8080
|
||||||
SECRET_KEY = placeholder
|
SECRET_KEY = placeholder
|
||||||
UPLOAD_FOLDER = uploads
|
UPLOAD_FOLDER = uploads
|
||||||
PASSWORD_REQUIREMENT = 12
|
PASSWORD_REQUIREMENT = 12
|
||||||
UPLOAD_LIMIT = 4
|
UPLOAD_LIMIT = 12
|
||||||
REDIS_URL = redis://localhost
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
|
|
||||||
|
passwordthing = input("insert pass: ")
|
||||||
|
print(generate_password_hash(passwordthing))
|
206
main
206
main
|
@ -9,15 +9,13 @@ import datetime
|
||||||
import socket
|
import socket
|
||||||
import threading
|
import threading
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import asyncio
|
||||||
|
from hypercorn.config import Config
|
||||||
|
from hypercorn.asyncio import serve
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
from waitress import serve
|
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
from werkzeug.security import generate_password_hash, check_password_hash
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
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 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
|
|
||||||
from flask_sse import sse
|
|
||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
|
|
||||||
# read config file
|
# read config file
|
||||||
|
@ -29,23 +27,10 @@ SECRET_KEY = config["config"]["SECRET_KEY"]
|
||||||
UPLOAD_FOLDER = config["config"]["UPLOAD_FOLDER"]
|
UPLOAD_FOLDER = config["config"]["UPLOAD_FOLDER"]
|
||||||
UPLOAD_LIMIT = config["config"]["UPLOAD_LIMIT"]
|
UPLOAD_LIMIT = config["config"]["UPLOAD_LIMIT"]
|
||||||
PASSWORD_REQUIREMENT = config["config"]["PASSWORD_REQUIREMENT"]
|
PASSWORD_REQUIREMENT = config["config"]["PASSWORD_REQUIREMENT"]
|
||||||
REDIS_URL = config["config"]["REDIS_URL"]
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Quart(__name__)
|
||||||
app.config["SECRET_KEY"] = SECRET_KEY
|
app.config["SECRET_KEY"] = SECRET_KEY
|
||||||
app.config["REDIS_URL"] = REDIS_URL
|
|
||||||
app.config["MAX_CONTENT_LENGTH"] = int(UPLOAD_LIMIT) * 1000 * 1000
|
app.config["MAX_CONTENT_LENGTH"] = int(UPLOAD_LIMIT) * 1000 * 1000
|
||||||
app.register_blueprint(sse, url_prefix="/stream")
|
|
||||||
|
|
||||||
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":
|
if SECRET_KEY == "placeholder":
|
||||||
print("[WARNING] Secret key is not set")
|
print("[WARNING] Secret key is not set")
|
||||||
|
@ -136,15 +121,14 @@ def get_session(id):
|
||||||
full_hash = subprocess.check_output(["git", "rev-parse", "HEAD"]).decode().strip()
|
full_hash = subprocess.check_output(["git", "rev-parse", "HEAD"]).decode().strip()
|
||||||
short_hash = subprocess.check_output(["git", "rev-parse", "--short=8", "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"}
|
ALLOWED_EXTENSIONS = {"png", "apng", "jpg", "jpeg", "gif", "svg", "webp", "jxl"}
|
||||||
|
|
||||||
def allowed_file(filename):
|
def allowed_file(filename):
|
||||||
return '.' in filename and \
|
return '.' in filename and \
|
||||||
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
||||||
|
|
||||||
|
|
||||||
@app.route("/", methods=("GET", "POST"))
|
@app.route("/", methods=("GET", "POST"))
|
||||||
def main():
|
async def main():
|
||||||
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
||||||
conn = get_db_connection()
|
conn = get_db_connection()
|
||||||
posts = conn.execute("SELECT * FROM posts ORDER BY created DESC;").fetchall()
|
posts = conn.execute("SELECT * FROM posts ORDER BY created DESC;").fetchall()
|
||||||
|
@ -153,22 +137,22 @@ def main():
|
||||||
if usersession:
|
if usersession:
|
||||||
userCookie = get_session(usersession)
|
userCookie = get_session(usersession)
|
||||||
user = get_user(userCookie["id"])
|
user = get_user(userCookie["id"])
|
||||||
return render_template("main.html", userdata=user, posts=posts, full_hash=full_hash, short_hash=short_hash)
|
return await render_template("main.html", userdata=user, posts=posts, full_hash=full_hash, short_hash=short_hash)
|
||||||
else:
|
else:
|
||||||
return render_template("main.html", posts=posts, full_hash=full_hash, short_hash=short_hash)
|
return await render_template("main.html", posts=posts, full_hash=full_hash, short_hash=short_hash)
|
||||||
|
|
||||||
@app.route("/chat", methods=("GET", "POST"))
|
@app.route("/chat", methods=("GET", "POST"))
|
||||||
def chat():
|
async def chat():
|
||||||
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
||||||
if usersession:
|
if usersession:
|
||||||
userCookie = get_session(usersession)
|
userCookie = get_session(usersession)
|
||||||
user = get_user(userCookie["id"])
|
user = get_user(userCookie["id"])
|
||||||
return render_template("chat.html", userdata=user)
|
return await render_template("chat.html", userdata=user)
|
||||||
else:
|
else:
|
||||||
return render_template("chat.html")
|
return await render_template("chat.html")
|
||||||
|
|
||||||
@app.route("/api/chat/listrooms")
|
@app.route("/api/chat/listrooms")
|
||||||
def chatlistrooms():
|
async def chatlistrooms():
|
||||||
conn = get_db_connection()
|
conn = get_db_connection()
|
||||||
rooms = conn.execute("SELECT * FROM chatrooms ORDER BY id ASC;").fetchall()
|
rooms = conn.execute("SELECT * FROM chatrooms ORDER BY id ASC;").fetchall()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
@ -185,8 +169,7 @@ def chatlistrooms():
|
||||||
return(template), 200
|
return(template), 200
|
||||||
|
|
||||||
@app.route("/api/chat/getmessages/<roomid>")
|
@app.route("/api/chat/getmessages/<roomid>")
|
||||||
@limiter.exempt
|
async def chatget(roomid):
|
||||||
def chatget(roomid):
|
|
||||||
messages = get_messages(roomid, 150)
|
messages = get_messages(roomid, 150)
|
||||||
|
|
||||||
template = []
|
template = []
|
||||||
|
@ -209,13 +192,14 @@ def chatget(roomid):
|
||||||
|
|
||||||
return(template), 200
|
return(template), 200
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/chat/send/<roomid>", methods=("GET", "POST"))
|
@app.route("/api/chat/send/<roomid>", methods=("GET", "POST"))
|
||||||
def chatsend(roomid):
|
async def chatsend(roomid):
|
||||||
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
||||||
if usersession:
|
if usersession:
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
|
|
||||||
data = request.get_json()
|
data = await request.get_json()
|
||||||
content = data["content"]
|
content = data["content"]
|
||||||
|
|
||||||
userCookie = get_session(usersession)
|
userCookie = get_session(usersession)
|
||||||
|
@ -233,7 +217,7 @@ def chatsend(roomid):
|
||||||
"created": str(time.time())
|
"created": str(time.time())
|
||||||
}
|
}
|
||||||
|
|
||||||
sse.publish({"message": chatMessageContent}, type="publish")
|
#message_queue.append({"message": chatMessageContent})
|
||||||
|
|
||||||
conn = get_db_connection()
|
conn = get_db_connection()
|
||||||
conn.execute("INSERT INTO chatmessages (content, chatroom_id, creator, created) VALUES (?, ?, ?, ?)",
|
conn.execute("INSERT INTO chatmessages (content, chatroom_id, creator, created) VALUES (?, ?, ?, ?)",
|
||||||
|
@ -245,7 +229,7 @@ def chatsend(roomid):
|
||||||
|
|
||||||
|
|
||||||
@app.route("/@<pageusername>", methods=("GET", "POST"))
|
@app.route("/@<pageusername>", methods=("GET", "POST"))
|
||||||
def user(pageusername):
|
async def user(pageusername):
|
||||||
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
||||||
|
|
||||||
checkusername = check_username_taken(pageusername)
|
checkusername = check_username_taken(pageusername)
|
||||||
|
@ -255,20 +239,20 @@ def user(pageusername):
|
||||||
if usersession:
|
if usersession:
|
||||||
userCookie = get_session(usersession)
|
userCookie = get_session(usersession)
|
||||||
user = get_user(userCookie["id"])
|
user = get_user(userCookie["id"])
|
||||||
return render_template("user.html", userdata=user, createddate=datetime.datetime.utcfromtimestamp(int(str(pageuser["created"]).split(".")[0])).strftime("%Y-%m-%d"), pageuser=pageuser)
|
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:
|
else:
|
||||||
return render_template("user.html", createddate=datetime.datetime.utcfromtimestamp(int(str(pageuser["created"]).split(".")[0])).strftime("%Y-%m-%d"), pageuser=pageuser)
|
return await render_template("user.html", createddate=datetime.datetime.utcfromtimestamp(int(str(pageuser["created"]).split(".")[0])).strftime("%Y-%m-%d"), pageuser=pageuser)
|
||||||
else:
|
else:
|
||||||
return """<img src="https://http.cat/images/404.jpg">""", 404
|
return """<img src="https://http.cat/images/404.jpg">""", 404
|
||||||
|
|
||||||
@app.route("/api/page/<userid>", methods=("GET", "POST"))
|
@app.route("/api/page/<userid>", methods=("GET", "POST"))
|
||||||
def apipageuser(userid):
|
async def apipageuser(userid):
|
||||||
pageuser = get_user(userid)
|
pageuser = get_user(userid)
|
||||||
addhtml = """
|
addhtml = """
|
||||||
<!DOCTYPE html><script>
|
<!DOCTYPE html><script>
|
||||||
window.stop()
|
window.stop()
|
||||||
</script>
|
</script>
|
||||||
<base target="_blank"/> <head><meta http-equiv="Content-Security-Policy" default-src='none'; content="img-src cdn.discordapp.com cdn.discordapp.net media.tenor.com; style-src: 'self';" /></head>"""
|
<base target="_blank"/> <head><meta http-equiv="Content-Security-Policy" default-src='none'; content="img-src cdn.discordapp.com cdn.discordapp.net media.tenor.com; media-src: fonts.gstatic.com fonts.googleapis.com; style-src: 'self';" /></head>"""
|
||||||
|
|
||||||
if not pageuser == "error":
|
if not pageuser == "error":
|
||||||
return addhtml + pageuser["htmldescription"]
|
return addhtml + pageuser["htmldescription"]
|
||||||
|
@ -276,7 +260,7 @@ def apipageuser(userid):
|
||||||
return """<img src="https://http.cat/images/404.jpg">""", 404
|
return """<img src="https://http.cat/images/404.jpg">""", 404
|
||||||
|
|
||||||
@app.route("/@<pageusername>/edit", methods=("GET", "POST"))
|
@app.route("/@<pageusername>/edit", methods=("GET", "POST"))
|
||||||
def edituser(pageusername):
|
async def edituser(pageusername):
|
||||||
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
||||||
|
|
||||||
checkusername = check_username_taken(pageusername)
|
checkusername = check_username_taken(pageusername)
|
||||||
|
@ -288,7 +272,9 @@ def edituser(pageusername):
|
||||||
user = get_user(userCookie["id"])
|
user = get_user(userCookie["id"])
|
||||||
if pageuser["username"] == user["username"]:
|
if pageuser["username"] == user["username"]:
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
code = request.form["code"].replace("Content-Security-Policy", "").replace("<iframe>", "")
|
requestData = await request.form
|
||||||
|
|
||||||
|
code = requestData["code"].replace("Content-Security-Policy", "").replace("<iframe>", "")
|
||||||
conn = get_db_connection()
|
conn = get_db_connection()
|
||||||
conn.execute("UPDATE users SET htmldescription = ? WHERE id = ?",
|
conn.execute("UPDATE users SET htmldescription = ? WHERE id = ?",
|
||||||
(code, user["id"]))
|
(code, user["id"]))
|
||||||
|
@ -296,7 +282,7 @@ def edituser(pageusername):
|
||||||
conn.close()
|
conn.close()
|
||||||
return redirect("/@" + user["username"])
|
return redirect("/@" + user["username"])
|
||||||
else:
|
else:
|
||||||
return render_template("edituser.html", userdata=user, pageuser=pageuser)
|
return await render_template("edituser.html", userdata=user, pageuser=pageuser)
|
||||||
else:
|
else:
|
||||||
return """<img src="https://http.cat/images/403.jpg">""", 403
|
return """<img src="https://http.cat/images/403.jpg">""", 403
|
||||||
else:
|
else:
|
||||||
|
@ -306,7 +292,7 @@ def edituser(pageusername):
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/frontpage", methods=("GET", "POST"))
|
@app.route("/api/frontpage", methods=("GET", "POST"))
|
||||||
def apifrontpage():
|
async def apifrontpage():
|
||||||
conn = get_db_connection()
|
conn = get_db_connection()
|
||||||
posts = conn.execute("SELECT * FROM posts ORDER BY created DESC;").fetchall()
|
posts = conn.execute("SELECT * FROM posts ORDER BY created DESC;").fetchall()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
@ -346,7 +332,7 @@ def apifrontpage():
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@app.route("/api/userinfo", methods=("GET", "POST"))
|
@app.route("/api/userinfo", methods=("GET", "POST"))
|
||||||
def apiuserinfo():
|
async def apiuserinfo():
|
||||||
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
||||||
if usersession:
|
if usersession:
|
||||||
userCookie = get_session(usersession)
|
userCookie = get_session(usersession)
|
||||||
|
@ -363,12 +349,11 @@ def apiuserinfo():
|
||||||
}, 403
|
}, 403
|
||||||
|
|
||||||
|
|
||||||
@limiter.limit("10/minute", override_defaults=False)
|
|
||||||
@app.route("/api/login", methods=("GET", "POST"))
|
@app.route("/api/login", methods=("GET", "POST"))
|
||||||
def apilogin():
|
async def apilogin():
|
||||||
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
data = request.get_json()
|
data = await request.get_json()
|
||||||
username = data["username"]
|
username = data["username"]
|
||||||
password = data["password"]
|
password = data["password"]
|
||||||
|
|
||||||
|
@ -402,56 +387,56 @@ def apilogin():
|
||||||
}, 400
|
}, 400
|
||||||
|
|
||||||
@app.route("/apidocs", methods=("GET", "POST"))
|
@app.route("/apidocs", methods=("GET", "POST"))
|
||||||
def apidocs():
|
async def apidocs():
|
||||||
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
||||||
|
|
||||||
if usersession:
|
if usersession:
|
||||||
userCookie = get_session(usersession)
|
userCookie = get_session(usersession)
|
||||||
user = get_user(userCookie["id"])
|
user = get_user(userCookie["id"])
|
||||||
return render_template("apidocs.html", userdata=user)
|
return await render_template("apidocs.html", userdata=user)
|
||||||
else:
|
else:
|
||||||
return render_template("apidocs.html")
|
return await render_template("apidocs.html")
|
||||||
|
|
||||||
@app.route("/post", methods=("GET", "POST"))
|
@app.route("/post", methods=("GET", "POST"))
|
||||||
def post():
|
async def post():
|
||||||
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
||||||
if usersession:
|
if usersession:
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
title = request.form["title"]
|
formData = await request.form
|
||||||
|
formFiles = await request.files
|
||||||
|
|
||||||
|
title = formData["title"]
|
||||||
if title == "":
|
if title == "":
|
||||||
flash("Text required :3")
|
await flash("Text required :3")
|
||||||
return redirect(url_for("post"))
|
return redirect(url_for("post"))
|
||||||
|
|
||||||
if len(title) > 300:
|
if len(title) > 300:
|
||||||
flash("Too long title!")
|
await flash("Too long title!")
|
||||||
return redirect(url_for("post"))
|
return redirect(url_for("post"))
|
||||||
|
|
||||||
if "file" not in request.files:
|
file = formFiles["file"]
|
||||||
flash("No file selected :3")
|
|
||||||
return redirect(url_for("post"))
|
|
||||||
|
|
||||||
file = request.files["file"]
|
|
||||||
if file.filename == "":
|
|
||||||
flash("No file selected :3")
|
|
||||||
return redirect(url_for("post"))
|
|
||||||
|
|
||||||
|
if (file):
|
||||||
if not allowed_file(file.filename):
|
if not allowed_file(file.filename):
|
||||||
flash("File is not an image!")
|
await flash("File is not an image!")
|
||||||
return redirect(url_for("post"))
|
return redirect(url_for("post"))
|
||||||
|
|
||||||
userCookie = get_session(usersession)
|
userCookie = get_session(usersession)
|
||||||
user = get_user(userCookie["id"])
|
user = get_user(userCookie["id"])
|
||||||
|
|
||||||
if not user["banned"] == "0":
|
if not user["banned"] == "0":
|
||||||
flash("Your account has been banned. You may no longer perform this action.")
|
await flash("Your account has been suspended. You may no longer post")
|
||||||
return redirect(url_for("post"))
|
return redirect(url_for("post"))
|
||||||
|
|
||||||
filename = secure_filename(file.filename)
|
filename = secure_filename(file.filename)
|
||||||
finalfilename = secrets.token_hex(32) + filename
|
finalfilename = secrets.token_hex(32) + filename
|
||||||
|
|
||||||
file.save(os.path.join(UPLOAD_FOLDER, finalfilename))
|
await file.save(os.path.join(UPLOAD_FOLDER, finalfilename))
|
||||||
imgurl = "/cdn/" + finalfilename
|
imgurl = "/cdn/" + finalfilename
|
||||||
|
|
||||||
|
if (not file):
|
||||||
|
imgurl = ""
|
||||||
|
|
||||||
conn = get_db_connection()
|
conn = get_db_connection()
|
||||||
conn.execute("INSERT INTO posts (textstr, imageurl, creator, created) VALUES (?, ?, ?, ?)",
|
conn.execute("INSERT INTO posts (textstr, imageurl, creator, created) VALUES (?, ?, ?, ?)",
|
||||||
(title, imgurl, userCookie["id"], str(time.time())))
|
(title, imgurl, userCookie["id"], str(time.time())))
|
||||||
|
@ -462,20 +447,19 @@ def post():
|
||||||
else:
|
else:
|
||||||
userCookie = get_session(usersession)
|
userCookie = get_session(usersession)
|
||||||
user = get_user(userCookie["id"])
|
user = get_user(userCookie["id"])
|
||||||
return render_template("post.html", userdata=user)
|
return await render_template("post.html", userdata=user)
|
||||||
else:
|
else:
|
||||||
flash("A burgercat account is required to post :3")
|
flash("A burgercat account is required to post :3")
|
||||||
return redirect(url_for("login"))
|
return redirect(url_for("login"))
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/comment", methods=("GET", "POST"))
|
@app.route("/api/comment", methods=("GET", "POST"))
|
||||||
@limiter.limit("1/second", override_defaults=False)
|
async def comment():
|
||||||
def comment():
|
|
||||||
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
||||||
if usersession:
|
if usersession:
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
|
|
||||||
data = request.get_json()
|
data = await request.get_json()
|
||||||
uid = data["id"]
|
uid = data["id"]
|
||||||
title = data["title"]
|
title = data["title"]
|
||||||
|
|
||||||
|
@ -510,7 +494,7 @@ def comment():
|
||||||
}, 401
|
}, 401
|
||||||
|
|
||||||
@app.route("/api/post/<post_id>/comments", methods=("GET", "POST"))
|
@app.route("/api/post/<post_id>/comments", methods=("GET", "POST"))
|
||||||
def apicomments(post_id):
|
async def apicomments(post_id):
|
||||||
postthing = get_comments(int(post_id))
|
postthing = get_comments(int(post_id))
|
||||||
|
|
||||||
if not postthing == "error":
|
if not postthing == "error":
|
||||||
|
@ -528,61 +512,62 @@ def apicomments(post_id):
|
||||||
return comments
|
return comments
|
||||||
|
|
||||||
@app.route("/cdn/<filename>", methods=("GET", "POST"))
|
@app.route("/cdn/<filename>", methods=("GET", "POST"))
|
||||||
@limiter.exempt
|
async def cdn(filename):
|
||||||
def cdn(filename):
|
|
||||||
if os.path.exists(os.path.join(UPLOAD_FOLDER, filename)):
|
if os.path.exists(os.path.join(UPLOAD_FOLDER, filename)):
|
||||||
return send_from_directory(UPLOAD_FOLDER, filename)
|
return await send_from_directory(UPLOAD_FOLDER, filename)
|
||||||
else:
|
else:
|
||||||
return "file doesn't exist!!"
|
return "file doesn't exist!!"
|
||||||
|
|
||||||
|
|
||||||
@app.route("/signup", methods=("GET", "POST"))
|
@app.route("/signup", methods=("GET", "POST"))
|
||||||
@limiter.limit("5/minute", override_defaults=False)
|
async def signup():
|
||||||
def signup():
|
|
||||||
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
||||||
if usersession:
|
if usersession:
|
||||||
return redirect(url_for("main"))
|
return redirect(url_for("main"))
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
if not check_username_taken(request.form["username"]) == "error":
|
requestData = await request.form
|
||||||
flash("Username already taken :3")
|
|
||||||
|
if not check_username_taken(requestData["username"]) == "error":
|
||||||
|
await flash("Username already taken :3")
|
||||||
return redirect(url_for("signup"))
|
return redirect(url_for("signup"))
|
||||||
|
|
||||||
if not request.form["username"].isalnum():
|
if not requestData["username"].isalnum():
|
||||||
flash("Username must be alphanumeric :3")
|
await flash("Username must be alphanumeric :3")
|
||||||
return redirect(url_for("signup"))
|
return redirect(url_for("signup"))
|
||||||
|
|
||||||
if not len(request.form["password"]) > int(PASSWORD_REQUIREMENT):
|
if not len(requestData["password"]) > int(PASSWORD_REQUIREMENT):
|
||||||
flash("Password must contain at least " + PASSWORD_REQUIREMENT + " characters")
|
await flash("Password must contain at least " + PASSWORD_REQUIREMENT + " characters")
|
||||||
return redirect(url_for("signup"))
|
return redirect(url_for("signup"))
|
||||||
|
|
||||||
hashedpassword = generate_password_hash(request.form["password"])
|
hashedpassword = generate_password_hash(requestData["password"])
|
||||||
|
|
||||||
conn = get_db_connection()
|
conn = get_db_connection()
|
||||||
conn.execute("INSERT INTO users (username, password, created, htmldescription) VALUES (?, ?, ?, ?)",
|
conn.execute("INSERT INTO users (username, password, created, htmldescription) VALUES (?, ?, ?, ?)",
|
||||||
(request.form["username"], hashedpassword, str(time.time()), ""))
|
(requestData["username"], hashedpassword, str(time.time()), ""))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
return redirect(url_for("login"))
|
return redirect(url_for("login"))
|
||||||
else:
|
else:
|
||||||
return render_template("signup.html")
|
return await render_template("signup.html")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/login", methods=("GET", "POST"))
|
@app.route("/login", methods=("GET", "POST"))
|
||||||
@limiter.limit("10/minute", override_defaults=False)
|
async def login():
|
||||||
def login():
|
|
||||||
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
||||||
if usersession:
|
if usersession:
|
||||||
redirect(url_for("main"))
|
redirect(url_for("main"))
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
userID = check_username_taken(request.form["username"])
|
requestData = await request.form
|
||||||
|
|
||||||
|
userID = check_username_taken(requestData["username"])
|
||||||
user = get_user(userID)
|
user = get_user(userID)
|
||||||
|
|
||||||
if user == "error":
|
if user == "error":
|
||||||
flash("Wrong username or password :3")
|
flash("Wrong username or password :3")
|
||||||
return redirect(url_for("login"))
|
return redirect(url_for("login"))
|
||||||
|
|
||||||
if not check_password_hash(user["password"], (request.form["password"])):
|
if not check_password_hash(user["password"], (requestData["password"])):
|
||||||
flash("Wrong username or password :3")
|
flash("Wrong username or password :3")
|
||||||
return redirect(url_for("login"))
|
return redirect(url_for("login"))
|
||||||
|
|
||||||
|
@ -594,32 +579,37 @@ def login():
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
resp = make_response(redirect("/"))
|
response = Response("""<script>window.location.href = "/";</script>""")
|
||||||
resp.set_cookie("session_DO_NOT_SHARE", randomCharacters, expires=datetime.datetime.now() + datetime.timedelta(days=float(180)))
|
response.set_cookie("session_DO_NOT_SHARE", randomCharacters)
|
||||||
|
return response
|
||||||
|
|
||||||
|
#resp = await make_response(redirect("/"))
|
||||||
|
#resp.set_cookie("session_DO_NOT_SHARE", randomCharacters, expires=datetime.datetime.now() + datetime.timedelta(days=float(180)))
|
||||||
|
|
||||||
|
#return redirect("/")
|
||||||
|
|
||||||
return resp
|
|
||||||
else:
|
else:
|
||||||
return render_template("login.html")
|
return await render_template("login.html")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/settings", methods=("GET", "POST"))
|
@app.route("/settings", methods=("GET", "POST"))
|
||||||
def settings():
|
async def settings():
|
||||||
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
||||||
if usersession:
|
if usersession:
|
||||||
userCookie = get_session(usersession)
|
userCookie = get_session(usersession)
|
||||||
user = get_user(userCookie["id"])
|
user = get_user(userCookie["id"])
|
||||||
|
|
||||||
return render_template("settings.html", userdata=user, createddate=datetime.datetime.utcfromtimestamp(int(str(user["created"]).split(".")[0])).strftime("%Y-%m-%d %H:%m:%S"))
|
return await render_template("settings.html", userdata=user, createddate=datetime.datetime.utcfromtimestamp(int(str(user["created"]).split(".")[0])).strftime("%Y-%m-%d %H:%m:%S"))
|
||||||
else:
|
else:
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/delete", methods=("GET", "POST"))
|
@app.route("/api/delete", methods=("GET", "POST"))
|
||||||
def delete():
|
async def delete():
|
||||||
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
data = request.get_json()
|
data = await request.get_json()
|
||||||
postid = int(data["id"])
|
postid = int(data["id"])
|
||||||
|
|
||||||
post = get_post(postid)
|
post = get_post(postid)
|
||||||
|
@ -645,7 +635,7 @@ def delete():
|
||||||
}, 400
|
}, 400
|
||||||
|
|
||||||
@app.route("/listusers", methods=("GET", "POST"))
|
@app.route("/listusers", methods=("GET", "POST"))
|
||||||
def listusers():
|
async def listusers():
|
||||||
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
||||||
if usersession:
|
if usersession:
|
||||||
userCookie = get_session(usersession)
|
userCookie = get_session(usersession)
|
||||||
|
@ -667,7 +657,7 @@ def listusers():
|
||||||
|
|
||||||
|
|
||||||
@app.route("/settings/logout", methods=("GET", "POST"))
|
@app.route("/settings/logout", methods=("GET", "POST"))
|
||||||
def logout():
|
async def logout():
|
||||||
resp = redirect(url_for("main"))
|
resp = redirect(url_for("main"))
|
||||||
session = request.cookies.get("session_DO_NOT_SHARE")
|
session = request.cookies.get("session_DO_NOT_SHARE")
|
||||||
resp.delete_cookie("session_DO_NOT_SHARE")
|
resp.delete_cookie("session_DO_NOT_SHARE")
|
||||||
|
@ -675,34 +665,32 @@ def logout():
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
@app.errorhandler(500)
|
@app.errorhandler(500)
|
||||||
def page_not_found(e):
|
async def page_not_found(e):
|
||||||
return """<img src="https://http.cat/images/500.jpg">""", 500
|
return """<img src="https://http.cat/images/500.jpg">""", 500
|
||||||
|
|
||||||
@app.errorhandler(400)
|
@app.errorhandler(400)
|
||||||
def page_not_found(e):
|
async def page_not_found(e):
|
||||||
return """<img src="https://http.cat/images/400.jpg">""", 400
|
return """<img src="https://http.cat/images/400.jpg">""", 400
|
||||||
|
|
||||||
@app.errorhandler(429)
|
@app.errorhandler(429)
|
||||||
def page_not_found(e):
|
async def page_not_found(e):
|
||||||
return """<img src="https://http.cat/images/429.jpg">""", 429
|
return """<img src="https://http.cat/images/429.jpg">""", 429
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def page_not_found(e):
|
async def page_not_found(e):
|
||||||
return """<img src="https://http.cat/images/404.jpg">""", 404
|
return """<img src="https://http.cat/images/404.jpg">""", 404
|
||||||
|
|
||||||
@app.errorhandler(413)
|
@app.errorhandler(413)
|
||||||
def page_not_found(e):
|
async def page_not_found(e):
|
||||||
return "Images can't be larger than " + str(UPLOAD_LIMIT) + "MB", 413
|
return "Images can't be larger than " + str(UPLOAD_LIMIT) + "MB", 413
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print("[INFO] Server started")
|
print("[INFO] Server started")
|
||||||
|
|
||||||
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as sock:
|
hypercornconfig = Config()
|
||||||
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
|
hypercornconfig.bind = ("0.0.0.0" + ":" + PORT)
|
||||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
||||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
asyncio.run(serve(app, hypercornconfig))
|
||||||
sock.bind(('', int(PORT)))
|
|
||||||
serve(app, sockets=[sock], threads=9999)
|
|
||||||
|
|
||||||
print("[INFO] Server stopped")
|
print("[INFO] Server stopped")
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
Flask
|
quart
|
||||||
Flask-Limiter
|
|
||||||
werkzeug
|
werkzeug
|
||||||
waitress
|
hypercorn
|
||||||
requests
|
requests
|
||||||
Flask-SSE
|
|
||||||
APScheduler
|
|
|
@ -1,4 +1,5 @@
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Inter&display=swap");
|
@import url("https://fonts.googleapis.com/css2?family=Inter&display=swap");
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300..700&display=swap");
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -10,13 +11,18 @@ body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: solid;
|
border: solid;
|
||||||
border-color: grey;
|
border-color: rgb(195, 195, 195);
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
font-family: "Space Grotesk", sans-serif;
|
||||||
|
font-optical-sizing: auto;
|
||||||
|
}
|
||||||
|
.navbar h1 {
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom {
|
.bottom {
|
||||||
|
@ -33,11 +39,16 @@ body {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navbar a {
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.navbar .selected {
|
.navbar .selected {
|
||||||
border: solid;
|
border: solid;
|
||||||
border-color: #f1b739;
|
border-color: #f1b739;
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
border-bottom-width: 2px;
|
border-bottom-width: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.postDiv {
|
.postDiv {
|
||||||
|
@ -80,6 +91,11 @@ body {
|
||||||
margin-right: 20%;
|
margin-right: 20%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.accountform h1 {
|
||||||
|
font-family: "Space Grotesk", sans-serif;
|
||||||
|
font-optical-sizing: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.accountform input {
|
.accountform input {
|
||||||
padding: 7px;
|
padding: 7px;
|
||||||
border: solid;
|
border: solid;
|
||||||
|
@ -128,12 +144,17 @@ body {
|
||||||
|
|
||||||
.post {
|
.post {
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
padding: 5px;
|
padding: 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background-color: rgb(250, 250, 250);
|
background-color: rgb(250, 250, 250);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.post h2, .post h3 {
|
||||||
|
font-family: "Space Grotesk", sans-serif;
|
||||||
|
font-optical-sizing: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.post img {
|
.post img {
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -76,25 +76,6 @@ function selectChannel(id) {
|
||||||
updateMessages(id)
|
updateMessages(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectChannel(id) {
|
|
||||||
channelID = id
|
|
||||||
|
|
||||||
let selectedButton = channelDiv.querySelector(".selected");
|
|
||||||
if (selectedButton) {
|
|
||||||
selectedButton.classList.remove("selected");
|
|
||||||
}
|
|
||||||
|
|
||||||
let channelButton = document.getElementById("channelButton" + id)
|
|
||||||
if (channelButton) {
|
|
||||||
channelButton.classList.add("selected")
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log("channelButton not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
updateMessages(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateRooms() {
|
async function updateRooms() {
|
||||||
let response = await fetch("/api/chat/listrooms");
|
let response = await fetch("/api/chat/listrooms");
|
||||||
let rooms = await response.json()
|
let rooms = await response.json()
|
||||||
|
@ -122,32 +103,34 @@ async function sendMessage(content, id) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let updateInterval = 4100
|
||||||
|
|
||||||
messageBox.addEventListener("keyup", function onEvent(event) {
|
messageBox.addEventListener("keyup", function onEvent(event) {
|
||||||
if (event.key === "Enter") {
|
if (event.key === "Enter") {
|
||||||
if (!messageBox.value == "") {
|
if (!messageBox.value == "") {
|
||||||
if (messageBox.value.length < 140) {
|
if (messageBox.value.length < 140) {
|
||||||
sendMessage(messageBox.value, channelID)
|
sendMessage(messageBox.value, channelID)
|
||||||
messageBox.value = ""
|
messageBox.value = ""
|
||||||
|
updateInterval = 1300
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
let messageStream = new EventSource("/stream")
|
|
||||||
|
|
||||||
window.addEventListener("load", function () {
|
|
||||||
updateRooms()
|
|
||||||
updateMessages(channelID)
|
updateMessages(channelID)
|
||||||
|
|
||||||
messageStream.addEventListener("publish", function (event) {
|
|
||||||
results = JSON.parse(event.data)
|
|
||||||
|
|
||||||
if (Number(results["message"]["roomid"]) == channelID) {
|
|
||||||
addMessage(results["message"]["content"], results["message"]["created"], results["message"]["creator"], results["message"]["roomid"])
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
window.addEventListener("beforeunload", function () {
|
function messageTimer(){
|
||||||
messageStream.close()
|
updateMessages(channelID)
|
||||||
})
|
if (updateInterval < 6000) {
|
||||||
|
updateInterval = updateInterval + 100
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(updateInterval)
|
||||||
|
setTimeout(messageTimer, updateInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
messageTimer();
|
||||||
|
|
||||||
|
|
||||||
|
updateRooms()
|
||||||
|
updateMessages()
|
||||||
|
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>burgercat</title>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
|
||||||
<link rel="stylesheet" type="text/css" href="/static/css/style.css" />
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="post">
|
|
||||||
<h2>burgercat is permanently shutting down on July 21th.</h2>
|
|
||||||
</div>
|
|
||||||
<div class="post">
|
|
||||||
<p>After 19 days of service, burgercat is permanently shutting down on July 21th 2023. 😔<br><br>
|
|
||||||
After July 21th, you will no longer be able to access burgercat. Please back up anything you want to keep before July 21th.</p>
|
|
||||||
<p>The Codeberg repository will be archived. Third-party instances will not be affected.</p>
|
|
||||||
<p><b>Thank you for using burgercat</b>, and thank <a href="https://codeberg.org/ffqq">@ffqq</a>, <a href="https://codeberg.org/carsand101">@carsand101</a>, <a href="https://codeberg.org/Mollomm1">@Mollomm1</a>, <a href="https://codeberg.org/TestingPlant">@TestingPlant</a>, <a href="https://codeberg.org/sewn">@sewn</a>, and <a href="/@Anonymous">@Anonymous</a> for contributing to the project.</p>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -13,8 +13,9 @@
|
||||||
<div class="navbar">
|
<div class="navbar">
|
||||||
<h1>burgercat</h1>
|
<h1>burgercat</h1>
|
||||||
<a href="/">home</a>
|
<a href="/">home</a>
|
||||||
|
<a href="/chat">chat</a>
|
||||||
<a href="/post">post</a>
|
<a href="/post">post</a>
|
||||||
<a class="selected" href="/apidocs">API</a>
|
<a class="selected" href="/apidocs">api</a>
|
||||||
{% if userdata %}
|
{% if userdata %}
|
||||||
<a href="/settings/logout" class="right r">log out</a>
|
<a href="/settings/logout" class="right r">log out</a>
|
||||||
<a href="/settings" class="right">{{ userdata.username }}</a>
|
<a href="/settings" class="right">{{ userdata.username }}</a>
|
||||||
|
@ -32,7 +33,7 @@
|
||||||
<div class="postDiv">
|
<div class="postDiv">
|
||||||
<div class="post">
|
<div class="post">
|
||||||
<h2>burgercat API documentation</h2>
|
<h2>burgercat API documentation</h2>
|
||||||
for API things that require authentication, you will need to set the <code>session_DO_NOT_SHARE</code> cookie. the key might expire after 180 days.<br><br>
|
for API endpoints that require authentication, you'll need to set the <code>session_DO_NOT_SHARE</code> cookie. key might expire after 180 days.<br><br>
|
||||||
|
|
||||||
GET <code>/api/frontpage</code> - returns frontpage<br><br>
|
GET <code>/api/frontpage</code> - returns frontpage<br><br>
|
||||||
POST <code>/post</code> - post ctas - authentication required<br>
|
POST <code>/post</code> - post ctas - authentication required<br>
|
||||||
|
@ -43,9 +44,9 @@
|
||||||
POST <code>/api/delete</code> - remove posts - authentication required<br>
|
POST <code>/api/delete</code> - remove posts - authentication required<br>
|
||||||
<code>id</code>, being the id of the post. you must be the owner of the post to delete.<br><br>
|
<code>id</code>, being the id of the post. you must be the owner of the post to delete.<br><br>
|
||||||
POST <code>/api/login</code> - get authentication key<br>
|
POST <code>/api/login</code> - get authentication key<br>
|
||||||
<code>username</code>, being the username and <code>password</code>, being the password. Returns authentication key.<br><br>
|
<code>username</code>, being the username and <code>password</code>, being the password.<br><br>
|
||||||
GET <code>/api/userinfo</code> - authentication required - Returns user info, username, ID, and account creation date.<br><br>
|
GET <code>/api/userinfo</code> - authentication required - Returns user info, username, ID, and account creation date.<br><br>
|
||||||
GET <code>/api/post/1/comments</code> - Returns comments of post
|
GET <code>/api/post/1/comments</code> - Returns comments of post. Replace 1 with the post ID.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -36,7 +36,6 @@
|
||||||
<p class="warning">Your account has been banned. Reason: "{{ userdata.banned }}". <a href="/settings/logout">Log out</a></p>
|
<p class="warning">Your account has been banned. Reason: "{{ userdata.banned }}". <a href="/settings/logout">Log out</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<p class="warning">⚠️ burgercat will permanently shut down on July 21th. ⚠️ <a href="/static/shutdown.html">Read more</a></p>
|
|
||||||
{% for post in posts %}
|
{% for post in posts %}
|
||||||
<div class="post" id="post">
|
<div class="post" id="post">
|
||||||
<p><a href='/@{{ getUser(post["creator"])["username"] }}' class="username usernamelink">{{ getUser(post["creator"])["username"] }}</a></p>
|
<p><a href='/@{{ getUser(post["creator"])["username"] }}' class="username usernamelink">{{ getUser(post["creator"])["username"] }}</a></p>
|
||||||
|
@ -44,7 +43,9 @@
|
||||||
{% if userdata %}
|
{% if userdata %}
|
||||||
<p class="hidden" id="usernameElement">{{ userdata.username }}</p>
|
<p class="hidden" id="usernameElement">{{ userdata.username }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if post["imageurl"] != "" %}
|
||||||
<img loading="lazy" src='{{ post["imageurl"] }}'>
|
<img loading="lazy" src='{{ post["imageurl"] }}'>
|
||||||
|
{% endif %}
|
||||||
<p class="text">{{ post["textstr"] }}</p>
|
<p class="text">{{ post["textstr"] }}</p>
|
||||||
{% if getComments(post["id"]) | length > 0 %}
|
{% if getComments(post["id"]) | length > 0 %}
|
||||||
<div id="commentBurgerDiv" class="commentsdiv">
|
<div id="commentBurgerDiv" class="commentsdiv">
|
||||||
|
@ -114,12 +115,6 @@
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<div class="bottom">
|
|
||||||
<span>
|
|
||||||
<a href="https://codeberg.org/burger-software/burgercat">burgercat</a>
|
|
||||||
<a href="https://codeberg.org/burger-software/burgercat/commit/{{ full_hash }}" style="float: right;">commit hash: {{ short_hash }}</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<script src="/static/js/main.js"></script>
|
<script src="/static/js/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
@ -34,28 +34,28 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
<form method="post" enctype="multipart/form-data">
|
<form method="post" enctype="multipart/form-data">
|
||||||
<h2>Post</h2>
|
<h2>New post</h2>
|
||||||
<p>Image</p>
|
<p>Image</p>
|
||||||
<input type="file" name="file" required>
|
<input type="file" name="file">
|
||||||
<br>
|
<br>
|
||||||
<p>Text</p>
|
<p>Text (required)</p>
|
||||||
<input name="title" type="text" placeholder="Text" required>
|
<input name="title" type="text" placeholder="Type something here.." required>
|
||||||
<br><br>
|
<br><br>
|
||||||
<input class="submit" type="submit" value="Post">
|
<input class="submit" type="submit" value="Post">
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="post">
|
<div class="post">
|
||||||
<form method="post" enctype="multipart/form-data">
|
<form method="post" enctype="multipart/form-data">
|
||||||
<h2>Rules</h2>
|
<h2>Posting Guidelines</h2>
|
||||||
|
<p>keep burgercat amazing: follow the posting guidelines</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Please do not spam</li>
|
<li>Spam content isn't allowed, this includes promoting services/products.</li>
|
||||||
<li>Treat everyone with respect, respect each others opinions</li>
|
<li>Treat everyone with respect, please respect others opinions.</li>
|
||||||
<li>Posting NSFW content is strictly prohibited, doing so will get you an instant ban</li>
|
<li>Posting NSFW content is strictly prohibited. This includes suggestive content.</li>
|
||||||
<li>Discussions regarding politics and related controversial topics are disallowed</li>
|
<li>We have zero-tolerance for racism, homophobia, transphobia etc.</li>
|
||||||
<li>Advertising is not allowed</li>
|
<li>Avoid politics and rage-bait.</li>
|
||||||
<li>Do not post links</li>
|
|
||||||
<li>Do not create alt-accounts to evade bans</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
<p>You might be moderated for things not listed here, use common sense.</p>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -26,13 +26,14 @@
|
||||||
|
|
||||||
<div class="postDiv">
|
<div class="postDiv">
|
||||||
<div class="post">
|
<div class="post">
|
||||||
<h3>Account</h3>
|
<h2>Your account</h2>
|
||||||
<p>Account Type: {% if userdata.administrator == 1 %}Administrator{% else %}Normal{% endif %}<br>
|
<a href="/@{{ userdata.username }}">View my public profile</a><br>
|
||||||
|
<h3>Information</h3>
|
||||||
|
<p>Account Type: {% if userdata.administrator == 1 %}Administrator{% else %}Regular{% endif %}<br>
|
||||||
Username: {{ userdata.username }}<br>
|
Username: {{ userdata.username }}<br>
|
||||||
User ID: {{ userdata.id }}<br>
|
User ID: {{ userdata.id }}<br>
|
||||||
Created on: {{ createddate }}</p>
|
Created on: {{ createddate }}</p>
|
||||||
<br>
|
<h3>Actions</h3>
|
||||||
<a href="/@{{ userdata.username }}">View my public profile</a><br>
|
|
||||||
<a href="/settings/logout">Log out</a>
|
<a href="/settings/logout">Log out</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Reference in New Issue