-
Notifications
You must be signed in to change notification settings - Fork 293
Expand file tree
/
Copy pathusers.py
More file actions
211 lines (159 loc) · 7.72 KB
/
users.py
File metadata and controls
211 lines (159 loc) · 7.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
from typing import Annotated, Any
from fastapi import APIRouter, Depends, Request
from fastcrud import PaginatedListResponse, compute_offset, paginated_response
from sqlalchemy.ext.asyncio import AsyncSession
from ...api.dependencies import get_current_superuser, get_current_user
from ...core.config import settings
from ...core.db.database import async_get_db
from ...core.exceptions.http_exceptions import DuplicateValueException, ForbiddenException, NotFoundException
from ...core.security import blacklist_token, get_password_hash, oauth2_scheme
from ...crud.crud_rate_limit import crud_rate_limits
from ...crud.crud_tier import crud_tiers
from ...crud.crud_users import crud_users
from ...schemas.tier import TierRead
from ...schemas.user import UserCreate, UserCreateInternal, UserRead, UserTierUpdate, UserUpdate
router = APIRouter(tags=["users"])
if settings.ENABLE_PASSWORD_AUTH: # If password auth is not enable there should be no way to create users via the API
@router.post("/user", response_model=UserRead, status_code=201)
async def write_user(
request: Request, user: UserCreate, db: Annotated[AsyncSession, Depends(async_get_db)]
) -> dict[str, Any]:
created_user = await write_user_internal(user=user, db=db)
return created_user
async def write_user_internal(user: UserCreate | UserCreateInternal, db: AsyncSession) -> dict[str, Any]:
email_row = await crud_users.exists(db=db, email=user.email)
if email_row:
raise DuplicateValueException("Email is already registered")
username_row = await crud_users.exists(db=db, username=user.username)
if username_row:
raise DuplicateValueException("Username not available")
if isinstance(user, UserCreate):
user_internal_dict = user.model_dump()
user_internal_dict["hashed_password"] = get_password_hash(password=user_internal_dict["password"])
del user_internal_dict["password"]
user = UserCreateInternal(**user_internal_dict)
created_user = await crud_users.create(db=db, object=user, schema_to_select=UserRead)
if created_user is None:
raise NotFoundException("Failed to create user")
return created_user
@router.get("/users", response_model=PaginatedListResponse[UserRead])
async def read_users(
request: Request, db: Annotated[AsyncSession, Depends(async_get_db)], page: int = 1, items_per_page: int = 10
) -> dict:
users_data = await crud_users.get_multi(
db=db,
offset=compute_offset(page, items_per_page),
limit=items_per_page,
is_deleted=False,
)
response: dict[str, Any] = paginated_response(crud_data=users_data, page=page, items_per_page=items_per_page)
return response
@router.get("/user/me/", response_model=UserRead)
async def read_users_me(request: Request, current_user: Annotated[dict, Depends(get_current_user)]) -> dict:
return current_user
@router.get("/user/{username}", response_model=UserRead)
async def read_user(
request: Request, username: str, db: Annotated[AsyncSession, Depends(async_get_db)]
) -> dict[str, Any]:
db_user = await crud_users.get(db=db, username=username, is_deleted=False, schema_to_select=UserRead)
if db_user is None:
raise NotFoundException("User not found")
return db_user
@router.patch("/user/{username}")
async def patch_user(
request: Request,
values: UserUpdate,
username: str,
current_user: Annotated[dict, Depends(get_current_user)],
db: Annotated[AsyncSession, Depends(async_get_db)],
) -> dict[str, str]:
db_user = await crud_users.get(db=db, username=username)
if db_user is None:
raise NotFoundException("User not found")
db_username = db_user["username"]
db_email = db_user["email"]
if db_username != current_user["username"]:
raise ForbiddenException()
if values.email is not None and values.email != db_email:
if await crud_users.exists(db=db, email=values.email):
raise DuplicateValueException("Email is already registered")
if values.username is not None and values.username != db_username:
if await crud_users.exists(db=db, username=values.username):
raise DuplicateValueException("Username not available")
await crud_users.update(db=db, object=values, username=username)
return {"message": "User updated"}
@router.delete("/user/{username}")
async def erase_user(
request: Request,
username: str,
current_user: Annotated[dict, Depends(get_current_user)],
db: Annotated[AsyncSession, Depends(async_get_db)],
token: str = Depends(oauth2_scheme),
) -> dict[str, str]:
db_user = await crud_users.get(db=db, username=username, schema_to_select=UserRead)
if not db_user:
raise NotFoundException("User not found")
if username != current_user["username"]:
raise ForbiddenException()
await crud_users.delete(db=db, username=username)
await blacklist_token(token=token, db=db)
return {"message": "User deleted"}
@router.delete("/db_user/{username}", dependencies=[Depends(get_current_superuser)])
async def erase_db_user(
request: Request,
username: str,
db: Annotated[AsyncSession, Depends(async_get_db)],
token: str = Depends(oauth2_scheme),
) -> dict[str, str]:
db_user = await crud_users.exists(db=db, username=username)
if not db_user:
raise NotFoundException("User not found")
await crud_users.db_delete(db=db, username=username)
await blacklist_token(token=token, db=db)
return {"message": "User deleted from the database"}
@router.get("/user/{username}/rate_limits", dependencies=[Depends(get_current_superuser)])
async def read_user_rate_limits(
request: Request, username: str, db: Annotated[AsyncSession, Depends(async_get_db)]
) -> dict[str, Any]:
db_user = await crud_users.get(db=db, username=username, schema_to_select=UserRead)
if db_user is None:
raise NotFoundException("User not found")
user_dict = dict(db_user)
if db_user["tier_id"] is None:
user_dict["tier_rate_limits"] = []
return user_dict
db_tier = await crud_tiers.get(db=db, id=db_user["tier_id"], schema_to_select=TierRead)
if db_tier is None:
raise NotFoundException("Tier not found")
db_rate_limits = await crud_rate_limits.get_multi(db=db, tier_id=db_tier["id"])
user_dict["tier_rate_limits"] = db_rate_limits["data"]
return user_dict
@router.get("/user/{username}/tier")
async def read_user_tier(
request: Request, username: str, db: Annotated[AsyncSession, Depends(async_get_db)]
) -> dict | None:
db_user = await crud_users.get(db=db, username=username, schema_to_select=UserRead)
if db_user is None:
raise NotFoundException("User not found")
if db_user["tier_id"] is None:
return None
db_tier = await crud_tiers.get(db=db, id=db_user["tier_id"], schema_to_select=TierRead)
if not db_tier:
raise NotFoundException("Tier not found")
user_dict = dict(db_user)
tier_dict = dict(db_tier)
for key, value in tier_dict.items():
user_dict[f"tier_{key}"] = value
return user_dict
@router.patch("/user/{username}/tier", dependencies=[Depends(get_current_superuser)])
async def patch_user_tier(
request: Request, username: str, values: UserTierUpdate, db: Annotated[AsyncSession, Depends(async_get_db)]
) -> dict[str, str]:
db_user = await crud_users.get(db=db, username=username, schema_to_select=UserRead)
if db_user is None:
raise NotFoundException("User not found")
db_tier = await crud_tiers.get(db=db, id=values.tier_id, schema_to_select=TierRead)
if db_tier is None:
raise NotFoundException("Tier not found")
await crud_users.update(db=db, object=values.model_dump(), username=username)
return {"message": f"User {db_user['name']} Tier updated"}