11"""Users API routes"""
22
33# import asyncio
4- # import datetime
54
65# import asyncpg
76# import orjson
8- # import sqlalchemy
9- from fastapi import APIRouter # , Depends, HTTPException, Request
10- # from pydantic import BaseModel
11- # from sqlalchemy.ext.asyncio import AsyncSession
7+ import sqlalchemy
8+ from fastapi import APIRouter , Request , Depends , Response
9+ from fastapi .exceptions import HTTPException
10+ from pydantic import BaseModel
11+ from typing import Optional
12+ from sqlalchemy .ext .asyncio import AsyncSession
13+ from datetime import datetime , timezone , timedelta
1214# from sqlalchemy.orm import selectinload
1315
14- # from api.auth import require_auth
15- # from db import get_db, engine
16- # from models.user import User, UserProject
16+ from api .auth . main import require_auth , generate_session_id , Permission # type: ignore
17+ from db import get_db
18+ from models .user import User
1719
1820
1921router = APIRouter ()
2022
23+ class CreateUserRequest (BaseModel ):
2124
25+ email : str
26+
27+ class UpdateUserRequest (BaseModel ):
28+
29+ id : int
30+ email : Optional [str ]
31+
32+ class DeleteUserRequest (BaseModel ):
33+
34+ id : int
35+ email : str # for silly, maybe not needed...
36+
37+ # these are all simple ahh db operations...too simple...am i doing something wrong...
2238# @limiter.limit("3/day")
23- async def create_user ():
39+ @router .post ("/api/users/create" )
40+ async def create_user (
41+ request : Request ,
42+ create_request : CreateUserRequest ,
43+ session : AsyncSession = Depends (get_db )
44+ ):
2445 """Create a new user"""
25- # TODO: implement create user functionality
46+ # TODO: swap out the version in /api/auth/main.py with this one
47+ # TODO: check if this is secure or even better than the current implementation
48+ new_user = User (email = create_request .email )
49+
50+ session .add (new_user )
51+ await session .commit ()
52+ await session .refresh (new_user )
2653
54+ return {"success" : True }
2755
56+ # there'll be a second endpoint for admins to update
2857# @protect
29- async def update_user ():
58+ @router .post ("api/users/update" )
59+ @require_auth
60+ async def update_user (
61+ request : Request ,
62+ update_request : UpdateUserRequest ,
63+ response : Response ,
64+ session : AsyncSession = Depends (get_db )
65+ ):
3066 """Update user details"""
31- # TODO: implement update user functionality
67+
68+ user_email = request .state .user ["sub" ]
69+
70+ user_raw = await session .execute (
71+ sqlalchemy .select (User ).where (
72+ User .id == update_request .id
73+ )
74+ )
75+
76+ user = user_raw .scalar_one_or_none ()
77+
78+ if user is None :
79+ raise HTTPException (401 ) # user doesn't exist
80+
81+ if user .email != user_email :
82+ raise HTTPException (401 ) # they're trying to update someone elses email, no!
83+
84+ update_data = update_request .model_dump (exclude_unset = True , exclude = {"id" })
85+
86+ for field , value in update_data .items ():
87+ setattr (user , field , value )
88+
89+ if update_request .email is not None :
90+ ret_jwt = await generate_session_id (update_request .email )
91+ response .set_cookie (
92+ key = "sessionId" , value = ret_jwt , httponly = True , secure = True , max_age = 604800
93+ )
94+
95+ await session .commit ()
96+ await session .refresh (user ) #TODO: figure out why we're refreshing every time and if its needed
97+
98+ return {"success" : True }
3299
33100
34101# @protect
35- async def get_user ():
102+ async def get_user (
103+ request : Request ,
104+ create_request : CreateUserRequest ,
105+ session : AsyncSession = Depends (get_db )
106+ ):
36107 """Get user details"""
37108 # TODO: implement get user functionality
109+ #TODO: Figure out how many users this allows (just yourself? everyone? a subset?)
38110
39111
40112# @protect
41- async def delete_user (): # can only delete their own user!!! don't let them delete other users!!!
113+ @router .post ("/api/users/delete" )
114+ @require_auth
115+ async def delete_user (
116+ request : Request ,
117+ delete_request : DeleteUserRequest ,
118+ # response: Response,
119+ session : AsyncSession = Depends (get_db )
120+ ): # can only delete their own user!!! don't let them delete other users!!!
42121 """Delete a user account"""
43122 # TODO: implement delete user functionality
44123
124+ user_email = request .state .user ["sub" ]
125+
126+ user_raw = await session .execute (
127+ sqlalchemy .select (User ).where (
128+ User .id == delete_request .id ,
129+ User .email == delete_request .email
130+ )
131+ )
132+
133+ user = user_raw .scalar_one_or_none ()
134+
135+ if user is None :
136+ raise HTTPException (401 ) # user doesn't exist
137+
138+ if user .email != user_email :
139+ raise HTTPException (401 ) # they're trying to delete someone elses email, no!
140+
141+ user .marked_for_deletion = True
142+ user .date_for_deletion = datetime .now (timezone .utc ) + timedelta (days = 30 )
143+
144+ await session .commit ()
145+ await session .refresh (user )
146+
147+ return {"success" : True }
148+
149+
150+
45151
46152# disabled for 30 days, no login -> delete
47153
@@ -50,6 +156,7 @@ async def delete_user(): # can only delete their own user!!! don't let them del
50156async def is_pending_deletion ():
51157 """Check if a user account is pending deletion"""
52158 # TODO: implement is pending deletion functionality
159+ # TODO: figure out how we want to decide if they're able to get deletion status
53160
54161
55162# async def run():
0 commit comments