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

120
main
View File

@ -2,8 +2,10 @@
import os import os
import configparser import configparser
import sqlite3 import sqlite3
import time
import json import json
import secrets import secrets
from itertools import groupby
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 flask import Flask, render_template, request, url_for, flash, redirect, session, make_response, send_from_directory, stream_with_context, Response, request from flask import Flask, render_template, request, url_for, flash, redirect, session, make_response, send_from_directory, stream_with_context, Response, request
@ -27,7 +29,7 @@ if SECRET_KEY == "placeholder":
print("[WARNING] Secret key is not set") print("[WARNING] Secret key is not set")
if not os.path.exists(UPLOAD_FOLDER): if not os.path.exists(UPLOAD_FOLDER):
print("[WARNING] Upload folder doesn't exist, creating one") print("[WARNING] Upload folder doesn't exist, creating")
os.mkdir(UPLOAD_FOLDER) os.mkdir(UPLOAD_FOLDER)
if not os.path.exists("database.db"): if not os.path.exists("database.db"):
@ -55,6 +57,18 @@ def get_user(id):
return post 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
app.jinja_env.globals.update(getComments=get_comments)
def get_post(id): def get_post(id):
conn = get_db_connection() conn = get_db_connection()
post = conn.execute("SELECT * FROM posts WHERE id = ?", post = conn.execute("SELECT * FROM posts WHERE id = ?",
@ -100,8 +114,7 @@ def allowed_file(filename):
def main(): 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( posts = conn.execute("SELECT * FROM posts ORDER BY created DESC;").fetchall()
"SELECT * FROM posts ORDER BY created DESC;").fetchall()
conn.close() conn.close()
if usersession: if usersession:
@ -111,7 +124,6 @@ def main():
else: else:
return render_template("main.html", posts=posts) return render_template("main.html", posts=posts)
@app.route("/post", methods=("GET", "POST")) @app.route("/post", methods=("GET", "POST"))
def post(): def post():
usersession = request.cookies.get("session_DO_NOT_SHARE") usersession = request.cookies.get("session_DO_NOT_SHARE")
@ -120,20 +132,20 @@ def post():
title = request.form["title"] title = request.form["title"]
if title == "": if title == "":
flash("Text required :3") flash("Text required :3")
return redirect(request.url) return redirect(url_for("post"))
if "file" not in request.files: if "file" not in request.files:
flash("No file selected :3") flash("No file selected :3")
return redirect(request.url) return redirect(url_for("post"))
file = request.files["file"] file = request.files["file"]
if file.filename == "": if file.filename == "":
flash("No file selected :3") flash("No file selected :3")
return redirect(request.url) return redirect(url_for("post"))
if not allowed_file(file.filename): if not allowed_file(file.filename):
flash("File is not an image!") flash("File is not an image!")
return redirect(request.url) return redirect(url_for("post"))
filename = secure_filename(file.filename) filename = secure_filename(file.filename)
finalfilename = secrets.token_hex(64) + filename finalfilename = secrets.token_hex(64) + filename
@ -147,16 +159,14 @@ def post():
if not user["banned"] == "0": if not user["banned"] == "0":
flash("Your account has been banned. Reason: " + flash("Your account has been banned. Reason: " +
user["banned"]) user["banned"])
return redirect(request.url) return redirect(url_for("post"))
print(userCookie)
conn = get_db_connection() conn = get_db_connection()
conn.execute("INSERT INTO posts (textstr, imageurl, creator) VALUES (?, ?, ?)", conn.execute("INSERT INTO posts (textstr, imageurl, creator, created) VALUES (?, ?, ?, ?)",
(title, imgurl, userCookie["id"])) (title, imgurl, userCookie["id"], str(time.time())))
conn.commit() conn.commit()
conn.close() conn.close()
return redirect("/") return redirect(url_for("main"))
else: else:
userCookie = get_session(usersession) userCookie = get_session(usersession)
@ -164,7 +174,40 @@ def post():
return render_template("post.html", userdata=user) return 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("/login") return redirect(url_for("login"))
@app.route("/comment", methods=("GET", "POST"))
def comment():
usersession = request.cookies.get("session_DO_NOT_SHARE")
if usersession:
if request.method == "POST":
data = request.get_json()
uid = data["id"]
title = data["title"]
userCookie = get_session(usersession)
user = get_user(userCookie["id"])
if not user["banned"] == "0":
flash("Your account has been banned. Reason: " +
user["banned"])
return redirect(url_for("comment"))
conn = get_db_connection()
conn.execute("INSERT INTO comments (textstr, post_id, creator, created) VALUES (?, ?, ?, ?)",
(title, uid, userCookie["id"], str(time.time())))
conn.commit()
conn.close()
return "success"
else:
return """<img src="https://http.cat/images/400.jpg">""", 400
else:
flash("A burgercat account is required to post :3")
return redirect(url_for("login"))
@app.route("/cdn/<filename>", methods=("GET", "POST")) @app.route("/cdn/<filename>", methods=("GET", "POST"))
@ -179,29 +222,29 @@ def cdn(filename):
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("/") return redirect(url_for("main"))
if request.method == "POST": if request.method == "POST":
if not check_username_taken(request.form["username"]) == "error": if not check_username_taken(request.form["username"]) == "error":
flash("Username already taken :3") flash("Username already taken :3")
return redirect(request.url) return redirect(url_for("signup"))
if not request.form["username"].isalnum(): if not request.form["username"].isalnum():
flash("Username must be alphanumeric :3") flash("Username must be alphanumeric :3")
return redirect(request.url) return redirect(url_for("signup"))
if not len(request.form["password"]) > int(PASSWORD_REQUIREMENT): if not len(request.form["password"]) > int(PASSWORD_REQUIREMENT):
flash("Password must contain at least " + PASSWORD_REQUIREMENT + " characters") flash("Password must contain at least " + PASSWORD_REQUIREMENT + " characters")
return redirect(request.url) return redirect(url_for("signup"))
hashedpassword = generate_password_hash(request.form["password"]) hashedpassword = generate_password_hash(request.form["password"])
conn = get_db_connection() conn = get_db_connection()
conn.execute("INSERT INTO users (username, password) VALUES (?, ?)", conn.execute("INSERT INTO users (username, password, created) VALUES (?, ?, ?)",
(request.form["username"], hashedpassword)) (request.form["username"], hashedpassword, str(time.time())))
conn.commit() conn.commit()
conn.close() conn.close()
return redirect("/login") return redirect(url_for("login"))
else: else:
return render_template("signup.html") return render_template("signup.html")
@ -210,18 +253,18 @@ def signup():
def login(): def login():
usersession = request.cookies.get("session_DO_NOT_SHARE") usersession = request.cookies.get("session_DO_NOT_SHARE")
if usersession: if usersession:
return redirect("/") redirect(url_for("main"))
if request.method == "POST": if request.method == "POST":
userID = check_username_taken(request.form["username"]) userID = check_username_taken(request.form["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(request.url) return redirect(url_for("login"))
if not check_password_hash(user["password"], (request.form["password"])): if not check_password_hash(user["password"], (request.form["password"])):
flash("Wrong username or password :3") flash("Wrong username or password :3")
return redirect(request.url) return redirect(url_for("login"))
randomCharacters = secrets.token_hex(512) randomCharacters = secrets.token_hex(512)
@ -267,7 +310,7 @@ def remove(postid):
else: else:
return "nice try" return "nice try"
else: else:
return redirect("/login") return redirect(url_for("login"))
@app.route("/listusers", methods=("GET", "POST")) @app.route("/listusers", methods=("GET", "POST"))
def listusers(): def listusers():
@ -286,32 +329,39 @@ def listusers():
return thing return thing
else: else:
return "nice try" return """<img src="https://http.cat/images/403.jpg">"""
else: else:
return redirect("/login") return redirect(url_for("login"))
@app.route("/settings/logout", methods=("GET", "POST")) @app.route("/settings/logout", methods=("GET", "POST"))
def logout(): def logout():
resp = redirect("/") 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")
return resp 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) @app.errorhandler(413)
def page_not_found(e): def page_not_found(e):
return "the server decided to commit die" return "Images can't be larger than 4MB", 413
@app.errorhandler(413)
def page_not_found(e):
return "Images can't be larger than 4MB"
if __name__ == "__main__": if __name__ == "__main__":
from waitress import serve from waitress import serve
print("[INFO] Server started") print("[INFO] Server started")
serve(app, host=HOST, port=PORT) serve(app, host=HOST, port=PORT)
#app.run(host=HOST, port=PORT, debug=True)
print("[INFO] Server stopped") 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,10 +1,11 @@
DROP TABLE IF EXISTS users; DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS posts; DROP TABLE IF EXISTS posts;
DROP TABLE IF EXISTS comments;
DROP TABLE IF EXISTS sessions; DROP TABLE IF EXISTS sessions;
CREATE TABLE users ( CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, created TEXT NOT NULL,
username TEXT NOT NULL, username TEXT NOT NULL,
password TEXT NOT NULL, password TEXT NOT NULL,
banned TEXT NOT NULL DEFAULT 0, banned TEXT NOT NULL DEFAULT 0,
@ -13,12 +14,20 @@ CREATE TABLE users (
CREATE TABLE posts ( CREATE TABLE posts (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, created TEXT NOT NULL,
creator TEXT NOT NULL, creator TEXT NOT NULL,
imageurl TEXT NOT NULL, imageurl TEXT NOT NULL,
textstr TEXT NOT NULL textstr TEXT NOT NULL
); );
CREATE TABLE comments (
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 ( CREATE TABLE sessions (
session TEXT PRIMARY KEY NOT NULL, session TEXT PRIMARY KEY NOT NULL,
id INTEGER NOT NULL id INTEGER NOT NULL

View File

@ -22,6 +22,9 @@ body {
border-width: 0; border-width: 0;
border-bottom-width: 2px; border-bottom-width: 2px;
} }
.postDiv {
padding-top: 120px;
}
.accountform { .accountform {
margin-left: 20%; margin-left: 20%;
@ -85,6 +88,41 @@ body {
border-radius: 10px; border-radius: 10px;
} }
.post button {
background-color: rgb(250, 250, 250);
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 { .post .username {
font-size: 18px; font-size: 18px;
} }
@ -92,3 +130,7 @@ body {
.post .date { .post .date {
color: rgb(175, 175, 175); 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

@ -23,12 +23,16 @@
{% endif %} {% endif %}
</div> </div>
<br><br><br><br><br><br> <script>
let timeStampElement
let unixTime
</script>
<div class="postDiv">
{% for post in posts %} {% for post in posts %}
<div class="post"> <div class="post" id="post">
<p class="username">{{ getUser(post["creator"])["username"] }}</p> <p class="username">{{ getUser(post["creator"])["username"] }}</p>
<p class="date">{{ post["created"] }}</p> <p class="date" id='timestamp_{{post["id"]}}'> </p>
{% if userdata %} {% if userdata %}
{% if userdata.administrator == 1 %} {% if userdata.administrator == 1 %}
<a class="date" href='/remove/{{post["id"]}}'>Remove post</a> <a class="date" href='/remove/{{post["id"]}}'>Remove post</a>
@ -37,9 +41,58 @@
{% endif %} {% endif %}
<img loading="lazy" src='{{ post["imageurl"] }}'> <img loading="lazy" src='{{ post["imageurl"] }}'>
<p class="text">{{ post["textstr"] }}</p> <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> </div>
{% endfor %} {% endfor %}
</div>
<script src="/static/js/main.js"></script>
</body> </body>
</html> </html>

View File

@ -54,8 +54,6 @@
<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>
In general, just be a good person.
</ul> </ul>
</form> </form>
</div> </div>

View File

@ -23,13 +23,12 @@
{% 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>