2023-07-08 17:00:32 +01:00
|
|
|
#!/usr/bin/python3
|
|
|
|
import os
|
2024-05-14 00:46:42 +01:00
|
|
|
import requests
|
2023-07-08 17:00:32 +01:00
|
|
|
import configparser
|
|
|
|
import sqlite3
|
|
|
|
import time
|
|
|
|
import json
|
|
|
|
import secrets
|
2023-07-08 20:19:07 +01:00
|
|
|
import datetime
|
2023-07-10 20:28:15 +01:00
|
|
|
import socket
|
2023-07-14 01:29:58 +01:00
|
|
|
import threading
|
2023-07-13 01:28:28 +01:00
|
|
|
import subprocess
|
2024-04-29 21:36:42 +01:00
|
|
|
import asyncio
|
|
|
|
from hypercorn.config import Config
|
|
|
|
from hypercorn.asyncio import serve
|
2023-07-08 17:00:32 +01:00
|
|
|
from itertools import groupby
|
|
|
|
from werkzeug.utils import secure_filename
|
|
|
|
from werkzeug.security import generate_password_hash, check_password_hash
|
2024-04-29 21:36:42 +01:00
|
|
|
from quart import Quart, render_template, request, url_for, flash, redirect, session, make_response, send_from_directory, stream_with_context, Response, request, jsonify, websocket
|
2023-07-14 01:29:58 +01:00
|
|
|
from apscheduler.schedulers.background import BackgroundScheduler
|
2023-07-08 17:00:32 +01:00
|
|
|
|
|
|
|
# read config file
|
|
|
|
config = configparser.ConfigParser()
|
|
|
|
config.read("config.ini")
|
|
|
|
|
|
|
|
PORT = config["config"]["PORT"]
|
|
|
|
SECRET_KEY = config["config"]["SECRET_KEY"]
|
|
|
|
UPLOAD_FOLDER = config["config"]["UPLOAD_FOLDER"]
|
|
|
|
UPLOAD_LIMIT = config["config"]["UPLOAD_LIMIT"]
|
|
|
|
PASSWORD_REQUIREMENT = config["config"]["PASSWORD_REQUIREMENT"]
|
|
|
|
|
2024-04-29 21:36:42 +01:00
|
|
|
app = Quart(__name__)
|
2023-07-08 17:00:32 +01:00
|
|
|
app.config["SECRET_KEY"] = SECRET_KEY
|
|
|
|
app.config["MAX_CONTENT_LENGTH"] = int(UPLOAD_LIMIT) * 1000 * 1000
|
2023-07-09 21:00:15 +01:00
|
|
|
|
2023-07-08 17:00:32 +01:00
|
|
|
if SECRET_KEY == "placeholder":
|
|
|
|
print("[WARNING] Secret key is not set")
|
|
|
|
|
|
|
|
if not os.path.exists(UPLOAD_FOLDER):
|
|
|
|
print("[WARNING] Upload folder doesn't exist, creating")
|
|
|
|
os.mkdir(UPLOAD_FOLDER)
|
|
|
|
|
|
|
|
if not os.path.exists("database.db"):
|
|
|
|
print("[ERROR] No database exists, please run init_db")
|
|
|
|
exit()
|
|
|
|
|
|
|
|
|
|
|
|
def makeStrSafe(url):
|
|
|
|
return str(urllib.parse.quote(url)).replace("%20", " ")
|
|
|
|
|
|
|
|
|
|
|
|
def get_db_connection():
|
|
|
|
conn = sqlite3.connect("database.db")
|
|
|
|
conn.row_factory = sqlite3.Row
|
|
|
|
return conn
|
|
|
|
|
|
|
|
|
|
|
|
def get_user(id):
|
|
|
|
conn = get_db_connection()
|
|
|
|
post = conn.execute("SELECT * FROM users WHERE id = ?",
|
|
|
|
(id,)).fetchone()
|
|
|
|
conn.close()
|
|
|
|
if post is None:
|
|
|
|
return "error"
|
|
|
|
return post
|
|
|
|
|
|
|
|
|
|
|
|
def get_comments(id):
|
|
|
|
conn = get_db_connection()
|
|
|
|
post = conn.execute("SELECT * FROM comments WHERE post_id = ?",
|
|
|
|
(id,)).fetchall()
|
|
|
|
conn.close()
|
|
|
|
if post is None:
|
|
|
|
return "error"
|
|
|
|
return post
|
|
|
|
|
|
|
|
|
2023-07-12 01:24:51 +01:00
|
|
|
def get_messages(chatroomid, max):
|
2023-07-11 20:41:57 +01:00
|
|
|
conn = get_db_connection()
|
|
|
|
post = conn.execute("SELECT * FROM chatmessages WHERE chatroom_id = ? ORDER BY created DESC;",
|
2023-07-12 01:24:51 +01:00
|
|
|
(chatroomid,)).fetchmany(max + 1)
|
2023-07-11 20:41:57 +01:00
|
|
|
conn.close()
|
|
|
|
if post is None:
|
|
|
|
return "error"
|
|
|
|
return post
|
|
|
|
|
|
|
|
|
2023-07-08 17:00:32 +01:00
|
|
|
app.jinja_env.globals.update(getComments=get_comments)
|
|
|
|
|
|
|
|
def get_post(id):
|
|
|
|
conn = get_db_connection()
|
|
|
|
post = conn.execute("SELECT * FROM posts WHERE id = ?",
|
|
|
|
(id,)).fetchone()
|
|
|
|
conn.close()
|
|
|
|
if post is None:
|
|
|
|
return "error"
|
|
|
|
return post
|
|
|
|
|
|
|
|
|
|
|
|
app.jinja_env.globals.update(getUser=get_user)
|
|
|
|
|
|
|
|
|
|
|
|
def check_username_taken(username):
|
|
|
|
conn = get_db_connection()
|
2023-07-09 21:00:15 +01:00
|
|
|
post = conn.execute("SELECT * FROM users WHERE lower(username) = ?",
|
|
|
|
(username.lower(),)).fetchone()
|
2023-07-08 17:00:32 +01:00
|
|
|
conn.close()
|
|
|
|
if post is None:
|
|
|
|
return "error"
|
|
|
|
return post["id"]
|
|
|
|
|
2024-05-15 19:45:36 +01:00
|
|
|
def check_sub_taken(sub):
|
|
|
|
conn = get_db_connection()
|
|
|
|
post = conn.execute("SELECT * FROM users WHERE password = ?",
|
|
|
|
(str("OAUTH-" + sub),)).fetchone()
|
|
|
|
conn.close()
|
|
|
|
if post is None:
|
|
|
|
return "error"
|
|
|
|
return post["id"]
|
2023-07-08 17:00:32 +01:00
|
|
|
|
|
|
|
def get_session(id):
|
|
|
|
conn = get_db_connection()
|
|
|
|
post = conn.execute("SELECT * FROM sessions WHERE session = ?",
|
|
|
|
(id,)).fetchone()
|
|
|
|
conn.close()
|
|
|
|
if post is None:
|
|
|
|
return "error"
|
|
|
|
return post
|
|
|
|
|
2023-07-13 04:05:33 +01:00
|
|
|
full_hash = subprocess.check_output(["git", "rev-parse", "HEAD"]).decode().strip()
|
|
|
|
short_hash = subprocess.check_output(["git", "rev-parse", "--short=8", "HEAD"]).decode().strip()
|
2023-07-08 17:00:32 +01:00
|
|
|
|
2024-04-29 21:36:42 +01:00
|
|
|
ALLOWED_EXTENSIONS = {"png", "apng", "jpg", "jpeg", "gif", "svg", "webp", "jxl"}
|
2023-07-08 17:00:32 +01:00
|
|
|
|
|
|
|
def allowed_file(filename):
|
|
|
|
return '.' in filename and \
|
|
|
|
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
|
|
|
|
|
|
|
@app.route("/", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def main():
|
2023-07-08 17:00:32 +01:00
|
|
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
|
|
|
conn = get_db_connection()
|
|
|
|
posts = conn.execute("SELECT * FROM posts ORDER BY created DESC;").fetchall()
|
|
|
|
conn.close()
|
|
|
|
|
|
|
|
if usersession:
|
|
|
|
userCookie = get_session(usersession)
|
|
|
|
user = get_user(userCookie["id"])
|
2024-04-29 21:36:42 +01:00
|
|
|
return await render_template("main.html", userdata=user, posts=posts, full_hash=full_hash, short_hash=short_hash)
|
2023-07-08 17:00:32 +01:00
|
|
|
else:
|
2024-04-29 21:36:42 +01:00
|
|
|
return await render_template("main.html", posts=posts, full_hash=full_hash, short_hash=short_hash)
|
2023-07-08 17:00:32 +01:00
|
|
|
|
2023-07-11 20:41:57 +01:00
|
|
|
@app.route("/chat", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def chat():
|
2023-07-11 20:41:57 +01:00
|
|
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
|
|
|
if usersession:
|
|
|
|
userCookie = get_session(usersession)
|
|
|
|
user = get_user(userCookie["id"])
|
2024-04-29 21:36:42 +01:00
|
|
|
return await render_template("chat.html", userdata=user)
|
2023-07-11 20:41:57 +01:00
|
|
|
else:
|
2024-04-29 21:36:42 +01:00
|
|
|
return await render_template("chat.html")
|
2023-07-11 20:41:57 +01:00
|
|
|
|
|
|
|
@app.route("/api/chat/listrooms")
|
2024-04-29 21:36:42 +01:00
|
|
|
async def chatlistrooms():
|
2023-07-11 20:41:57 +01:00
|
|
|
conn = get_db_connection()
|
2023-07-14 01:29:58 +01:00
|
|
|
rooms = conn.execute("SELECT * FROM chatrooms ORDER BY id ASC;").fetchall()
|
2023-07-11 20:41:57 +01:00
|
|
|
conn.close()
|
|
|
|
|
|
|
|
template = []
|
|
|
|
|
|
|
|
for room in rooms:
|
|
|
|
roomtemplate = {
|
|
|
|
"id": room["id"],
|
|
|
|
"name": room["roomname"]
|
|
|
|
}
|
|
|
|
template.append(roomtemplate)
|
|
|
|
|
|
|
|
return(template), 200
|
|
|
|
|
|
|
|
@app.route("/api/chat/getmessages/<roomid>")
|
2024-04-29 21:36:42 +01:00
|
|
|
async def chatget(roomid):
|
2023-07-14 01:29:58 +01:00
|
|
|
messages = get_messages(roomid, 150)
|
2023-07-11 20:41:57 +01:00
|
|
|
|
|
|
|
template = []
|
|
|
|
|
|
|
|
for message in messages:
|
|
|
|
creatorid = message["creator"]
|
|
|
|
|
|
|
|
creatortemplate = {
|
|
|
|
"id": message["creator"],
|
|
|
|
"username": get_user(creatorid)["username"]
|
|
|
|
}
|
|
|
|
|
|
|
|
messagetemplate = {
|
|
|
|
"id": message["id"],
|
|
|
|
"content": message["content"],
|
|
|
|
"creator": creatortemplate,
|
|
|
|
"created": message["created"]
|
|
|
|
}
|
|
|
|
template.append(messagetemplate)
|
|
|
|
|
|
|
|
return(template), 200
|
|
|
|
|
2024-04-29 21:36:42 +01:00
|
|
|
|
2023-07-11 20:41:57 +01:00
|
|
|
@app.route("/api/chat/send/<roomid>", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def chatsend(roomid):
|
2023-07-11 20:41:57 +01:00
|
|
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
|
|
|
if usersession:
|
|
|
|
if request.method == "POST":
|
|
|
|
|
2024-04-29 21:36:42 +01:00
|
|
|
data = await request.get_json()
|
2023-07-11 20:41:57 +01:00
|
|
|
content = data["content"]
|
|
|
|
|
|
|
|
userCookie = get_session(usersession)
|
|
|
|
user = get_user(userCookie["id"])
|
|
|
|
|
|
|
|
if not user["banned"] == "0":
|
|
|
|
return {
|
|
|
|
"error": "banned"
|
|
|
|
}, 403
|
|
|
|
|
2023-07-14 01:29:58 +01:00
|
|
|
chatMessageContent = {
|
|
|
|
"content": content,
|
|
|
|
"creator": user["username"],
|
|
|
|
"roomid": roomid,
|
|
|
|
"created": str(time.time())
|
|
|
|
}
|
|
|
|
|
2024-04-29 21:36:42 +01:00
|
|
|
#message_queue.append({"message": chatMessageContent})
|
2023-07-14 01:29:58 +01:00
|
|
|
|
2023-07-11 20:41:57 +01:00
|
|
|
conn = get_db_connection()
|
|
|
|
conn.execute("INSERT INTO chatmessages (content, chatroom_id, creator, created) VALUES (?, ?, ?, ?)",
|
|
|
|
(content, roomid, userCookie["id"], str(time.time())))
|
|
|
|
conn.commit()
|
|
|
|
conn.close()
|
|
|
|
|
|
|
|
return "success", 200
|
|
|
|
|
|
|
|
|
2023-07-09 21:00:15 +01:00
|
|
|
@app.route("/@<pageusername>", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def user(pageusername):
|
2023-07-09 21:00:15 +01:00
|
|
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
|
|
|
|
|
|
|
checkusername = check_username_taken(pageusername)
|
|
|
|
|
|
|
|
if not checkusername == "error":
|
|
|
|
pageuser = get_user(checkusername)
|
|
|
|
if usersession:
|
|
|
|
userCookie = get_session(usersession)
|
|
|
|
user = get_user(userCookie["id"])
|
2024-05-14 07:47:59 +01:00
|
|
|
return await render_template("user.html", userdata=user, createddate=datetime.datetime.fromtimestamp(int(str(pageuser["created"]).split(".")[0])).strftime("%Y-%m-%d"), pageuser=pageuser)
|
2023-07-09 21:00:15 +01:00
|
|
|
else:
|
2024-05-14 07:47:59 +01:00
|
|
|
return await render_template("user.html", createddate=datetime.datetime.fromtimestamp(int(str(pageuser["created"]).split(".")[0])).strftime("%Y-%m-%d"), pageuser=pageuser)
|
2023-07-09 21:00:15 +01:00
|
|
|
else:
|
|
|
|
return """<img src="https://http.cat/images/404.jpg">""", 404
|
|
|
|
|
2023-07-10 02:10:19 +01:00
|
|
|
@app.route("/api/page/<userid>", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def apipageuser(userid):
|
2023-07-09 22:27:28 +01:00
|
|
|
pageuser = get_user(userid)
|
2023-07-10 14:01:57 +01:00
|
|
|
addhtml = """
|
|
|
|
<!DOCTYPE html><script>
|
2023-07-10 14:46:23 +01:00
|
|
|
window.stop()
|
2023-07-10 14:01:57 +01:00
|
|
|
</script>
|
2024-04-29 22:54:55 +01:00
|
|
|
<base target="_blank"/> <head><meta http-equiv="Content-Security-Policy" default-src='none'; child-src='none'; content="img-src cdn.discordapp.com cdn.discordapp.net media.tenor.com; media-src: fonts.gstatic.com fonts.googleapis.com;" /></head>"""
|
2023-07-09 22:27:28 +01:00
|
|
|
|
|
|
|
if not pageuser == "error":
|
|
|
|
return addhtml + pageuser["htmldescription"]
|
|
|
|
else:
|
|
|
|
return """<img src="https://http.cat/images/404.jpg">""", 404
|
|
|
|
|
2023-07-09 21:00:15 +01:00
|
|
|
@app.route("/@<pageusername>/edit", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def edituser(pageusername):
|
2023-07-09 21:00:15 +01:00
|
|
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
|
|
|
|
|
|
|
checkusername = check_username_taken(pageusername)
|
|
|
|
|
|
|
|
if not checkusername == "error":
|
|
|
|
pageuser = get_user(checkusername)
|
|
|
|
if usersession:
|
|
|
|
userCookie = get_session(usersession)
|
|
|
|
user = get_user(userCookie["id"])
|
|
|
|
if pageuser["username"] == user["username"]:
|
|
|
|
if request.method == "POST":
|
2024-04-29 21:36:42 +01:00
|
|
|
requestData = await request.form
|
|
|
|
|
|
|
|
code = requestData["code"].replace("Content-Security-Policy", "").replace("<iframe>", "")
|
2023-07-09 21:00:15 +01:00
|
|
|
conn = get_db_connection()
|
|
|
|
conn.execute("UPDATE users SET htmldescription = ? WHERE id = ?",
|
|
|
|
(code, user["id"]))
|
|
|
|
conn.commit()
|
|
|
|
conn.close()
|
|
|
|
return redirect("/@" + user["username"])
|
|
|
|
else:
|
2024-04-29 21:36:42 +01:00
|
|
|
return await render_template("edituser.html", userdata=user, pageuser=pageuser)
|
2023-07-09 21:00:15 +01:00
|
|
|
else:
|
|
|
|
return """<img src="https://http.cat/images/403.jpg">""", 403
|
|
|
|
else:
|
|
|
|
return """<img src="https://http.cat/images/403.jpg">""", 403
|
|
|
|
else:
|
|
|
|
return """<img src="https://http.cat/images/404.jpg">""", 404
|
|
|
|
|
|
|
|
|
2023-07-08 23:44:53 +01:00
|
|
|
@app.route("/api/frontpage", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def apifrontpage():
|
2023-07-08 23:44:53 +01:00
|
|
|
conn = get_db_connection()
|
|
|
|
posts = conn.execute("SELECT * FROM posts ORDER BY created DESC;").fetchall()
|
|
|
|
conn.close()
|
|
|
|
|
|
|
|
result = []
|
|
|
|
|
|
|
|
for post in posts:
|
|
|
|
comments = []
|
|
|
|
|
|
|
|
for comment in get_comments(post["id"]):
|
|
|
|
commentthing = {
|
|
|
|
"title": comment["textstr"],
|
2023-07-10 00:06:36 +01:00
|
|
|
"id": comment["id"],
|
2023-07-08 23:44:53 +01:00
|
|
|
"created": comment["created"],
|
|
|
|
"creator": {
|
|
|
|
"id": comment["creator"],
|
|
|
|
"username": get_user(comment["creator"])["username"]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
comments.append(commentthing)
|
|
|
|
|
|
|
|
|
|
|
|
mainthing = {
|
|
|
|
"id": post["id"],
|
|
|
|
"created": post["created"],
|
|
|
|
"title": post["textstr"],
|
|
|
|
"imgurl": post["imageurl"],
|
|
|
|
"creator": {
|
|
|
|
"id": post["creator"],
|
|
|
|
"username": get_user(post["creator"])["username"]
|
|
|
|
},
|
|
|
|
"comments": comments
|
|
|
|
}
|
|
|
|
|
|
|
|
result.append(mainthing)
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
@app.route("/api/userinfo", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def apiuserinfo():
|
2023-07-08 23:44:53 +01:00
|
|
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
|
|
|
if usersession:
|
|
|
|
userCookie = get_session(usersession)
|
|
|
|
user = get_user(userCookie["id"])
|
|
|
|
datatemplate = {
|
|
|
|
"username": user["username"],
|
|
|
|
"id": user["id"],
|
|
|
|
"created": user["created"]
|
|
|
|
}
|
|
|
|
return datatemplate
|
|
|
|
else:
|
|
|
|
return {
|
|
|
|
"error": "no authentication"
|
|
|
|
}, 403
|
|
|
|
|
2023-07-09 23:21:48 +01:00
|
|
|
|
2023-07-08 23:44:53 +01:00
|
|
|
@app.route("/api/login", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def apilogin():
|
2023-07-08 23:44:53 +01:00
|
|
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
|
|
|
if request.method == "POST":
|
2024-04-29 21:36:42 +01:00
|
|
|
data = await request.get_json()
|
2023-07-08 23:44:53 +01:00
|
|
|
username = data["username"]
|
|
|
|
password = data["password"]
|
|
|
|
|
|
|
|
userID = check_username_taken(username)
|
|
|
|
user = get_user(userID)
|
|
|
|
|
|
|
|
if user == "error":
|
|
|
|
return {
|
|
|
|
"error": "wrong username or password"
|
|
|
|
}, 401
|
|
|
|
|
|
|
|
if not check_password_hash(user["password"], (password)):
|
|
|
|
return {
|
|
|
|
"error": "wrong username or password"
|
|
|
|
}, 401
|
|
|
|
|
|
|
|
randomCharacters = secrets.token_hex(512)
|
|
|
|
|
|
|
|
conn = get_db_connection()
|
|
|
|
conn.execute("INSERT INTO sessions (session, id) VALUES (?, ?)",
|
|
|
|
(randomCharacters, userID))
|
|
|
|
conn.commit()
|
|
|
|
conn.close()
|
|
|
|
|
|
|
|
return {
|
|
|
|
"key": randomCharacters
|
2023-07-11 20:41:57 +01:00
|
|
|
}, 200
|
2023-07-10 17:53:11 +01:00
|
|
|
else:
|
|
|
|
return {
|
|
|
|
"error": "https://http.cat/images/400.jpg"
|
|
|
|
}, 400
|
2023-07-08 23:44:53 +01:00
|
|
|
|
2024-05-15 19:45:36 +01:00
|
|
|
@app.route("/api/migrate", methods=("GET", "POST"))
|
|
|
|
async def migrate():
|
|
|
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
|
|
|
if request.method == "POST":
|
|
|
|
data = await request.get_json()
|
|
|
|
sub = data["sub"]
|
|
|
|
password = data["access_token"]
|
|
|
|
userCookie = get_session(usersession)
|
|
|
|
user = get_user(userCookie["id"])
|
|
|
|
if user == "error":
|
|
|
|
return { "error": "User doesn't exist" }, 403
|
|
|
|
|
|
|
|
conn = get_db_connection()
|
|
|
|
subdata = '{"access_token":"' + password + '"}'
|
|
|
|
response = requests.post("https://auth.hectabit.org/api/uniqueid", subdata)
|
|
|
|
if response.status_code == 200:
|
|
|
|
data = response.json()
|
|
|
|
conn.execute("UPDATE users SET password = ? WHERE id = ?",
|
|
|
|
(str("OAUTH-" + data['uniqueid']), user["id"]))
|
|
|
|
else:
|
|
|
|
return {"error": response.json()["error"]}, response.status_code
|
|
|
|
conn.commit()
|
|
|
|
conn.close()
|
|
|
|
return {"success": "true"}, 200
|
|
|
|
|
2024-05-14 00:46:42 +01:00
|
|
|
@app.route("/api/oauth", methods=("GET", "POST"))
|
2024-05-14 01:23:03 +01:00
|
|
|
async def apioauth():
|
2024-05-14 00:46:42 +01:00
|
|
|
if request.method == "POST":
|
|
|
|
data = await request.get_json()
|
|
|
|
username = data["username"]
|
2024-05-15 19:45:36 +01:00
|
|
|
sub = data["sub"]
|
2024-05-14 00:46:42 +01:00
|
|
|
password = data["access_token"]
|
|
|
|
|
2024-05-14 01:23:03 +01:00
|
|
|
conn = get_db_connection()
|
|
|
|
subdata = '{"access_token":"' + password + '"}'
|
|
|
|
response = requests.post("https://auth.hectabit.org/api/loggedin", subdata)
|
2024-05-14 00:46:42 +01:00
|
|
|
if response.status_code == 200:
|
2024-05-15 19:45:36 +01:00
|
|
|
userID = check_sub_taken(sub)
|
2024-05-14 00:46:42 +01:00
|
|
|
user = get_user(userID)
|
|
|
|
if user == "error":
|
2024-05-15 19:45:36 +01:00
|
|
|
userID = check_username_taken(username)
|
|
|
|
user = get_user(userID)
|
|
|
|
if user == "error":
|
2024-05-16 09:11:21 +01:00
|
|
|
conn.execute("INSERT INTO users (username, password, created, htmldescription, banned) VALUES (?, ?, ?, ?, ?)",
|
|
|
|
(username, str("OAUTH-" + sub), str(time.time()), "", "0"))
|
|
|
|
userID = conn.execute("SELECT * FROM users WHERE lower(username) = ?",
|
|
|
|
(username.lower(),)).fetchone()["id"]
|
2024-05-15 19:45:36 +01:00
|
|
|
else:
|
|
|
|
if user["password"] != "OAUTH-" + sub:
|
|
|
|
return {"error": "Migration required or username taken"}, 422
|
2024-05-14 00:46:42 +01:00
|
|
|
else:
|
2024-05-14 01:23:03 +01:00
|
|
|
return {"error": response.json()["error"]}, response.status_code
|
2024-05-14 00:46:42 +01:00
|
|
|
|
|
|
|
randomCharacters = secrets.token_hex(512)
|
|
|
|
|
|
|
|
conn.execute("INSERT INTO sessions (session, id) VALUES (?, ?)",
|
|
|
|
(randomCharacters, userID))
|
|
|
|
conn.commit()
|
|
|
|
conn.close()
|
|
|
|
|
|
|
|
return {
|
|
|
|
"key": randomCharacters
|
|
|
|
}, 200
|
|
|
|
else:
|
|
|
|
return {
|
|
|
|
"error": "https://http.cat/images/405.jpg"
|
|
|
|
}, 405
|
|
|
|
|
2023-07-08 23:44:53 +01:00
|
|
|
@app.route("/apidocs", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def apidocs():
|
2023-07-08 23:44:53 +01:00
|
|
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
|
|
|
|
|
|
|
if usersession:
|
|
|
|
userCookie = get_session(usersession)
|
|
|
|
user = get_user(userCookie["id"])
|
2024-04-29 21:36:42 +01:00
|
|
|
return await render_template("apidocs.html", userdata=user)
|
2023-07-08 23:44:53 +01:00
|
|
|
else:
|
2024-04-29 21:36:42 +01:00
|
|
|
return await render_template("apidocs.html")
|
2023-07-08 23:44:53 +01:00
|
|
|
|
2023-07-08 17:00:32 +01:00
|
|
|
@app.route("/post", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def post():
|
2023-07-08 17:00:32 +01:00
|
|
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
|
|
|
if usersession:
|
|
|
|
if request.method == "POST":
|
2024-04-29 21:36:42 +01:00
|
|
|
formData = await request.form
|
|
|
|
formFiles = await request.files
|
|
|
|
|
|
|
|
title = formData["title"]
|
2023-07-08 17:00:32 +01:00
|
|
|
if title == "":
|
2024-04-29 21:36:42 +01:00
|
|
|
await flash("Text required :3")
|
2023-07-08 17:00:32 +01:00
|
|
|
return redirect(url_for("post"))
|
|
|
|
|
2023-07-09 23:10:36 +01:00
|
|
|
if len(title) > 300:
|
2024-04-29 21:36:42 +01:00
|
|
|
await flash("Too long title!")
|
2023-07-08 17:00:32 +01:00
|
|
|
return redirect(url_for("post"))
|
|
|
|
|
2024-04-29 21:36:42 +01:00
|
|
|
file = formFiles["file"]
|
2023-07-08 17:00:32 +01:00
|
|
|
|
2024-04-29 21:36:42 +01:00
|
|
|
if (file):
|
|
|
|
if not allowed_file(file.filename):
|
|
|
|
await flash("File is not an image!")
|
|
|
|
return redirect(url_for("post"))
|
2023-07-08 17:00:32 +01:00
|
|
|
|
2023-07-10 14:01:57 +01:00
|
|
|
userCookie = get_session(usersession)
|
|
|
|
user = get_user(userCookie["id"])
|
|
|
|
|
2023-07-09 23:10:36 +01:00
|
|
|
if not user["banned"] == "0":
|
2024-04-29 21:36:42 +01:00
|
|
|
await flash("Your account has been suspended. You may no longer post")
|
2023-07-09 23:10:36 +01:00
|
|
|
return redirect(url_for("post"))
|
|
|
|
|
2023-07-08 17:00:32 +01:00
|
|
|
filename = secure_filename(file.filename)
|
2023-07-09 23:10:36 +01:00
|
|
|
finalfilename = secrets.token_hex(32) + filename
|
2023-07-08 17:00:32 +01:00
|
|
|
|
2024-04-29 21:36:42 +01:00
|
|
|
await file.save(os.path.join(UPLOAD_FOLDER, finalfilename))
|
2023-07-08 17:00:32 +01:00
|
|
|
imgurl = "/cdn/" + finalfilename
|
|
|
|
|
2024-04-29 21:36:42 +01:00
|
|
|
if (not file):
|
|
|
|
imgurl = ""
|
|
|
|
|
2023-07-08 17:00:32 +01:00
|
|
|
conn = get_db_connection()
|
|
|
|
conn.execute("INSERT INTO posts (textstr, imageurl, creator, created) VALUES (?, ?, ?, ?)",
|
|
|
|
(title, imgurl, userCookie["id"], str(time.time())))
|
|
|
|
conn.commit()
|
|
|
|
conn.close()
|
|
|
|
return redirect(url_for("main"))
|
|
|
|
|
|
|
|
else:
|
|
|
|
userCookie = get_session(usersession)
|
|
|
|
user = get_user(userCookie["id"])
|
2024-04-29 21:36:42 +01:00
|
|
|
return await render_template("post.html", userdata=user)
|
2023-07-08 17:00:32 +01:00
|
|
|
else:
|
2024-04-29 22:01:03 +01:00
|
|
|
await flash("A burgercat account is required to post :3")
|
2023-07-08 17:00:32 +01:00
|
|
|
return redirect(url_for("login"))
|
|
|
|
|
|
|
|
|
2023-07-08 23:44:53 +01:00
|
|
|
@app.route("/api/comment", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def comment():
|
2023-07-08 17:00:32 +01:00
|
|
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
|
|
|
if usersession:
|
|
|
|
if request.method == "POST":
|
|
|
|
|
2024-04-29 21:36:42 +01:00
|
|
|
data = await request.get_json()
|
2023-07-08 17:00:32 +01:00
|
|
|
uid = data["id"]
|
|
|
|
title = data["title"]
|
2024-05-15 19:45:36 +01:00
|
|
|
|
2023-07-08 17:00:32 +01:00
|
|
|
userCookie = get_session(usersession)
|
|
|
|
user = get_user(userCookie["id"])
|
|
|
|
|
2023-07-09 23:10:36 +01:00
|
|
|
if len(title) > 300:
|
|
|
|
return {
|
|
|
|
"error": "too much text"
|
|
|
|
}, 403
|
|
|
|
|
2023-07-08 17:00:32 +01:00
|
|
|
if not user["banned"] == "0":
|
2023-07-08 23:44:53 +01:00
|
|
|
return {
|
2023-07-10 18:25:08 +01:00
|
|
|
"error": "banned"
|
2023-07-08 23:44:53 +01:00
|
|
|
}, 403
|
2023-07-08 17:00:32 +01:00
|
|
|
|
|
|
|
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()
|
|
|
|
|
2023-07-11 20:41:57 +01:00
|
|
|
return "success", 200
|
2023-07-08 17:00:32 +01:00
|
|
|
|
|
|
|
else:
|
2023-07-08 23:44:53 +01:00
|
|
|
return {
|
|
|
|
"error": "https://http.cat/images/400.jpg"
|
|
|
|
}, 400
|
2023-07-08 17:00:32 +01:00
|
|
|
else:
|
2023-07-08 23:44:53 +01:00
|
|
|
return {
|
|
|
|
"error": "https://http.cat/images/401.jpg"
|
|
|
|
}, 401
|
2023-07-08 17:00:32 +01:00
|
|
|
|
2023-07-09 23:42:53 +01:00
|
|
|
@app.route("/api/post/<post_id>/comments", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def apicomments(post_id):
|
2023-07-09 23:42:53 +01:00
|
|
|
postthing = get_comments(int(post_id))
|
|
|
|
|
|
|
|
if not postthing == "error":
|
|
|
|
comments = []
|
|
|
|
for comment in postthing:
|
|
|
|
commentthing = {
|
|
|
|
"title": comment["textstr"],
|
|
|
|
"created": comment["created"],
|
|
|
|
"creator": {
|
|
|
|
"id": comment["creator"],
|
|
|
|
"username": get_user(comment["creator"])["username"]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
comments.append(commentthing)
|
|
|
|
return comments
|
2023-07-08 17:00:32 +01:00
|
|
|
|
|
|
|
@app.route("/cdn/<filename>", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def cdn(filename):
|
2023-07-08 17:00:32 +01:00
|
|
|
if os.path.exists(os.path.join(UPLOAD_FOLDER, filename)):
|
2024-04-29 21:36:42 +01:00
|
|
|
return await send_from_directory(UPLOAD_FOLDER, filename)
|
2023-07-08 17:00:32 +01:00
|
|
|
else:
|
|
|
|
return "file doesn't exist!!"
|
|
|
|
|
2024-05-15 19:45:36 +01:00
|
|
|
@app.route("/legacysignup", methods=("GET", "POST"))
|
|
|
|
async def legacysignup():
|
|
|
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
|
|
|
if usersession:
|
|
|
|
return redirect(url_for("main"))
|
|
|
|
if request.method == "POST":
|
|
|
|
requestData = await request.form
|
|
|
|
|
|
|
|
if not check_username_taken(requestData["username"]) == "error":
|
|
|
|
await flash("Username already taken :3")
|
|
|
|
return redirect(url_for("signup"))
|
|
|
|
|
|
|
|
if not requestData["username"].isalnum():
|
|
|
|
await flash("Username must be alphanumeric :3")
|
|
|
|
return redirect(url_for("signup"))
|
|
|
|
|
|
|
|
if not len(requestData["password"]) > int(PASSWORD_REQUIREMENT):
|
|
|
|
await flash("Password must contain at least " + PASSWORD_REQUIREMENT + " characters")
|
|
|
|
return redirect(url_for("signup"))
|
|
|
|
|
|
|
|
hashedpassword = generate_password_hash(requestData["password"])
|
|
|
|
|
|
|
|
conn = get_db_connection()
|
|
|
|
conn.execute("INSERT INTO users (username, password, created, htmldescription) VALUES (?, ?, ?, ?)",
|
|
|
|
(requestData["username"], hashedpassword, str(time.time()), ""))
|
|
|
|
conn.commit()
|
|
|
|
conn.close()
|
|
|
|
|
|
|
|
return redirect(url_for("login"))
|
|
|
|
else:
|
|
|
|
return await render_template("signup.html")
|
|
|
|
|
2023-07-08 17:00:32 +01:00
|
|
|
@app.route("/signup", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def signup():
|
2023-07-08 17:00:32 +01:00
|
|
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
|
|
|
if usersession:
|
|
|
|
return redirect(url_for("main"))
|
|
|
|
else:
|
2024-05-14 07:47:59 +01:00
|
|
|
return redirect(url_for("oauth"))
|
2023-07-08 17:00:32 +01:00
|
|
|
|
|
|
|
@app.route("/login", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def login():
|
2023-07-08 17:00:32 +01:00
|
|
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
|
|
|
if usersession:
|
|
|
|
redirect(url_for("main"))
|
|
|
|
if request.method == "POST":
|
2024-04-29 21:36:42 +01:00
|
|
|
requestData = await request.form
|
|
|
|
|
|
|
|
userID = check_username_taken(requestData["username"])
|
2023-07-08 17:00:32 +01:00
|
|
|
user = get_user(userID)
|
|
|
|
|
|
|
|
if user == "error":
|
2024-04-29 22:01:03 +01:00
|
|
|
await flash("Wrong username or password :3")
|
2023-07-08 17:00:32 +01:00
|
|
|
return redirect(url_for("login"))
|
|
|
|
|
2024-04-29 21:36:42 +01:00
|
|
|
if not check_password_hash(user["password"], (requestData["password"])):
|
2024-04-29 22:01:03 +01:00
|
|
|
await flash("Wrong username or password :3")
|
2023-07-08 17:00:32 +01:00
|
|
|
return redirect(url_for("login"))
|
|
|
|
|
|
|
|
randomCharacters = secrets.token_hex(512)
|
|
|
|
|
|
|
|
conn = get_db_connection()
|
|
|
|
conn.execute("INSERT INTO sessions (session, id) VALUES (?, ?)",
|
|
|
|
(randomCharacters, userID))
|
|
|
|
conn.commit()
|
|
|
|
conn.close()
|
|
|
|
|
2024-05-16 09:11:21 +01:00
|
|
|
response = Response("""<script>window.location.href = "/oauth";</script>""")
|
2024-05-16 16:48:22 +01:00
|
|
|
response.set_cookie("session_DO_NOT_SHARE", randomCharacters, samesite="Strict", secure=True)
|
|
|
|
response.set_cookie("legacy_migrate", "1", samesite="Strict", secure=True)
|
2024-04-29 21:36:42 +01:00
|
|
|
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("/")
|
2023-07-08 17:00:32 +01:00
|
|
|
|
|
|
|
else:
|
2024-04-29 21:36:42 +01:00
|
|
|
return await render_template("login.html")
|
2023-07-08 17:00:32 +01:00
|
|
|
|
2024-05-14 01:23:03 +01:00
|
|
|
@app.route("/oauth", methods=("GET", "POST"))
|
|
|
|
async def oauth():
|
2024-05-15 19:45:36 +01:00
|
|
|
legacymigrate = request.cookies.get("legacy_migrate")
|
|
|
|
if legacymigrate != "1":
|
|
|
|
return await render_template("oauth.html")
|
|
|
|
else:
|
|
|
|
return await render_template("migrate.html")
|
2023-07-08 17:00:32 +01:00
|
|
|
|
|
|
|
@app.route("/settings", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def settings():
|
2023-07-08 17:00:32 +01:00
|
|
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
|
|
|
if usersession:
|
|
|
|
userCookie = get_session(usersession)
|
|
|
|
user = get_user(userCookie["id"])
|
|
|
|
|
2024-05-14 07:47:59 +01:00
|
|
|
return await render_template("settings.html", userdata=user, createddate=datetime.datetime.fromtimestamp(int(str(user["created"]).split(".")[0])).strftime("%Y-%m-%d %H:%m:%S"))
|
2023-07-08 17:00:32 +01:00
|
|
|
else:
|
|
|
|
return redirect("/")
|
|
|
|
|
|
|
|
|
2023-07-10 17:53:11 +01:00
|
|
|
@app.route("/api/delete", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def delete():
|
2023-07-08 17:00:32 +01:00
|
|
|
usersession = request.cookies.get("session_DO_NOT_SHARE")
|
2023-07-10 17:53:11 +01:00
|
|
|
|
|
|
|
if request.method == "POST":
|
2024-04-29 21:36:42 +01:00
|
|
|
data = await request.get_json()
|
2023-07-10 17:53:11 +01:00
|
|
|
postid = int(data["id"])
|
|
|
|
|
|
|
|
post = get_post(postid)
|
|
|
|
if not post == "error":
|
|
|
|
if usersession:
|
|
|
|
userCookie = get_session(usersession)
|
|
|
|
user = get_user(userCookie["id"])
|
|
|
|
|
|
|
|
if (str(user["administrator"]) == "1") or (int(user["id"]) == int(post["creator"])):
|
|
|
|
post = get_post(postid)
|
|
|
|
conn = get_db_connection()
|
|
|
|
conn.execute("DELETE FROM posts WHERE id = ?", (postid,))
|
|
|
|
conn.commit()
|
|
|
|
conn.close()
|
2023-07-11 20:41:57 +01:00
|
|
|
return "success", 200
|
2023-07-10 17:53:11 +01:00
|
|
|
else:
|
|
|
|
return {
|
|
|
|
"error": "https://http.cat/images/403.jpg"
|
|
|
|
}, 403
|
2023-07-08 17:00:32 +01:00
|
|
|
else:
|
2023-07-10 17:53:11 +01:00
|
|
|
return {
|
|
|
|
"error": "https://http.cat/images/400.jpg"
|
|
|
|
}, 400
|
2023-07-08 17:00:32 +01:00
|
|
|
|
|
|
|
@app.route("/listusers", methods=("GET", "POST"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def listusers():
|
2023-07-08 17:00:32 +01:00
|
|
|
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"))
|
2024-04-29 21:36:42 +01:00
|
|
|
async def logout():
|
2023-07-08 17:00:32 +01:00
|
|
|
resp = redirect(url_for("main"))
|
|
|
|
session = request.cookies.get("session_DO_NOT_SHARE")
|
|
|
|
resp.delete_cookie("session_DO_NOT_SHARE")
|
2024-05-15 19:45:36 +01:00
|
|
|
resp.delete_cookie("prefuser")
|
2024-05-16 09:11:21 +01:00
|
|
|
resp.delete_cookie("legacy_migrate")
|
2023-07-08 17:00:32 +01:00
|
|
|
return resp
|
|
|
|
|
|
|
|
@app.errorhandler(500)
|
2024-04-29 21:36:42 +01:00
|
|
|
async def page_not_found(e):
|
2023-07-08 17:00:32 +01:00
|
|
|
return """<img src="https://http.cat/images/500.jpg">""", 500
|
|
|
|
|
|
|
|
@app.errorhandler(400)
|
2024-04-29 21:36:42 +01:00
|
|
|
async def page_not_found(e):
|
2023-07-08 17:00:32 +01:00
|
|
|
return """<img src="https://http.cat/images/400.jpg">""", 400
|
|
|
|
|
2023-07-09 21:00:15 +01:00
|
|
|
@app.errorhandler(429)
|
2024-04-29 21:36:42 +01:00
|
|
|
async def page_not_found(e):
|
2023-07-09 21:00:15 +01:00
|
|
|
return """<img src="https://http.cat/images/429.jpg">""", 429
|
|
|
|
|
2023-07-08 17:00:32 +01:00
|
|
|
@app.errorhandler(404)
|
2024-04-29 21:36:42 +01:00
|
|
|
async def page_not_found(e):
|
2023-07-08 17:00:32 +01:00
|
|
|
return """<img src="https://http.cat/images/404.jpg">""", 404
|
|
|
|
|
|
|
|
@app.errorhandler(413)
|
2024-04-29 21:36:42 +01:00
|
|
|
async def page_not_found(e):
|
2023-07-08 23:44:53 +01:00
|
|
|
return "Images can't be larger than " + str(UPLOAD_LIMIT) + "MB", 413
|
2023-07-08 17:00:32 +01:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
print("[INFO] Server started")
|
2023-07-10 20:28:15 +01:00
|
|
|
|
2024-04-29 21:36:42 +01:00
|
|
|
hypercornconfig = Config()
|
|
|
|
hypercornconfig.bind = ("0.0.0.0" + ":" + PORT)
|
|
|
|
|
|
|
|
asyncio.run(serve(app, hypercornconfig))
|
2023-07-10 20:28:15 +01:00
|
|
|
|
2023-07-12 02:55:30 +01:00
|
|
|
print("[INFO] Server stopped")
|