from fastapi import FastAPI, Depends, HTTPException from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import PlainTextResponse from pydantic import BaseModel from typing import Literal import sqlite3 from database import fetch_all, fetch_one from auth import ( create_user, verify_user, create_token, get_current_user ) from permissions import has_role from rooms import ( create_room, search_room, accept_room, get_rooms, get_room, get_room_members, leave_room, delete_room as delete_room_db, change_room_name, regenerate_invite_code ) from events import ( create_event, get_user_events, check_event_threshold, delete_event, get_room_events, update_event ) from event_signups import ( respond_to_event, count_confirmations ) from database import get_table_names from auth import ( create_user, verify_user, create_token, get_current_user, get_user ) from events import ( create_event, get_user_events, check_event_threshold, delete_event, get_room_events, update_event, get_event, get_event_room_id, get_event_creator ) from event_signups import ( respond_to_event, count_confirmations, get_event_responses ) class Login(BaseModel): username: str password: str class UserCreate(BaseModel): username: str password: str class EventCreate(BaseModel): room_id: int type: str title: str description: str start_time: str end_time: str visibility: str status: str min_people: int class RoomCreate(BaseModel): room_name: str invite_code: str class RoomSearch(BaseModel): invite_code: str class RoomJoin(BaseModel): invite_code: str class RoomUpdate(BaseModel): new_name: str class EventUpdate(BaseModel): title: str | None = None description: str | None = None start_time: str | None = None end_time: str | None = None min_people: int | None = None app = FastAPI( title="Calendar API", description="Room based agenda system", version="1.0" ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # general @app.get("/") def home(): return { "status": "online", "service": "Calendar API" } @app.get("/database") def database_status(): tables = get_table_names() return { "tables": [table["name"] for table in tables] } # user endpoints @app.post("/create-user") def api_create_user(user: UserCreate): try: user_id = create_user( user.username, user.password ) except sqlite3.IntegrityError: raise HTTPException( status_code=409, detail="username already exists" ) return { "status": "created", "user_id": user_id } @app.post("/login") def api_login(login: Login): user_id = verify_user( login.username, login.password ) if user_id is None: return { "status": "failed" } token = create_token(user_id) return { "status": "success", "access_token": token, "token_type": "bearer" } @app.get("/me") def get_me( user_id: int = Depends(get_current_user) ): user = get_user(user_id) if user is None: raise HTTPException( status_code=404, detail="user not found" ) return dict(user) @app.get("/users/{user_id}") def api_get_user( user_id: int, current_user: int = Depends(get_current_user) ): user = get_user(user_id) if user is None: raise HTTPException( status_code=404, detail="user not found" ) return dict(user) # room endpoints @app.post("/create-room") def api_create_room( room: RoomCreate, user_id: int = Depends(get_current_user) ): try: result = create_room( user_id, room.room_name, room.invite_code ) except sqlite3.IntegrityError: raise HTTPException( status_code=409, detail="invite_code or roomname already exists" ) return { "status": "created", **result } @app.post("/search-room") def api_search_room(room: RoomSearch): room_name = search_room( room.invite_code ) if room_name is None: return { "status": "failed", "reason": "room_not_found" } return { "status": "found", "room_name": room_name } @app.post("/join-room") def api_join_room( room: RoomJoin, user_id: int = Depends(get_current_user) ): result = accept_room( user_id, room.invite_code ) if result is None: return { "status": "failed", "reason": "room_not_found_or_already_member" } return { "status": "joined", **result } @app.get("/rooms") def api_get_rooms( user_id: int = Depends(get_current_user) ): rooms = get_rooms(user_id) return { "rooms": [dict(room) for room in rooms] } @app.get("/rooms/{room_id}") def api_get_room( room_id: int, user_id: int = Depends(get_current_user) ): room = get_room( room_id, user_id ) if room is None: raise HTTPException( status_code=404, detail="room not found or no access" ) return room @app.get("/rooms/{room_id}/members") def api_room_members( room_id: int, user_id: int = Depends(get_current_user) ): if not has_role(room_id, user_id, "member"): raise HTTPException( status_code=403, detail="room member permission required" ) members = get_room_members(room_id) return { "members": [dict(member) for member in members] } @app.get("/rooms/{room_id}/events") def api_room_events( room_id: int, user_id: int = Depends(get_current_user) ): if not has_role(room_id, user_id, "member"): raise HTTPException( status_code=403, detail="room member permission required" ) events = get_room_events(room_id) return { "events": [ dict(event) for event in events ] } # settings @app.patch("/rooms/{room_id}") def update_room( room_id: int, data: RoomUpdate, user_id: int = Depends(get_current_user) ): if not has_role(room_id, user_id, "admin"): raise HTTPException( status_code=403, detail="admin permission required" ) return change_room_name( room_id, data.new_name, user_id ) @app.post("/rooms/{room_id}/invite-code") def update_invite_code( room_id: int, user_id: int = Depends(get_current_user) ): if not has_role(room_id, user_id, "admin"): raise HTTPException( status_code=403, detail="admin permission required" ) return regenerate_invite_code(room_id) @app.delete("/rooms/{room_id}") def delete_room( room_id: int, user_id: int = Depends(get_current_user) ): if not has_role(room_id, user_id, "owner"): raise HTTPException( status_code=403, detail="owner permission required" ) return delete_room_db(room_id) @app.delete("/rooms/{room_id}/members/me") def api_leave_room( room_id: int, user_id: int = Depends(get_current_user) ): if not has_role(room_id, user_id, "member"): raise HTTPException( status_code=403, detail="not a room member" ) leave_room( room_id, user_id ) return { "status": "left" } # events @app.post("/events") def api_create_event( event: EventCreate, user_id: int = Depends(get_current_user) ): if not has_role(event.room_id, user_id, "member"): raise HTTPException( status_code=403, detail="room member permission required" ) 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 = get_user_events(user_id) return { "events": [dict(event) for event in events] } @app.get("/events/{event_id}") def api_get_event( event_id: int, user_id: int = Depends(get_current_user) ): event = get_event(event_id) if event is None: raise HTTPException( status_code=404, detail="event not found" ) if not has_role(event["room_id"], user_id, "member"): raise HTTPException( status_code=403, detail="room member permission required" ) return dict(event) @app.post("/events/{event_id}/respond") def respond_event( event_id: int, status: Literal["going", "maybe", "declined"], user_id: int = Depends(get_current_user) ): room_id = get_event_room_id(event_id) if room_id is None: raise HTTPException( status_code=404, detail="event not found" ) if not has_role(room_id, user_id, "member"): raise HTTPException( status_code=403, detail="room member permission required" ) respond_to_event( event_id, user_id, status ) check_event_threshold(event_id) return { "status": "response_saved", "going": count_confirmations(event_id) } @app.get("/events/{event_id}/responses") def api_event_responses( event_id: int, user_id: int = Depends(get_current_user) ): room_id = get_event_room_id(event_id) if room_id is None: raise HTTPException( status_code=404, detail="event not found" ) if not has_role(room_id, user_id, "member"): raise HTTPException( status_code=403, detail="room member permission required" ) responses = get_event_responses(event_id) return { "responses": [dict(response) for response in responses] } # more events @app.patch("/events/{event_id}") def api_update_event( event_id: int, event: EventUpdate, user_id: int = Depends(get_current_user) ): creator_id = get_event_creator(event_id) if creator_id is None: raise HTTPException( status_code=404, detail="event not found" ) if creator_id != user_id: raise HTTPException( status_code=403, detail="event owner permission required" ) update_event( event_id, event.title, event.description, event.start_time, event.end_time, event.min_people ) return { "status": "updated" } @app.delete("/events/{event_id}") def api_delete_event( event_id: int, user_id: int = Depends(get_current_user) ): creator_id = get_event_creator(event_id) if creator_id is None: raise HTTPException( status_code=404, detail="event not found" ) if creator_id != user_id: raise HTTPException( status_code=403, detail="event owner permission required" ) delete_event(event_id) return { "status": "deleted" } # easter egg @app.get("/ascii-art", response_class=PlainTextResponse) def ascii_art(): return """ ██████╗ █████╗ ██╗ ███████╗███╗ ██╗██████╗ █████╗ ██████╗ ██╔════╝██╔══██╗██║ ██╔════╝████╗ ██║██╔══██╗██╔══██╗██╔══██╗ ██║ ███████║██║ █████╗ ██╔██╗ ██║██║ ██║███████║██████╔╝ ██║ ██╔══██║██║ ██╔══╝ ██║╚██╗██║██║ ██║██╔══██║██╔══██╗ ╚██████╗██║ ██║███████╗███████╗██║ ╚████║██████╔╝██║ ██║██║ ██║ ╚═════╝╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═══╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ status: online "You are a stinky Poo" - Ben de Roo, programmer """