updated db schema for attachments + add create user api endpoint

This commit is contained in:
0x01FE committed 2025-08-21 16:22:34 -05:00
1 parent e6d3f5c7b7
commit af7d682a38
8 files changed
+91 -9

No files matched your search

BIN
View File
Binary file not shown.
+10
View File
@@ -3,6 +3,7 @@ CREATE TABLE IF NOT EXISTS "messages" (
"user" INTEGER NOT NULL, "user" INTEGER NOT NULL,
"thread" INTEGER NOT NULL, "thread" INTEGER NOT NULL,
"content" TEXT NOT NULL, "content" TEXT NOT NULL,
"time" INTEGER NOT NULL,
PRIMARY KEY("id"), PRIMARY KEY("id"),
FOREIGN KEY ("user") REFERENCES "users"("id") FOREIGN KEY ("user") REFERENCES "users"("id")
ON UPDATE NO ACTION ON DELETE NO ACTION, ON UPDATE NO ACTION ON DELETE NO ACTION,
@@ -13,6 +14,7 @@ CREATE TABLE IF NOT EXISTS "messages" (
CREATE TABLE IF NOT EXISTS "users" ( CREATE TABLE IF NOT EXISTS "users" (
"id" INTEGER NOT NULL UNIQUE, "id" INTEGER NOT NULL UNIQUE,
"name" TEXT NOT NULL, "name" TEXT NOT NULL,
"passhash" TEXT NOT NULL,
PRIMARY KEY("id") PRIMARY KEY("id")
); );
@@ -41,3 +43,11 @@ CREATE TABLE IF NOT EXISTS "threads" (
ON UPDATE NO ACTION ON DELETE NO ACTION ON UPDATE NO ACTION ON DELETE NO ACTION
); );
CREATE TABLE IF NOT EXISTS "attachments" (
"id" INTEGER NOT NULL UNIQUE,
"message_id" INTEGER NOT NULL,
"file_path" TEXT NOT NULL,
PRIMARY KEY("id"),
FOREIGN KEY ("message_id") REFERENCES "messages"("id")
ON UPDATE NO ACTION ON DELETE NO ACTION
);
+3 -3
View File
@@ -26,10 +26,10 @@ def send_message() -> tuple[str, int]:
if any([not p in data for p in ['content', 'thread']]): if any([not p in data for p in ['content', 'thread']]):
return '', 400 return '', 400
new_message = database.MessageORM( new_message = database.MessageDTO(
user=flask_login.current_user.get_id(), user_id=flask_login.current_user.get_id(),
content=data['content'], content=data['content'],
thread=data['thread'], thread_id=data['thread'],
time=int(time.time()) time=int(time.time())
) )
+40
View File
@@ -23,3 +23,43 @@ def get_user_forums() -> tuple[flask.Response, int]:
forums_dict = {"forums" : [dataclasses.asdict(forum) for forum in forums]} forums_dict = {"forums" : [dataclasses.asdict(forum) for forum in forums]}
return flask.jsonify(forums_dict), 200 return flask.jsonify(forums_dict), 200
@user.route('/create', methods=['POST'])
def create_user() -> tuple[flask.Response, int]:
"""
Create a new user account.
Expects JSON with 'username' and 'password' fields.
"""
data = flask.request.get_json()
if not data:
return flask.jsonify({"error": "No JSON data provided"}), 400
username = data.get('username')
password = data.get('password')
if not username or not password:
return flask.jsonify({"error": "Username and password are required"}), 400
# Validate username length and characters
if len(username.strip()) == 0:
return flask.jsonify({"error": "Username cannot be empty"}), 400
if len(password) < 6:
return flask.jsonify({"error": "Password must be at least 6 characters long"}), 400
# Attempt to create the user
new_user = database.create_user(username.strip(), password)
if new_user is None:
return flask.jsonify({"error": "Username already exists or creation failed"}), 409
# Return the created user (without sensitive data)
user_dict = {
"id": new_user.id,
"name": new_user.name,
"message": "User created successfully"
}
return flask.jsonify(user_dict), 201
+32 -5
View File
@@ -7,7 +7,7 @@ import flask_login
import mgU import mgU
DATABASE = "./main2.db" DATABASE = "./main.db"
ph = argon2.PasswordHasher() ph = argon2.PasswordHasher()
@@ -46,21 +46,48 @@ class UserDTO:
@dataclasses.dataclass @dataclasses.dataclass
class MessageDTO: class MessageDTO:
user: UserDTO user_id: UserDTO
content: str content: str
time: int time: int
thread_id: int thread_id: int
id: int | None = None id: int | None = None
def insert_message(message: mgU.Message) -> None: def create_user(username: str, password: str) -> mgU.User | None:
"""
Create a new user with the given username and password.
Returns the created User object with id, or None if creation fails.
"""
# Hash the password
passhash = ph.hash(password)
try:
with Opener() as (conn, cur):
q = load_query("insert_user")
cur.execute(q, [username, passhash])
user_id = cur.lastrowid
return mgU.User(name=username, id=user_id)
except sqlite3.IntegrityError:
# Username already exists or other constraint violation
return None
def insert_message(message: mgU.Message | MessageDTO) -> None:
""" """
Insert a new message (from mgU.Message dataclass) and return a new mgU.Message with id, or None on failure. Insert a new message (from mgU.Message dataclass) and return a new mgU.Message with id, or None on failure.
""" """
if type(message) == mgU.Message:
thread_id = message.thread
user_id = message.user.id
elif type(message) == MessageDTO:
thread_id = message.thread_id
user_id = message.user_id
with Opener() as (conn, cur): with Opener() as (conn, cur):
q = load_query("insert_message") q = load_query("insert_message")
cur.execute(q, [ cur.execute(q, [
message.user, user_id,
message.thread, thread_id,
message.content, message.content,
message.time message.time
]) ])
+4
View File
@@ -0,0 +1,4 @@
{
"username" : "johndoe",
"password" : "securepassword123"
}
-1
View File
@@ -1,4 +1,3 @@
import typing
import dataclasses import dataclasses
@dataclasses.dataclass(kw_only=True) @dataclasses.dataclass(kw_only=True)
+2
View File
@@ -0,0 +1,2 @@
INSERT INTO users (name, passhash)
VALUES (?, ?);