Skip to content

Commit a616888

Browse files
committed
fix for a few bugs
1 parent d0eb0f8 commit a616888

File tree

5 files changed

+77
-27
lines changed

5 files changed

+77
-27
lines changed

src/app/api/dependencies.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from app.core.database import async_get_db
1010
from app.crud.crud_users import get_user_by_email, get_user_by_username
1111
from app.core.models import TokenData
12+
from app.models.user import User
1213

1314
async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)], db: Annotated[AsyncSession, Depends(async_get_db)]):
1415
credentials_exception = HTTPException(
@@ -38,3 +39,10 @@ async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)], db: An
3839
raise HTTPException(status_code=400, detai="User deleted")
3940

4041
return user
42+
43+
def get_current_superuser(current_user: Annotated[User, Depends(get_current_user)]) -> User:
44+
if not current_user.is_superuser:
45+
raise HTTPException(
46+
status_code=403, detail="The user doesn't have enough privileges"
47+
)
48+
return current_user

src/app/api/v1/users.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import fastapi
66

77
from app.schemas.user import UserCreate, UserUpdate, UserRead, UserBase
8-
from app.api.dependencies import get_current_user
8+
from app.api.dependencies import get_current_user, get_current_superuser
99
from app.core.database import async_get_db
1010
from ...crud.crud_users import (
1111
get_user,
@@ -98,3 +98,17 @@ async def erase_user(
9898

9999
db_user = await delete_user(db=db, id=id, user=db_user)
100100
return db_user
101+
102+
103+
@router.delete("/db_user/{id}")
104+
async def erase_db_user(
105+
id: int,
106+
current_superuser: Annotated[UserRead, Depends(get_current_superuser)],
107+
db: AsyncSession = Depends(async_get_db)
108+
):
109+
db_user = await get_user(db=db, id=id)
110+
if db_user is None:
111+
raise HTTPException(status_code=404, detail="User not found")
112+
113+
db_user = await delete_user(db=db, id=id, user=db_user)
114+
return db_user

src/app/crud/crud_users.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from datetime import datetime
2+
from typing import Union, Dict, Any
23

34
from sqlalchemy.ext.asyncio import AsyncSession
45
from sqlalchemy import select, delete, update
56
from passlib.context import CryptContext
67
from sqlalchemy import and_
78

9+
810
from app.schemas.user import (
911
UserCreate,
1012
UserUpdate,
@@ -56,23 +58,23 @@ async def get_users(db: AsyncSession):
5658
async def update_user(
5759
db: AsyncSession,
5860
id: int,
59-
values: UserUpdate,
61+
values: Union[UserUpdate, Dict[str, Any]],
6062
user: User | None = None
6163
):
6264
user = user or await get_user(db=db, id=id)
6365
if user is not None:
64-
internal_values = UserUpdateInternal(
65-
name=values.name,
66-
username=values.username,
67-
email=values.email,
68-
profile_image_url=values.profile_image_url,
69-
updated_at=datetime.utcnow()
70-
)
71-
query = update(User).where(User.id == id).values(internal_values.dict(exclude_unset=True))
72-
await db.execute(query)
73-
await db.commit()
74-
await db.refresh(user)
75-
66+
if isinstance(values, dict):
67+
update_data = values
68+
else:
69+
update_data = values.model_dump(exclude_unset=True)
70+
71+
update_data.update({"updated_at": datetime.utcnow()})
72+
for field in user.__dict__:
73+
if field in update_data:
74+
setattr(user, field, update_data[field])
75+
db.add(user)
76+
await db.commit()
77+
7678
return user
7779

7880
async def delete_user(
@@ -86,7 +88,7 @@ async def delete_user(
8688
is_deleted=True,
8789
deleted_at=datetime.utcnow()
8890
)
89-
query = update(User).where(User.id == id).values(values.dict(exclude_unset=True))
91+
query = update(User).where(User.id == id).values(values.model_dump())
9092
await db.execute(query)
9193
await db.commit()
9294
await db.refresh(user)

src/app/models/user.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class User(Base):
1818
email: Mapped[str] = mapped_column(String(50), unique=True, index=True)
1919
hashed_password: Mapped[str] = mapped_column(String)
2020

21-
profile_image_url: Mapped[str] = mapped_column(String, default=None)
21+
profile_image_url: Mapped[str] = mapped_column(String, default="https://profileimageurl.com")
2222
uuid: Mapped[uuid_pkg.UUID] = mapped_column(
2323
default_factory=uuid_pkg.uuid4, primary_key=True, unique=True
2424
)

src/app/schemas/user.py

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
from typing import Annotated
1+
from typing import Annotated, Optional
22
from datetime import datetime
33

4-
from pydantic import BaseModel, EmailStr, StringConstraints, Field, HttpUrl
4+
from pydantic import BaseModel, EmailStr, Field, HttpUrl, ConfigDict
55

66
from app.core.models import UUIDModel, TimestampModel, PersistentDeletion
77

@@ -27,11 +27,16 @@ class UserBase(BaseModel):
2727

2828

2929
class User(TimestampModel, UserBase, UUIDModel, PersistentDeletion):
30-
profile_image_url: Annotated[HttpUrl, Field(default="profileimageurl.com")]
30+
profile_image_url: Annotated[
31+
str,
32+
Field(default="https://profileimageurl.com")
33+
]
3134
hashed_password: str
35+
is_superuser: bool = False
3236

3337

3438
class UserRead(BaseModel):
39+
id: int
3540
name: Annotated[
3641
str,
3742
Field(
@@ -44,10 +49,12 @@ class UserRead(BaseModel):
4449
min_length=2, max_length=20, pattern=r"^[a-z0-9]+$", examples=["userson"]
4550
)
4651
]
47-
profile_image_url: Annotated[HttpUrl | None, Field()]
52+
profile_image_url: str
4853

4954

5055
class UserCreate(UserBase):
56+
model_config = ConfigDict(extra='forbid')
57+
5158
password: Annotated[
5259
str,
5360
Field(
@@ -57,32 +64,51 @@ class UserCreate(UserBase):
5764

5865

5966
class UserUpdate(BaseModel):
67+
model_config = ConfigDict(extra='forbid')
68+
6069
name: Annotated[
61-
str,
70+
Optional[str],
6271
Field(
63-
min_length=2, max_length=30, examples=["User Userson"], default=None
72+
min_length=2,
73+
max_length=30,
74+
examples=["User Userberg"],
75+
default=None
6476
)
6577
]
6678
username: Annotated[
67-
str,
79+
Optional[str],
6880
Field(
69-
min_length=2, max_length=20, pattern=r"^[a-z0-9]+$", examples=["userson"], default=None
81+
min_length=2,
82+
max_length=20,
83+
pattern=r"^[a-z0-9]+$",
84+
examples=["userberg"],
85+
default=None
7086
)
7187
]
7288
email: Annotated[
73-
EmailStr,
89+
Optional[EmailStr],
7490
Field(
75-
examples=["[email protected]"], default=None
91+
examples=["[email protected]"],
92+
default=None
93+
)
94+
]
95+
profile_image_url: Annotated[
96+
Optional[str],
97+
Field(
98+
pattern=r"^((http|https)://)[-a-zA-Z0-9@:%._\\+~#?&//=]{2,256}\\.[a-z]{2,6}\\b([-a-zA-Z0-9@:%._\\+~#?&//=]*)$",
99+
examples=["https://profileimageurl.com"],
100+
default=None
76101
)
77102
]
78-
profile_image_url: Annotated[HttpUrl | None, Field(default=None)]
79103

80104

81105
class UserUpdateInternal(UserUpdate):
82106
updated_at: datetime
83107

84108

85109
class UserDelete(BaseModel):
110+
model_config = ConfigDict(extra='forbid')
111+
86112
is_deleted: bool
87113
deleted_at: datetime
88114

0 commit comments

Comments
 (0)