From af7d682a38dbfedc14e8a73fa6df84a172e59e77 Mon Sep 17 00:00:00 2001 From: 0x01FE Date: Thu, 21 Aug 2025 16:22:34 -0500 Subject: [PATCH] updated db schema for attachments + add create user api endpoint --- main.db | Bin 0 -> 53248 bytes schema.sql | 10 +++++++ server/api/forum_api.py | 6 ++-- server/api/user_api.py | 40 ++++++++++++++++++++++++++ server/database.py | 37 ++++++++++++++++++++---- server/example_jsons/create_user.json | 4 +++ server/mgU.py | 1 - server/sql/insert_user.sql | 2 ++ 8 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 main.db create mode 100644 server/example_jsons/create_user.json create mode 100644 server/sql/insert_user.sql diff --git a/main.db b/main.db new file mode 100644 index 0000000000000000000000000000000000000000..6c13ba7e6f114eb26e655e41fdb9a5b360ed8e65 GIT binary patch literal 53248 zcmeI%T~E_c7{KvXp<`tej4=%ol9O`7;#-LEMq_Y7A&axf+!FPssZeDJ1EeLoz(UZ*2aAt*7T%+w=U+>FJj9VyoHp9P#$xsAqfPo-`}Tvh-L8 zNs<)*RrO!VU$8%s?D$(bRUTGUq&wD)>da56GWAWm@?hp`_2ZS9%J*t>`eEgZdVSij zF8>&IF!cx^fB*srAb`Nd0%upJOWNY1eBSl!H+zoldG_{h&)N6fRI#$!GU{zZwChir zhR{(KUg zb>Y=GU!nwv5Jx8PtiT^vP?YWvm-5!(269h$P_Z z(Sd4t(-fWOYe637rl_yB8-9iVzh*Rzbe&qeUs1HiqAYdycbpII`#t}wo@F0<2jRYz z%HCQ|?Nm>_a!H$?m(N#1N^kebv3J~fqh<-?+L8G7?Vi&wqx)Yzk9+;@BcVmdX1&6? zF|=;0idLT=rWGe@EysoFv)83?zg-o3tU@8A0tBeQ|&4qLCY3Dr&a1oZ2a$OqR5{Ir(%e z?2_mc(YuoEh&bm_Zu-HaHp3ritF@boqOHsg8$U^NqiyN*+U1g_Y4X|R@6qFiRukEV zMtr=X~R+o+Y*7hljo{j(iRrv&+ozn;;GS1wwJPLB-JC$ z6Tx)`&IfvQ?+uP^AMAVnbU$E%@w&mh^XI%i()| z1q2X4009ILKmY**5I_I{1abse|IYy;i~s@%Ab$31Q0*~0R#|0009ILK%f8utp69F6G;I91Q0*~ z0R#|0009ILKmdUp0oMO>fCwXi00IagfB*srAb tuple[str, int]: if any([not p in data for p in ['content', 'thread']]): return '', 400 - new_message = database.MessageORM( - user=flask_login.current_user.get_id(), + new_message = database.MessageDTO( + user_id=flask_login.current_user.get_id(), content=data['content'], - thread=data['thread'], + thread_id=data['thread'], time=int(time.time()) ) diff --git a/server/api/user_api.py b/server/api/user_api.py index c1bf8dc..05b246d 100644 --- a/server/api/user_api.py +++ b/server/api/user_api.py @@ -23,3 +23,43 @@ def get_user_forums() -> tuple[flask.Response, int]: forums_dict = {"forums" : [dataclasses.asdict(forum) for forum in forums]} 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 diff --git a/server/database.py b/server/database.py index cfe492d..efc0b53 100644 --- a/server/database.py +++ b/server/database.py @@ -7,7 +7,7 @@ import flask_login import mgU -DATABASE = "./main2.db" +DATABASE = "./main.db" ph = argon2.PasswordHasher() @@ -46,21 +46,48 @@ class UserDTO: @dataclasses.dataclass class MessageDTO: - user: UserDTO + user_id: UserDTO content: str time: int thread_id: int 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. """ + 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): q = load_query("insert_message") cur.execute(q, [ - message.user, - message.thread, + user_id, + thread_id, message.content, message.time ]) diff --git a/server/example_jsons/create_user.json b/server/example_jsons/create_user.json new file mode 100644 index 0000000..2cf1a51 --- /dev/null +++ b/server/example_jsons/create_user.json @@ -0,0 +1,4 @@ +{ + "username" : "johndoe", + "password" : "securepassword123" +} diff --git a/server/mgU.py b/server/mgU.py index faa07d2..39159a4 100644 --- a/server/mgU.py +++ b/server/mgU.py @@ -1,4 +1,3 @@ -import typing import dataclasses @dataclasses.dataclass(kw_only=True) diff --git a/server/sql/insert_user.sql b/server/sql/insert_user.sql new file mode 100644 index 0000000..3833497 --- /dev/null +++ b/server/sql/insert_user.sql @@ -0,0 +1,2 @@ +INSERT INTO users (name, passhash) +VALUES (?, ?);