This commit is contained in:
maaa 2023-07-08 18:00:32 +02:00
parent 30575bcf20
commit c548e8c406
15 changed files with 897 additions and 714 deletions

2
.gitignore vendored
View File

@ -1,2 +1,2 @@
database.db database.db
uploads uploads

View File

@ -1,7 +1,7 @@
[config] [config]
HOST = 0.0.0.0 HOST = 0.0.0.0
PORT = 8080 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 = 4

View File

@ -1,21 +1,21 @@
#!/usr/bin/python3 #!/usr/bin/python3
import sqlite3 import sqlite3
import os import os
userid = input("Insert ID: ") userid = input("Insert ID: ")
changewhat = input("Change what value?: ") changewhat = input("Change what value?: ")
towhat = input("What should " + changewhat + " be changed to?: ") towhat = input("What should " + changewhat + " be changed to?: ")
def get_db_connection(): def get_db_connection():
conn = sqlite3.connect("database.db") conn = sqlite3.connect("database.db")
conn.row_factory = sqlite3.Row conn.row_factory = sqlite3.Row
return conn return conn
conn = get_db_connection() conn = get_db_connection()
conn.execute("UPDATE users SET " + changewhat + " = ?" conn.execute("UPDATE users SET " + changewhat + " = ?"
" WHERE id = ?", " WHERE id = ?",
(str(towhat), userid)) (str(towhat), userid))
conn.commit() conn.commit()
conn.close() conn.close()
print("Success!") print("Success!")

56
init_db
View File

@ -1,29 +1,29 @@
#!/usr/bin/python3 #!/usr/bin/python3
import sqlite3 import sqlite3
import os import os
def generatedb(): def generatedb():
connection = sqlite3.connect("database.db") connection = sqlite3.connect("database.db")
with open("schema.sql") as f: with open("schema.sql") as f:
connection.executescript(f.read()) connection.executescript(f.read())
cur = connection.cursor() cur = connection.cursor()
connection.commit() connection.commit()
connection.close() connection.close()
print("[INFO] Generated database") print("[INFO] Generated database")
if not os.path.exists("database.db"): if not os.path.exists("database.db"):
generatedb() generatedb()
else: else:
answer = input("Proceeding will overwrite the database. Proceed? (y/N)") answer = input("Proceeding will overwrite the database. Proceed? (y/N)")
if "y" in answer.lower(): if "y" in answer.lower():
generatedb() generatedb()
elif "n" in answer.lower(): elif "n" in answer.lower():
print("Stopped") print("Stopped")
elif ":3" in answer: elif ":3" in answer:
print(":3") print(":3")
else: else:
print("Stopped") print("Stopped")

684
main
View File

@ -1,317 +1,367 @@
#!/usr/bin/python3 #!/usr/bin/python3
import os import os
import configparser import configparser
import sqlite3 import sqlite3
import json import time
import secrets import json
from werkzeug.utils import secure_filename import secrets
from werkzeug.security import generate_password_hash, check_password_hash from itertools import groupby
from flask import Flask, render_template, request, url_for, flash, redirect, session, make_response, send_from_directory, stream_with_context, Response, request from werkzeug.utils import secure_filename
from werkzeug.security import generate_password_hash, check_password_hash
# read config file from flask import Flask, render_template, request, url_for, flash, redirect, session, make_response, send_from_directory, stream_with_context, Response, request
config = configparser.ConfigParser()
config.read("config.ini") # read config file
config = configparser.ConfigParser()
HOST = config["config"]["HOST"] config.read("config.ini")
PORT = config["config"]["PORT"]
SECRET_KEY = config["config"]["SECRET_KEY"] HOST = config["config"]["HOST"]
UPLOAD_FOLDER = config["config"]["UPLOAD_FOLDER"] PORT = config["config"]["PORT"]
UPLOAD_LIMIT = config["config"]["UPLOAD_LIMIT"] SECRET_KEY = config["config"]["SECRET_KEY"]
PASSWORD_REQUIREMENT = config["config"]["PASSWORD_REQUIREMENT"] UPLOAD_FOLDER = config["config"]["UPLOAD_FOLDER"]
UPLOAD_LIMIT = config["config"]["UPLOAD_LIMIT"]
app = Flask(__name__) PASSWORD_REQUIREMENT = config["config"]["PASSWORD_REQUIREMENT"]
app.config["SECRET_KEY"] = SECRET_KEY
app.config["MAX_CONTENT_LENGTH"] = int(UPLOAD_LIMIT) * 1000 * 1000 app = Flask(__name__)
app.config["SECRET_KEY"] = SECRET_KEY
if SECRET_KEY == "placeholder": app.config["MAX_CONTENT_LENGTH"] = int(UPLOAD_LIMIT) * 1000 * 1000
print("[WARNING] Secret key is not set")
if SECRET_KEY == "placeholder":
if not os.path.exists(UPLOAD_FOLDER): print("[WARNING] Secret key is not set")
print("[WARNING] Upload folder doesn't exist, creating one")
os.mkdir(UPLOAD_FOLDER) if not os.path.exists(UPLOAD_FOLDER):
print("[WARNING] Upload folder doesn't exist, creating")
if not os.path.exists("database.db"): os.mkdir(UPLOAD_FOLDER)
print("[ERROR] No database exists, please run init_db")
exit() 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 makeStrSafe(url):
return str(urllib.parse.quote(url)).replace("%20", " ")
def get_db_connection():
conn = sqlite3.connect("database.db")
conn.row_factory = sqlite3.Row def get_db_connection():
return conn 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 = ?", def get_user(id):
(id,)).fetchone() conn = get_db_connection()
conn.close() post = conn.execute("SELECT * FROM users WHERE id = ?",
if post is None: (id,)).fetchone()
return "error" conn.close()
return post if post is None:
return "error"
return post
def get_post(id):
conn = get_db_connection()
post = conn.execute("SELECT * FROM posts WHERE id = ?", def get_comments(id):
(id,)).fetchone() conn = get_db_connection()
conn.close() post = conn.execute("SELECT * FROM comments WHERE post_id = ?",
if post is None: (id,)).fetchall()
return "error" conn.close()
return post if post is None:
return "error"
return post
app.jinja_env.globals.update(getUser=get_user)
app.jinja_env.globals.update(getComments=get_comments)
def check_username_taken(username):
conn = get_db_connection() def get_post(id):
post = conn.execute("SELECT * FROM users WHERE username = ?", conn = get_db_connection()
(username,)).fetchone() post = conn.execute("SELECT * FROM posts WHERE id = ?",
conn.close() (id,)).fetchone()
if post is None: conn.close()
return "error" if post is None:
return post["id"] return "error"
return post
def get_session(id):
conn = get_db_connection() app.jinja_env.globals.update(getUser=get_user)
post = conn.execute("SELECT * FROM sessions WHERE session = ?",
(id,)).fetchone()
conn.close() def check_username_taken(username):
if post is None: conn = get_db_connection()
return "error" post = conn.execute("SELECT * FROM users WHERE username = ?",
return post (username,)).fetchone()
conn.close()
if post is None:
ALLOWED_EXTENSIONS = {"png", "apng", "jpg", "jpeg", "gif", "svg", "webp"} return "error"
return post["id"]
def allowed_file(filename):
return '.' in filename and \ def get_session(id):
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS conn = get_db_connection()
post = conn.execute("SELECT * FROM sessions WHERE session = ?",
(id,)).fetchone()
@app.route("/", methods=("GET", "POST")) conn.close()
def main(): if post is None:
usersession = request.cookies.get("session_DO_NOT_SHARE") return "error"
conn = get_db_connection() return post
posts = conn.execute(
"SELECT * FROM posts ORDER BY created DESC;").fetchall()
conn.close() ALLOWED_EXTENSIONS = {"png", "apng", "jpg", "jpeg", "gif", "svg", "webp"}
if usersession:
userCookie = get_session(usersession) def allowed_file(filename):
user = get_user(userCookie["id"]) return '.' in filename and \
return render_template("main.html", userdata=user, posts=posts) filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
else:
return render_template("main.html", posts=posts)
@app.route("/", methods=("GET", "POST"))
def main():
@app.route("/post", methods=("GET", "POST")) usersession = request.cookies.get("session_DO_NOT_SHARE")
def post(): conn = get_db_connection()
usersession = request.cookies.get("session_DO_NOT_SHARE") posts = conn.execute("SELECT * FROM posts ORDER BY created DESC;").fetchall()
if usersession: conn.close()
if request.method == "POST":
title = request.form["title"] if usersession:
if title == "": userCookie = get_session(usersession)
flash("Text required :3") user = get_user(userCookie["id"])
return redirect(request.url) return render_template("main.html", userdata=user, posts=posts)
else:
if "file" not in request.files: return render_template("main.html", posts=posts)
flash("No file selected :3")
return redirect(request.url) @app.route("/post", methods=("GET", "POST"))
def post():
file = request.files["file"] usersession = request.cookies.get("session_DO_NOT_SHARE")
if file.filename == "": if usersession:
flash("No file selected :3") if request.method == "POST":
return redirect(request.url) title = request.form["title"]
if title == "":
if not allowed_file(file.filename): flash("Text required :3")
flash("File is not an image!") return redirect(url_for("post"))
return redirect(request.url)
if "file" not in request.files:
filename = secure_filename(file.filename) flash("No file selected :3")
finalfilename = secrets.token_hex(64) + filename return redirect(url_for("post"))
file.save(os.path.join(UPLOAD_FOLDER, finalfilename)) file = request.files["file"]
imgurl = "/cdn/" + finalfilename if file.filename == "":
flash("No file selected :3")
userCookie = get_session(usersession) return redirect(url_for("post"))
user = get_user(userCookie["id"])
if not allowed_file(file.filename):
if not user["banned"] == "0": flash("File is not an image!")
flash("Your account has been banned. Reason: " + return redirect(url_for("post"))
user["banned"])
return redirect(request.url) filename = secure_filename(file.filename)
finalfilename = secrets.token_hex(64) + filename
print(userCookie)
file.save(os.path.join(UPLOAD_FOLDER, finalfilename))
conn = get_db_connection() imgurl = "/cdn/" + finalfilename
conn.execute("INSERT INTO posts (textstr, imageurl, creator) VALUES (?, ?, ?)",
(title, imgurl, userCookie["id"])) userCookie = get_session(usersession)
conn.commit() user = get_user(userCookie["id"])
conn.close()
return redirect("/") if not user["banned"] == "0":
flash("Your account has been banned. Reason: " +
else: user["banned"])
userCookie = get_session(usersession) return redirect(url_for("post"))
user = get_user(userCookie["id"])
return render_template("post.html", userdata=user) conn = get_db_connection()
else: conn.execute("INSERT INTO posts (textstr, imageurl, creator, created) VALUES (?, ?, ?, ?)",
flash("A burgercat account is required to post :3") (title, imgurl, userCookie["id"], str(time.time())))
return redirect("/login") conn.commit()
conn.close()
return redirect(url_for("main"))
@app.route("/cdn/<filename>", methods=("GET", "POST"))
def cdn(filename): else:
if os.path.exists(os.path.join(UPLOAD_FOLDER, filename)): userCookie = get_session(usersession)
return send_from_directory(UPLOAD_FOLDER, filename) user = get_user(userCookie["id"])
else: return render_template("post.html", userdata=user)
return "file doesn't exist!!" else:
flash("A burgercat account is required to post :3")
return redirect(url_for("login"))
@app.route("/signup", methods=("GET", "POST"))
def signup():
usersession = request.cookies.get("session_DO_NOT_SHARE") @app.route("/comment", methods=("GET", "POST"))
if usersession: def comment():
return redirect("/") usersession = request.cookies.get("session_DO_NOT_SHARE")
if request.method == "POST": if usersession:
if not check_username_taken(request.form["username"]) == "error": if request.method == "POST":
flash("Username already taken :3")
return redirect(request.url) data = request.get_json()
uid = data["id"]
if not request.form["username"].isalnum(): title = data["title"]
flash("Username must be alphanumeric :3")
return redirect(request.url) userCookie = get_session(usersession)
user = get_user(userCookie["id"])
if not len(request.form["password"]) > int(PASSWORD_REQUIREMENT):
flash("Password must contain at least " + PASSWORD_REQUIREMENT + " characters") if not user["banned"] == "0":
return redirect(request.url) flash("Your account has been banned. Reason: " +
user["banned"])
hashedpassword = generate_password_hash(request.form["password"]) return redirect(url_for("comment"))
conn = get_db_connection() conn = get_db_connection()
conn.execute("INSERT INTO users (username, password) VALUES (?, ?)", conn.execute("INSERT INTO comments (textstr, post_id, creator, created) VALUES (?, ?, ?, ?)",
(request.form["username"], hashedpassword)) (title, uid, userCookie["id"], str(time.time())))
conn.commit() conn.commit()
conn.close() conn.close()
return redirect("/login") return "success"
else:
return render_template("signup.html") else:
return """<img src="https://http.cat/images/400.jpg">""", 400
else:
@app.route("/login", methods=("GET", "POST")) flash("A burgercat account is required to post :3")
def login(): return redirect(url_for("login"))
usersession = request.cookies.get("session_DO_NOT_SHARE")
if usersession:
return redirect("/") @app.route("/cdn/<filename>", methods=("GET", "POST"))
if request.method == "POST": def cdn(filename):
userID = check_username_taken(request.form["username"]) if os.path.exists(os.path.join(UPLOAD_FOLDER, filename)):
user = get_user(userID) return send_from_directory(UPLOAD_FOLDER, filename)
else:
if user == "error": return "file doesn't exist!!"
flash("Wrong username or password :3")
return redirect(request.url)
@app.route("/signup", methods=("GET", "POST"))
if not check_password_hash(user["password"], (request.form["password"])): def signup():
flash("Wrong username or password :3") usersession = request.cookies.get("session_DO_NOT_SHARE")
return redirect(request.url) if usersession:
return redirect(url_for("main"))
randomCharacters = secrets.token_hex(512) if request.method == "POST":
if not check_username_taken(request.form["username"]) == "error":
conn = get_db_connection() flash("Username already taken :3")
conn.execute("INSERT INTO sessions (session, id) VALUES (?, ?)", return redirect(url_for("signup"))
(randomCharacters, userID))
conn.commit() if not request.form["username"].isalnum():
conn.close() flash("Username must be alphanumeric :3")
return redirect(url_for("signup"))
resp = make_response(redirect("/"))
resp.set_cookie("session_DO_NOT_SHARE", randomCharacters) if not len(request.form["password"]) > int(PASSWORD_REQUIREMENT):
flash("Password must contain at least " + PASSWORD_REQUIREMENT + " characters")
return resp return redirect(url_for("signup"))
else:
return render_template("login.html") hashedpassword = generate_password_hash(request.form["password"])
conn = get_db_connection()
@app.route("/settings", methods=("GET", "POST")) conn.execute("INSERT INTO users (username, password, created) VALUES (?, ?, ?)",
def settings(): (request.form["username"], hashedpassword, str(time.time())))
usersession = request.cookies.get("session_DO_NOT_SHARE") conn.commit()
if usersession: conn.close()
userCookie = get_session(usersession)
user = get_user(userCookie["id"]) return redirect(url_for("login"))
else:
return render_template("settings.html", userdata=user) return render_template("signup.html")
else:
return redirect("/")
@app.route("/login", methods=("GET", "POST"))
def login():
@app.route("/remove/<postid>", methods=("GET", "POST")) usersession = request.cookies.get("session_DO_NOT_SHARE")
def remove(postid): if usersession:
usersession = request.cookies.get("session_DO_NOT_SHARE") redirect(url_for("main"))
if usersession: if request.method == "POST":
userCookie = get_session(usersession) userID = check_username_taken(request.form["username"])
user = get_user(userCookie["id"]) user = get_user(userID)
if str(user["administrator"]) == "1":
post = get_post(postid) if user == "error":
conn = get_db_connection() flash("Wrong username or password :3")
conn.execute("DELETE FROM posts WHERE id = ?", (postid,)) return redirect(url_for("login"))
conn.commit()
conn.close() if not check_password_hash(user["password"], (request.form["password"])):
return "Deleted post!" flash("Wrong username or password :3")
else: return redirect(url_for("login"))
return "nice try"
else: randomCharacters = secrets.token_hex(512)
return redirect("/login")
conn = get_db_connection()
@app.route("/listusers", methods=("GET", "POST")) conn.execute("INSERT INTO sessions (session, id) VALUES (?, ?)",
def listusers(): (randomCharacters, userID))
usersession = request.cookies.get("session_DO_NOT_SHARE") conn.commit()
if usersession: conn.close()
userCookie = get_session(usersession)
user = get_user(userCookie["id"]) resp = make_response(redirect("/"))
if str(user["administrator"]) == "1": resp.set_cookie("session_DO_NOT_SHARE", randomCharacters)
thing = ""
return resp
conn = get_db_connection() else:
users = conn.execute("SELECT * FROM users").fetchall() return render_template("login.html")
conn.close()
for x in users:
thing = str(x["id"]) + " - " + x["username"] + "<br>" + thing @app.route("/settings", methods=("GET", "POST"))
def settings():
return thing usersession = request.cookies.get("session_DO_NOT_SHARE")
else: if usersession:
return "nice try" userCookie = get_session(usersession)
else: user = get_user(userCookie["id"])
return redirect("/login")
return render_template("settings.html", userdata=user)
else:
@app.route("/settings/logout", methods=("GET", "POST")) return redirect("/")
def logout():
resp = redirect("/")
session = request.cookies.get("session_DO_NOT_SHARE") @app.route("/remove/<postid>", methods=("GET", "POST"))
resp.delete_cookie("session_DO_NOT_SHARE") def remove(postid):
usersession = request.cookies.get("session_DO_NOT_SHARE")
return resp if usersession:
userCookie = get_session(usersession)
user = get_user(userCookie["id"])
@app.errorhandler(413) if str(user["administrator"]) == "1":
def page_not_found(e): post = get_post(postid)
return "the server decided to commit die" conn = get_db_connection()
conn.execute("DELETE FROM posts WHERE id = ?", (postid,))
conn.commit()
@app.errorhandler(413) conn.close()
def page_not_found(e): return "Deleted post!"
return "Images can't be larger than 4MB" else:
return "nice try"
else:
if __name__ == "__main__": return redirect(url_for("login"))
from waitress import serve
print("[INFO] Server started") @app.route("/listusers", methods=("GET", "POST"))
serve(app, host=HOST, port=PORT) def listusers():
print("[INFO] Server stopped") usersession = request.cookies.get("session_DO_NOT_SHARE")
if usersession:
userCookie = get_session(usersession)
user = get_user(userCookie["id"])
if str(user["administrator"]) == "1":
thing = ""
conn = get_db_connection()
users = conn.execute("SELECT * FROM users").fetchall()
conn.close()
for x in users:
thing = str(x["id"]) + " - " + x["username"] + "<br>" + thing
return thing
else:
return """<img src="https://http.cat/images/403.jpg">"""
else:
return redirect(url_for("login"))
@app.route("/settings/logout", methods=("GET", "POST"))
def logout():
resp = redirect(url_for("main"))
session = request.cookies.get("session_DO_NOT_SHARE")
resp.delete_cookie("session_DO_NOT_SHARE")
return resp
@app.errorhandler(500)
def page_not_found(e):
return """<img src="https://http.cat/images/500.jpg">""", 500
@app.errorhandler(400)
def page_not_found(e):
return """<img src="https://http.cat/images/400.jpg">""", 400
@app.errorhandler(404)
def page_not_found(e):
return """<img src="https://http.cat/images/404.jpg">""", 404
@app.errorhandler(413)
def page_not_found(e):
return "Images can't be larger than 4MB", 413
if __name__ == "__main__":
from waitress import serve
print("[INFO] Server started")
serve(app, host=HOST, port=PORT)
#app.run(host=HOST, port=PORT, debug=True)
print("[INFO] Server stopped")

View File

@ -1,7 +0,0 @@
burger social media
self hosting:
- git clone https://codeberg.org/burger-software/burgercat
- cd burgercat
- python init_db
- python main

View File

@ -1,2 +1,3 @@
flask flask
werkzeug werkzeug
waitress

View File

@ -1,25 +1,34 @@
DROP TABLE IF EXISTS users; DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS posts; DROP TABLE IF EXISTS posts;
DROP TABLE IF EXISTS sessions; DROP TABLE IF EXISTS comments;
DROP TABLE IF EXISTS sessions;
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT, CREATE TABLE users (
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL, created TEXT NOT NULL,
password TEXT NOT NULL, username TEXT NOT NULL,
banned TEXT NOT NULL DEFAULT 0, password TEXT NOT NULL,
administrator INTEGER NOT NULL DEFAULT 0 banned TEXT NOT NULL DEFAULT 0,
); administrator INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE posts (
id INTEGER PRIMARY KEY AUTOINCREMENT, CREATE TABLE posts (
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, id INTEGER PRIMARY KEY AUTOINCREMENT,
creator TEXT NOT NULL, created TEXT NOT NULL,
imageurl TEXT NOT NULL, creator TEXT NOT NULL,
textstr TEXT NOT NULL imageurl TEXT NOT NULL,
); textstr TEXT NOT NULL
);
CREATE TABLE sessions (
session TEXT PRIMARY KEY NOT NULL, CREATE TABLE comments (
id INTEGER NOT NULL id INTEGER PRIMARY KEY AUTOINCREMENT,
post_id INTEGER NOT NULL,
created TEXT NOT NULL,
creator TEXT NOT NULL,
textstr TEXT NOT NULL
);
CREATE TABLE sessions (
session TEXT PRIMARY KEY NOT NULL,
id INTEGER NOT NULL
); );

View File

@ -1,94 +1,136 @@
@import url("https://fonts.googleapis.com/css2?family=Inter&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Inter&display=swap");
body { body {
margin: 0; margin: 0;
font-family: "Inter", sans-serif; font-family: "Inter", sans-serif;
} }
.navbar { .navbar {
margin: 0; margin: 0;
padding: 10px; padding: 10px;
border: solid; border: solid;
border-color: grey; border-color: grey;
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;
} }
.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: 2px;
} }
.postDiv {
.accountform { padding-top: 120px;
margin-left: 20%; }
margin-right: 20%;
} .accountform {
.accountform input { margin-left: 20%;
padding: 7px; margin-right: 20%;
border: solid; }
border-width: 1px; .accountform input {
border-color: grey; padding: 7px;
width: calc(100% - 15px); border: solid;
font-size: 16px; border-width: 1px;
border-radius: 8px; border-color: grey;
} width: calc(100% - 15px);
.accountform .flash { font-size: 16px;
padding: 7px; border-radius: 8px;
border: solid; }
border-width: 1px; .accountform .flash {
border-color: #f1b739; padding: 7px;
width: calc(100% - 15px); border: solid;
font-size: 16px; border-width: 1px;
border-radius: 8px; border-color: #f1b739;
} width: calc(100% - 15px);
.accountform button { font-size: 16px;
padding: 7px; border-radius: 8px;
border: solid; }
border-width: 1px; .accountform button {
color: black; padding: 7px;
border-color: grey; border: solid;
background-color: white; border-width: 1px;
font-size: 16px; color: black;
width: 100%; border-color: grey;
border-radius: 8px; background-color: white;
} font-size: 16px;
width: 100%;
.navbar a { border-radius: 8px;
color: black; }
text-decoration: none;
} .navbar a {
color: black;
.navbar .right { text-decoration: none;
float: right; }
padding-right: 5px;
} .navbar .right {
float: right;
.navbar .r { padding-right: 5px;
padding-right: 15px; }
}
.navbar .r {
.post { padding-right: 15px;
margin: 10px; }
padding: 5px;
margin-bottom: 10px; .post {
border-radius: 10px; margin: 10px;
background-color: rgb(250, 250, 250); padding: 5px;
} margin-bottom: 10px;
border-radius: 10px;
.post img { background-color: rgb(250, 250, 250);
min-height: 200px; }
max-height: 300px;
border-radius: 10px; .post img {
} min-height: 200px;
max-height: 300px;
.post .username { border-radius: 10px;
font-size: 18px; }
}
.post button {
.post .date { background-color: rgb(250, 250, 250);
color: rgb(175, 175, 175); border: solid;
border-color: rgb(197, 197, 197);
border-width: 1px;
border-radius: 6px;
font-size: 16px;
font-family: "Inter", sans-serif;
}
.post button:hover {
border-color: #f1b739;
}
.post .commentdiv input {
background-color: rgb(250, 250, 250);
border: solid;
border-color: rgb(197, 197, 197);
border-width: 1px;
border-radius: 6px;
font-size: 16px;
margin-top: 5px;
font-family: "Inter", sans-serif;
}
.post hr {
color: rgb(255, 255, 255);
background-color: rgb(255, 255, 255);
border-color: rgb(255, 255, 255);
}
.post .commentsdiv {
background-color: #f5f5f5;
padding: 5px;
border-radius: 10px;
margin-bottom: 5px;
}
.post .username {
font-size: 18px;
}
.post .date {
color: rgb(175, 175, 175);
}
.hidden {
display: none;
} }

38
static/js/main.js Normal file
View File

@ -0,0 +1,38 @@
const posts = document.getElementsByClassName("post")
for (let i = 0; i < posts.length; i++) {
let post = posts[i]
let commentButton = post.children["commentButton"]
let commentId = post.children["commentId"]
let commentDiv = post.children["commentDiv"]
let commentBox = commentDiv.children["commentBox"]
let commentSave = commentDiv.children["commentDivSave"]
let commentCancel = commentDiv.children["commentDivCancel"]
commentButton.addEventListener("click", (e) => {
commentDiv.classList.remove("hidden")
commentBox.value = ""
});
commentCancel.addEventListener("click", (e) => {
commentDiv.classList.add("hidden")
});
commentSave.addEventListener("click", (e) => {
console.log(commentId.innerHTML)
title = String(commentBox.value)
id = String(commentId.innerHTML)
fetch("/comment", {
method: "POST",
body: JSON.stringify({
id: id,
title: title,
}),
headers: {
"Content-Type": "application/json"
}
})
.then((response) => response.json())
.then((json) => console.log(json));
commentDiv.classList.add("hidden")
});
}

View File

@ -1,35 +1,35 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>burgercat</title> <title>burgercat</title>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" type="text/css" href="/static/css/style.css" /> <link rel="stylesheet" type="text/css" href="/static/css/style.css" />
</head> </head>
<body> <body>
<form class="accountform" method="post"> <form class="accountform" method="post">
<br> <br>
<a href="/">back</a> <a href="/">back</a>
<br><br> <br><br>
<h1>Log in to burgercat</h1> <h1>Log in to burgercat</h1>
{% with messages = get_flashed_messages() %} {% with messages = get_flashed_messages() %}
{% if messages %} {% if messages %}
{% for message in messages %} {% for message in messages %}
<p class="flash">{{ message }}</p> <p class="flash">{{ message }}</p>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% endwith %} {% endwith %}
<input name="username" type="text" placeholder="Username" required> <input name="username" type="text" placeholder="Username" required>
<br><br> <br><br>
<input name="password" type="password" placeholder="Password" required> <input name="password" type="password" placeholder="Password" required>
<br> <br>
<br> <br>
<button type="submit">Log in</button> <button type="submit">Log in</button>
<br><br> <br><br>
Don't have an account? <a href="/signup">Sign up!</a> Don't have an account? <a href="/signup">Sign up!</a>
</form> </form>
</body> </body>
</html> </html>

View File

@ -1,45 +1,98 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>burgercat</title> <title>burgercat</title>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" type="text/css" href="/static/css/style.css" /> <link rel="stylesheet" type="text/css" href="/static/css/style.css" />
</head> </head>
<body> <body>
<div class="navbar"> <div class="navbar">
<h1>burgercat</h1> <h1>burgercat</h1>
<a class="selected" href="/">home</a> <a class="selected" href="/">home</a>
<a href="/post">post</a> <a href="/post">post</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>
{% else %} {% else %}
<a href="/signup" class="right r">sign up</a> <a href="/signup" class="right r">sign up</a>
<a href="/login" class="right">log in</a> <a href="/login" class="right">log in</a>
{% endif %} {% endif %}
</div> </div>
<br><br><br><br><br><br> <script>
let timeStampElement
{% for post in posts %} let unixTime
<div class="post"> </script>
<p class="username">{{ getUser(post["creator"])["username"] }}</p>
<p class="date">{{ post["created"] }}</p> <div class="postDiv">
{% if userdata %} {% for post in posts %}
{% if userdata.administrator == 1 %} <div class="post" id="post">
<a class="date" href='/remove/{{post["id"]}}'>Remove post</a> <p class="username">{{ getUser(post["creator"])["username"] }}</p>
<br><br> <p class="date" id='timestamp_{{post["id"]}}'> </p>
{% endif %} {% if userdata %}
{% endif %} {% if userdata.administrator == 1 %}
<img loading="lazy" src='{{ post["imageurl"] }}'> <a class="date" href='/remove/{{post["id"]}}'>Remove post</a>
<p class="text">{{ post["textstr"] }}</p> <br><br>
</div> {% endif %}
{% endfor %} {% endif %}
<img loading="lazy" src='{{ post["imageurl"] }}'>
</body> <p class="text">{{ post["textstr"] }}</p>
<div class="commentsdiv">
{% for comment in getComments(post["id"]) %}
<p>{{ getUser(comment["creator"])["username"] }}: {{ comment.textstr }}</p>
{% endfor %}
</div>
<p id="commentId" class="hidden">{{ post.id }}</p>
<button id="commentButton" class="comment">comment</button>
<div id="commentDiv" class="commentdiv hidden">
<input id="commentBox" type="text" placeholder="content">
<button id="commentDivSave">save</button>
<button id="commentDivCancel">cancel</button>
</div>
<script>
function time2TimeAgo(ts) {
var d = new Date();
var nowTs = Math.floor(d.getTime() / 1000);
var seconds = nowTs - ts;
var interval = seconds / 31536000;
if (interval > 1) {
return Math.floor(interval) + " years ago";
}
interval = seconds / 2592000;
if (interval > 1) {
return Math.floor(interval) + " months ago";
}
interval = seconds / 86400;
if (interval > 1) {
return Math.floor(interval) + " days ago";
}
interval = seconds / 3600;
if (interval > 1) {
return Math.floor(interval) + " hours ago";
}
interval = seconds / 60;
if (interval > 1) {
return Math.floor(interval) + " minutes ago";
}
return Math.floor(seconds) + " seconds";
}
timeStampElement = document.getElementById('timestamp_{{post["id"]}}')
unixTime = '{{post["created"]}}'
timeStampElement.innerHTML = time2TimeAgo(unixTime)
</script>
</div>
{% endfor %}
</div>
<script src="/static/js/main.js"></script>
</body>
</html> </html>

View File

@ -1,65 +1,63 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>burgercat</title> <title>burgercat</title>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" type="text/css" href="/static/css/style.css" /> <link rel="stylesheet" type="text/css" href="/static/css/style.css" />
</head> </head>
<body> <body>
<div class="navbar"> <div class="navbar">
<h1>burgercat</h1> <h1>burgercat</h1>
<a href="/">home</a> <a href="/">home</a>
<a class="selected" href="/post">post</a> <a class="selected" href="/post">post</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>
{% else %} {% else %}
<a href="/signup" class="right r">sign up</a> <a href="/signup" class="right r">sign up</a>
<a href="/login" class="right">log in</a> <a href="/login" class="right">log in</a>
{% endif %} {% endif %}
</div> </div>
<br><br><br><br><br><br> <br><br><br><br><br><br>
<div class="post"> <div class="post">
{% with messages = get_flashed_messages() %} {% with messages = get_flashed_messages() %}
{% if messages %} {% if messages %}
{% for message in messages %} {% for message in messages %}
<p class="flash">{{ message }}</p> <p class="flash">{{ message }}</p>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% endwith %} {% endwith %}
<form method="post" enctype="multipart/form-data"> <form method="post" enctype="multipart/form-data">
<h2>Post</h2> <h2>Post</h2>
<p>Image</p> <p>Image</p>
<input type="file" name="file" required> <input type="file" name="file" required>
<br> <br>
<p>Text</p> <p>Text</p>
<input name="title" type="text" placeholder="Text" required> <input name="title" type="text" placeholder="Text" 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>Rules</h2>
<ul> <ul>
<li>Please do not spam</li> <li>Please do not spam</li>
<li>Treat everyone with respect, respect each others opinions</li> <li>Treat everyone with respect, respect each 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, doing so will get you an instant ban</li>
<li>Discussions regarding politics and related controversial topics are disallowed</li> <li>Discussions regarding politics and related controversial topics are disallowed</li>
<li>Advertising is not allowed</li> <li>Advertising is not allowed</li>
<li>Do not post links</li> <li>Do not post links</li>
<li>Do not create alt-accounts to evade bans</li> <li>Do not create alt-accounts to evade bans</li>
<br> </ul>
In general, just be a good person. </form>
</ul> </div>
</form>
</div> </body>
</body>
</html> </html>

View File

@ -1,39 +1,38 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>burgercat</title> <title>burgercat</title>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" type="text/css" href="/static/css/style.css" /> <link rel="stylesheet" type="text/css" href="/static/css/style.css" />
</head> </head>
<body> <body>
<div class="navbar"> <div class="navbar">
<h1>burgercat</h1> <h1>burgercat</h1>
<a href="/">home</a> <a href="/">home</a>
<a href="/post">post</a> <a href="/post">post</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 selected">{{ userdata.username }}</a> <a href="/settings" class="right selected">{{ userdata.username }}</a>
{% else %} {% else %}
<a href="/signup" class="right r">sign up</a> <a href="/signup" class="right r">sign up</a>
<a href="/login" class="right">log in</a> <a href="/login" class="right">log in</a>
{% endif %} {% endif %}
</div> </div>
<br><br><br><br><br><br> <br><br><br><br><br><br><br>
<div class="post"> <div class="post">
{% if userdata.administrator == 1 %} {% if userdata.administrator == 1 %}
Administrator<br><br> Administrator<br><br>
{% endif %} {% endif %}
Logged in as {{ userdata.username }}<br> Logged in as {{ userdata.username }}
Your user ID is {{ userdata.id }} <br><br>
<br><br> <a href="/settings/logout">Log out</a>
<a href="/settings/logout">Log out</a> </div>
</div>
</body>
</body>
</html> </html>

View File

@ -1,35 +1,35 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>burgercat</title> <title>burgercat</title>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" type="text/css" href="/static/css/style.css" /> <link rel="stylesheet" type="text/css" href="/static/css/style.css" />
</head> </head>
<body> <body>
<form class="accountform" method="post"> <form class="accountform" method="post">
<br> <br>
<a href="/">back</a> <a href="/">back</a>
<br><br> <br><br>
<h1>Create a burgercat account</h1> <h1>Create a burgercat account</h1>
<p>Create a burgercat acccount to create posts and more!</p> <p>Create a burgercat acccount to create posts and more!</p>
{% with messages = get_flashed_messages() %} {% with messages = get_flashed_messages() %}
{% if messages %} {% if messages %}
{% for message in messages %} {% for message in messages %}
<p class="flash">{{ message }}</p> <p class="flash">{{ message }}</p>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% endwith %} {% endwith %}
<input name="username" type="text" placeholder="Username" required> <input name="username" type="text" placeholder="Username" required>
<br><br> <br><br>
<input name="password" type="password" placeholder="Password" required> <input name="password" type="password" placeholder="Password" required>
<br><br> <br><br>
<button type="submit">Sign up</button> <button type="submit">Sign up</button>
<br><br> <br><br>
Already have an account? <a href="/login">Log in!</a> Already have an account? <a href="/login">Log in!</a>
</form> </form>
</body> </body>
</html> </html>