Skip to content

Commit f3e2612

Browse files
perf: User api
1 parent 5e08719 commit f3e2612

File tree

10 files changed

+122
-48
lines changed

10 files changed

+122
-48
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ [email protected]
1313
FIRST_SUPERUSER_PASSWORD=123456 # Change this to your pwd
1414

1515
TOKEN_KEY="X-SQLBOT-TOKEN"
16+
DEFAULT_PWD="SQLBot@123456"
1617

1718
# Postgres
1819
POSTGRES_SERVER=localhost

backend/apps/system/api/user.py

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
from fastapi import APIRouter, Depends, Request
2-
from apps.system.crud.user import get_user_info
3-
from apps.system.models.user import user_grid
4-
from apps.system.schemas.system_schema import UserLanguage
1+
from fastapi import APIRouter
2+
from apps.system.crud.user import get_db_user, get_user_info
3+
from apps.system.models.user import UserModel
4+
from apps.system.schemas.system_schema import UserCreator, UserEditor, UserGrid, UserLanguage
55
from common.core.deps import CurrentUser, SessionDep
66
from common.core.pagination import Paginator
77
from common.core.schemas import PaginatedResponse, PaginationParams
8+
from common.utils.time import get_timestamp
89

910
router = APIRouter(tags=["user"], prefix="/user")
1011

@@ -18,7 +19,7 @@ async def user_info(session: SessionDep, current_user: CurrentUser):
1819
return db_user
1920

2021

21-
@router.get("/pager/{pageNum}/{pageSize}", response_model=PaginatedResponse[user_grid])
22+
@router.get("/pager/{pageNum}/{pageSize}", response_model=PaginatedResponse[UserGrid])
2223
async def pager(
2324
session: SessionDep,
2425
pageNum: int,
@@ -28,16 +29,44 @@ async def pager(
2829
paginator = Paginator(session)
2930
filters = {}
3031
return await paginator.get_paginated_response(
31-
model=user_grid,
32+
model=UserModel,
3233
pagination=pagination,
3334
**filters)
35+
36+
@router.get("/{id}", response_model=UserEditor)
37+
async def query(session: SessionDep, id: int) -> UserEditor:
38+
db_user: UserModel = get_db_user(session = session, user_id = id)
39+
return db_user
40+
41+
@router.post("")
42+
async def create(session: SessionDep, creator: UserCreator):
43+
data = creator.model_dump(exclude_unset=True)
44+
user_model = UserModel.model_validate(data)
45+
#user_model.create_time = get_timestamp()
46+
user_model.language = "zh-CN"
47+
session.add(user_model)
48+
session.commit()
49+
50+
@router.put("")
51+
async def update(session: SessionDep, editor: UserEditor):
52+
user_model: UserModel = get_db_user(session = session, user_id = editor.id)
53+
data = editor.model_dump(exclude_unset=True)
54+
user_model.sqlmodel_update(data)
55+
session.add(user_model)
56+
session.commit()
57+
58+
@router.delete("/{id}")
59+
async def delete(session: SessionDep, id: int):
60+
user_model: UserModel = get_db_user(session = session, user_id = id)
61+
session.delete(user_model)
62+
session.commit()
3463

3564
@router.put("/language")
3665
async def langChange(session: SessionDep, current_user: CurrentUser, language: UserLanguage):
3766
lang = language.language
3867
if lang not in ["zh-CN", "en"]:
3968
return {"message": "Language not supported"}
40-
db_user = get_user_info(session=session, user_id=current_user.id)
69+
db_user = session.get(UserModel, current_user.id)
4170
if not db_user:
4271
return {"message": "User not found"}
4372
db_user.language = lang

backend/apps/system/crud/user.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
11

22
from sqlmodel import Session, select
3-
from ..models.user import sys_user, user_grid
3+
4+
from apps.system.schemas.system_schema import BaseUserDTO
5+
from ..models.user import UserModel
46
from common.core.security import verify_md5pwd
57

6-
def get_user_by_account(*, session: Session, account: str) -> sys_user | None:
7-
#statement = select(sys_user).where(sys_user.account == account)
8-
statement = select(user_grid.id, user_grid.account, user_grid.oid, user_grid.password).where(user_grid.account == account)
9-
session_user = session.exec(statement).first()
10-
result_user = sys_user.model_validate(session_user)
11-
return result_user
8+
def get_db_user(*, session: Session, user_id: int) -> UserModel:
9+
db_user = session.get(UserModel, user_id)
10+
if not db_user:
11+
raise RuntimeError("user not exist")
12+
return db_user
1213

13-
def get_user_info(*, session: Session, user_id: int) -> sys_user | None:
14-
db_user = session.get(user_grid, user_id)
14+
def get_user_by_account(*, session: Session, account: str) -> BaseUserDTO | None:
15+
statement = select(UserModel).where(UserModel.account == account)
16+
db_user = session.exec(statement).first()
1517
if not db_user:
1618
return None
17-
return db_user
19+
return BaseUserDTO.model_validate(db_user.model_dump())
20+
21+
def get_user_info(*, session: Session, user_id: int) -> BaseUserDTO | None:
22+
db_user = get_db_user(session = session, user_id = user_id)
23+
return BaseUserDTO.model_validate(db_user.model_dump())
1824

19-
def authenticate(*, session: Session, account: str, password: str) -> sys_user | None:
25+
def authenticate(*, session: Session, account: str, password: str) -> BaseUserDTO | None:
2026
db_user = get_user_by_account(session=session, account=account)
2127
if not db_user:
2228
return None

backend/apps/system/models/user.py

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
11

22
from typing import Optional
3-
from sqlmodel import SQLModel, Field
3+
from sqlmodel import BigInteger, SQLModel, Field
4+
5+
from common.core.models import SnowflakeBase
6+
from common.core.security import default_md5_pwd
7+
from common.utils.time import get_timestamp
48

5-
class sys_user(SQLModel):
6-
id: int = Field(primary_key=True, index=True)
7-
account: str = Field(max_length=255, unique=True)
8-
password: str = Field(max_length=255)
9-
oid: int = Field(default=1)
109

11-
def to_dict(self):
12-
return {
13-
"id": self.id,
14-
"account": self.account,
15-
"oid": self.oid
16-
}
17-
18-
class user_grid(sys_user, table=True):
19-
__tablename__ = "sys_user"
20-
name: str
21-
email: str
22-
status: int
23-
create_time: int
24-
language: str = Field(max_length=255, default="zh-CN")
2510

11+
class BaseUserPO(SQLModel):
12+
account: str = Field(max_length=255, unique=True)
13+
oid: int = Field(nullable=False, sa_type=BigInteger())
14+
name: str = Field(max_length=255, unique=True)
15+
password: str = Field(default_factory=default_md5_pwd, max_length=255)
16+
email: str = Field(max_length=255)
17+
status: int = Field(default=0, nullable=False)
18+
create_time: int = Field(default_factory=get_timestamp, sa_type=BigInteger(), nullable=False)
19+
language: str = Field(max_length=255, default="zh-CN")
20+
21+
class UserModel(SnowflakeBase, BaseUserPO, table=True):
22+
__tablename__ = "sys_user"
23+
Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,39 @@
11
from pydantic import BaseModel
22

3+
from common.core.schemas import BaseCreatorDTO
4+
35
class model_status(BaseModel):
46
status: bool
57
ids: list[int]
68

79

810
class UserLanguage(BaseModel):
9-
language: str
11+
language: str
12+
13+
14+
class BaseUser(BaseModel):
15+
account: str
16+
oid: int
17+
18+
class BaseUserDTO(BaseUser, BaseCreatorDTO):
19+
password: str
20+
def to_dict(self):
21+
return {
22+
"id": self.id,
23+
"account": self.account,
24+
"oid": self.oid
25+
}
26+
27+
28+
29+
class UserCreator(BaseUser):
30+
name: str
31+
email: str
32+
status: int = 1
33+
34+
class UserEditor(UserCreator, BaseCreatorDTO):
35+
pass
36+
37+
class UserGrid(UserEditor):
38+
create_time: int
39+
language: str = "zh-CN"

backend/common/core/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ def all_cors_origins(self) -> list[str]:
5858
POSTGRES_DB: str = ""
5959

6060
TOKEN_KEY: str
61+
DEFAULT_PWD: str
6162

6263
@computed_field # type: ignore[prop-decorator]
6364
@property

backend/common/core/deps.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
from fastapi import Depends, HTTPException, status
55
# from fastapi.security import OAuth2PasswordBearer
66
from jwt.exceptions import InvalidTokenError
7-
from pydantic import BaseModel, ValidationError
8-
from sqlmodel import Session, select
7+
from pydantic import ValidationError
8+
from sqlmodel import Session
9+
from apps.system.crud.user import get_db_user
10+
from apps.system.schemas.system_schema import BaseUserDTO
911
from common.core.schemas import TokenPayload, XOAuth2PasswordBearer
1012
from common.core import security
1113
from common.core.config import settings
1214
from common.core.db import get_session
13-
from apps.system.models.user import sys_user, user_grid
15+
from apps.system.models.user import UserModel
1416
reusable_oauth2 = XOAuth2PasswordBearer(
1517
tokenUrl=f"{settings.API_V1_STR}/login/access-token"
1618
)
@@ -21,7 +23,7 @@
2123
SessionDep = Annotated[Session, Depends(get_session)]
2224
TokenDep = Annotated[str, Depends(reusable_oauth2)]
2325

24-
async def get_current_user(session: SessionDep, token: TokenDep) -> sys_user:
26+
async def get_current_user(session: SessionDep, token: TokenDep) -> BaseUserDTO:
2527
try:
2628
payload = jwt.decode(
2729
token, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
@@ -32,17 +34,16 @@ async def get_current_user(session: SessionDep, token: TokenDep) -> sys_user:
3234
status_code=status.HTTP_403_FORBIDDEN,
3335
detail="Could not validate credentials",
3436
)
35-
statement = select(user_grid.id, user_grid.account, user_grid.oid, user_grid.password).where(user_grid.id == token_data.id)
36-
session_user = session.exec(statement).first()
37+
session_user: UserModel = get_db_user(session = session, user_id = token_data.id)
3738
if not session_user:
3839
raise HTTPException(status_code=404, detail="User not found")
39-
user = sys_user.model_validate(session_user)
40+
user = BaseUserDTO.model_validate(session_user.model_dump())
4041
if not user:
4142
raise HTTPException(status_code=404, detail="User not found")
4243
""" if not user.is_active:
4344
raise HTTPException(status_code=400, detail="Inactive user") """
4445
return user
45-
CurrentUser = Annotated[sys_user, Depends(get_current_user)]
46+
CurrentUser = Annotated[BaseUserDTO, Depends(get_current_user)]
4647

4748

4849

backend/common/core/security.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,10 @@ def md5pwd(password: str) -> str:
3535

3636
def verify_md5pwd(plain_password: str, md5_password: str) -> bool:
3737
return md5pwd(plain_password) == md5_password
38+
39+
def default_pwd() -> str:
40+
return settings.DEFAULT_PWD
41+
42+
def default_md5_pwd() -> str:
43+
pwd = default_pwd()
44+
return md5pwd(pwd)

backend/common/utils/time.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from datetime import datetime
22

33

4-
def get_timestamp():
4+
def get_timestamp() -> int:
55
dt_millis = int(datetime.now().timestamp() * 1000)
66
return dt_millis

backend/common/utils/whitelist.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"/system/config/key",
2727
"/images/*",
2828
"/sse",
29+
"/user*"
2930
]
3031

3132
class WhitelistChecker:

0 commit comments

Comments
 (0)