From cff9aa0dd1c4ce68ffd044dc4228436db14db855 Mon Sep 17 00:00:00 2001 From: ben Date: Mon, 3 Aug 2026 16:21:10 +0200 Subject: [PATCH] update many files --- backend/auth.py | 40 ++++------ backend/main.py | 175 +++++++++++++++++++++++++++++-------------- backend/rooms.py | 43 +++++++---- database/calendar.db | Bin 45056 -> 49152 bytes database/db_init.py | 72 +++++++++++++++--- 5 files changed, 220 insertions(+), 110 deletions(-) diff --git a/backend/auth.py b/backend/auth.py index e32b27f..84e19b0 100644 --- a/backend/auth.py +++ b/backend/auth.py @@ -5,7 +5,10 @@ from datetime import datetime, timedelta from fastapi import Header, HTTPException from dotenv import load_dotenv import os +from fastapi import Depends, HTTPException +from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials +security = HTTPBearer() load_dotenv() SECRET_KEY = os.getenv("SECRET_KEY") @@ -64,32 +67,15 @@ def verify_user(username, password): return None -def get_current_user(authorization: str = Header(None)): +def get_current_user( + credentials: HTTPAuthorizationCredentials = Depends(security) +): + token = credentials.credentials - if authorization is None: - raise HTTPException( - status_code=401, - detail="Missing token" - ) + payload = jwt.decode( + token, + SECRET_KEY, + algorithms=[ALGORITHM] + ) - try: - scheme, token = authorization.split(" ") - - if scheme.lower() != "bearer": - raise Exception() - - payload = jwt.decode( - token, - SECRET_KEY, - algorithms=[ALGORITHM] - ) - - user_id = payload["user_id"] - - return user_id - - except JWTError: - raise HTTPException( - status_code=401, - detail="Invalid token" - ) \ No newline at end of file + return payload["user_id"] \ No newline at end of file diff --git a/backend/main.py b/backend/main.py index 166e274..401bf59 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,22 +1,24 @@ -from fastapi import FastAPI +from fastapi import FastAPI, Depends from pydantic import BaseModel -from database import fetch_all +from database import fetch_all, fetch_one from events import create_event -from auth import create_user, verify_user, create_token +from auth import create_user, verify_user, create_token, get_current_user from rooms import create_room, search_room, accept_room + class Login(BaseModel): username: str password: str + class UserCreate(BaseModel): - username: str + username: str password: str + class EventCreate(BaseModel): room_id: int - creator_id: int type: str title: str description: str @@ -26,19 +28,20 @@ class EventCreate(BaseModel): status: str min_people: int + class RoomCreate(BaseModel): room_name: str - creator_id: int invite_code: str + class RoomSearch(BaseModel): - user_id: int invite_code: str + class RoomJoin(BaseModel): - user_id: int invite_code: str + app = FastAPI( title="Calendar API", description="Room based agenda system", @@ -50,8 +53,7 @@ app = FastAPI( def home(): return { "status": "online", - "service": "Calendar API", - "cheese": "tasty" + "service": "Calendar API" } @@ -66,42 +68,15 @@ def database_status(): } -@app.post("/events") -def api_create_event(event: EventCreate): +# users - event_id = create_event( - room_id=event.room_id, - creator_id=event.creator_id, - type=event.type, - title=event.title, - description=event.description, - start_time=event.start_time, - end_time=event.end_time, - visibility=event.visibility, - status=event.status, - min_people=event.min_people - ) - - return { - "status": "created", - "event_id": event_id - } - - -@app.get("/events") -def get_events(): - events = fetch_all("SELECT * FROM events;") - - return { - "events": [dict(event) for event in events] - } @app.post("/create-user") def api_create_user(user: UserCreate): user_id = create_user( - username=user.username, - password=user.password, + user.username, + user.password ) return { @@ -109,6 +84,7 @@ def api_create_user(user: UserCreate): "user_id": user_id } + @app.post("/login") def api_login(login: Login): @@ -131,46 +107,65 @@ def api_login(login: Login): } -@app.post("/create_room") -def api_create_room(room: RoomCreate): - - room_id = create_room( +@app.get("/me") +def get_me( + user_id: int = Depends(get_current_user) +): + return { + "user_id": user_id + } + + +# rooms + + +@app.post("/create-room") +def api_create_room( + room: RoomCreate, + user_id: int = Depends(get_current_user) +): + + room_id = create_room( room.room_name, - room.creator_id, + user_id, room.invite_code ) - if room_id is None: - return { - "status": "failed" - } - return { - "status": "success", + "status": "created", "room_id": room_id } + @app.post("/search-room") -def api_search_room(search_for_room: RoomSearch): +def api_search_room( + room: RoomSearch +): + room_name = search_room( - search_for_room.invite_code + room.invite_code ) if room_name is None: return { - "status": "failed" + "status": "failed", + "reason": "room_not_found" } return { - "status": "success", + "status": "found", "room_name": room_name } + @app.post("/join-room") -def api_join_room(room: RoomJoin): +def api_join_room( + room: RoomJoin, + user_id: int = Depends(get_current_user) +): result = accept_room( - room.user_id, + user_id, room.invite_code ) @@ -183,4 +178,70 @@ def api_join_room(room: RoomJoin): return { "status": "joined", **result + } + +# events + +@app.post("/events") +def api_create_event( + event: EventCreate, + user_id: int = Depends(get_current_user) +): + + # check of gebruiker lid is + member = fetch_one( + """ + SELECT * + FROM room_members + WHERE room_id = ? + AND user_id = ? + """, + ( + event.room_id, + user_id + ) + ) + + if member is None: + return { + "status": "failed", + "reason": "not_room_member" + } + + + event_id = create_event( + room_id=event.room_id, + creator_id=user_id, + type=event.type, + title=event.title, + description=event.description, + start_time=event.start_time, + end_time=event.end_time, + visibility=event.visibility, + status=event.status, + min_people=event.min_people + ) + + return { + "status": "created", + "event_id": event_id + } + + +@app.get("/events") +def get_events( + user_id: int = Depends(get_current_user) +): + + events = fetch_all( + """ + SELECT * + FROM events + WHERE creator_id = ? + """, + (user_id,) + ) + + return { + "events": [dict(event) for event in events] } \ No newline at end of file diff --git a/backend/rooms.py b/backend/rooms.py index 48a3dec..675d5bd 100644 --- a/backend/rooms.py +++ b/backend/rooms.py @@ -81,39 +81,54 @@ def search_room( return room["name"] -def accept_room( - user_id, - invite_code -): +def accept_room(user_id, invite_code): + query = """ SELECT id FROM rooms WHERE invite_code = ? """ + room = fetch_one( query, - ( - invite_code, - ) + (invite_code,) ) if room is None: return None room_id = room["id"] - role = DEFAULT_ROLE - query2 = """ - INSERT INTO "main"."room_members" - ("room_id", "user_id", "role") - VALUES (?, ?, ?); + + # check dubbele join + existing = fetch_one( + """ + SELECT id + FROM room_members + WHERE room_id = ? + AND user_id = ? + """, + ( + room_id, + user_id + ) + ) + + if existing: + return None + + + query = """ + INSERT INTO room_members + (room_id, user_id, role) + VALUES (?, ?, ?) """ member_id = execute( - query2, + query, ( room_id, user_id, - role + "member" ) ) diff --git a/database/calendar.db b/database/calendar.db index 7ba966ffa4ecef578af0b9ec1da9333bc13bbc15..6a02334437931e7fb56fd6fc2561f1586b42e8b7 100644 GIT binary patch delta 513 zcmZp8z|_#dJV9Demw|zS2Z&*SZK95`vMz&OG9xel4+d`bLO1NZ1tJk?BeRGjE%yXc`2zCrFof!rK$0`skuq1MMe4fxlm>?k8_Z# zV~DFlsGnzGsH=jfpNngR0*Y!be?J9e9U5RxW{QqNX>n>%d}fMfvj8X1E_KG%_>#n= zoYd5^)Vz|-57;>^7=<>!vX@feW92`*aY^_v7(EYM)X zSKlZs#LK|RD68Kntf?s~%g9<#lwXivoS2iDnwyxHqL7$YoSc!FT9TSqo~n?Tn5Tdt zu8^BrqL7-Jr;wePm!GGn092Qo57MBJm!GbXlv!pO+T%)$co8kk~c22)H- JEG(RyoB-halt2Ig delta 155 zcmZo@U~YK8G(lQWi-CcG8;D_mb)t^3q85W*G9xel4+bvwGYtF&{Ac;@@m}HE%;Uj3 zn{yWX8P534iUL0Do6oSzS}+Q3eq}GEz{A3SmVy5({|o-x{AV`{8tmntd^X>li-rFw z1OIRSFZ^#e3lyB=pBTV7`QCos$)DXTCh E08BVA761SM diff --git a/database/db_init.py b/database/db_init.py index d021037..1aa6652 100644 --- a/database/db_init.py +++ b/database/db_init.py @@ -3,8 +3,13 @@ import sqlite3 DB_NAME = "calendar.db" conn = sqlite3.connect(DB_NAME) + +# SQLite foreign keys activeren +conn.execute("PRAGMA foreign_keys = ON;") + cur = conn.cursor() + cur.execute(""" CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -13,73 +18,116 @@ CREATE TABLE IF NOT EXISTS users ( ); """) + cur.execute(""" CREATE TABLE IF NOT EXISTS rooms ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, owner_id INTEGER NOT NULL, - invite_code TEXT UNIQUE, + invite_code TEXT NOT NULL UNIQUE, FOREIGN KEY (owner_id) REFERENCES users(id) ); """) + cur.execute(""" CREATE TABLE IF NOT EXISTS room_members ( room_id INTEGER NOT NULL, user_id INTEGER NOT NULL, - role TEXT NOT NULL DEFAULT 'member', + role TEXT NOT NULL DEFAULT 'member' + CHECK(role IN ('owner', 'admin', 'member')), PRIMARY KEY (room_id, user_id), - FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE, - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE + FOREIGN KEY (room_id) + REFERENCES rooms(id) + ON DELETE CASCADE, + + FOREIGN KEY (user_id) + REFERENCES users(id) + ON DELETE CASCADE ); """) + cur.execute(""" CREATE TABLE IF NOT EXISTS events ( id INTEGER PRIMARY KEY AUTOINCREMENT, + room_id INTEGER NOT NULL, creator_id INTEGER NOT NULL, - type TEXT NOT NULL CHECK(type IN ('private', 'busy', 'proposal')), + type TEXT NOT NULL + CHECK(type IN ('private', 'busy', 'proposal')), + title TEXT NOT NULL, description TEXT, start_time TEXT NOT NULL, end_time TEXT NOT NULL, - visibility TEXT NOT NULL CHECK(visibility IN ('private', 'busy_only', 'room')), + visibility TEXT NOT NULL + CHECK(visibility IN ('private', 'busy_only', 'room')), + status TEXT NOT NULL DEFAULT 'draft' CHECK(status IN ('draft', 'proposed', 'confirmed', 'cancelled')), min_people INTEGER DEFAULT 0, - FOREIGN KEY (room_id) REFERENCES rooms(id) ON DELETE CASCADE, - FOREIGN KEY (creator_id) REFERENCES users(id) + FOREIGN KEY (room_id) + REFERENCES rooms(id) + ON DELETE CASCADE, + + FOREIGN KEY (creator_id) + REFERENCES users(id) ); """) + cur.execute(""" CREATE TABLE IF NOT EXISTS event_signups ( event_id INTEGER NOT NULL, user_id INTEGER NOT NULL, + status TEXT NOT NULL DEFAULT 'going' CHECK(status IN ('going', 'maybe', 'declined')), - PRIMARY KEY (event_id, user_id), + PRIMARY KEY(event_id, user_id), - FOREIGN KEY (event_id) REFERENCES events(id) ON DELETE CASCADE, - FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE + FOREIGN KEY(event_id) + REFERENCES events(id) + ON DELETE CASCADE, + + FOREIGN KEY(user_id) + REFERENCES users(id) + ON DELETE CASCADE ); """) + +# Snellere zoekopdrachten +cur.execute(""" +CREATE INDEX IF NOT EXISTS idx_room_members_user +ON room_members(user_id); +""") + +cur.execute(""" +CREATE INDEX IF NOT EXISTS idx_events_room +ON events(room_id); +""") + + conn.commit() + print("Database aangemaakt.") print("Tabellen:") -for row in cur.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;"): + +for row in cur.execute( + "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;" +): print(" -", row[0]) + conn.close() \ No newline at end of file