中文汉化
Deploy to Staging / deploy (push) Has been cancelled
Conflict detector / main (push) Has been cancelled
Lint Backend / lint-backend (push) Has been cancelled
Playwright Tests / changes (push) Has been cancelled
Test Backend / test-backend (push) Has been cancelled
Test Docker Compose / test-docker-compose (push) Has been cancelled
Playwright Tests / test-playwright (1, 4) (push) Has been cancelled
Playwright Tests / test-playwright (2, 4) (push) Has been cancelled
Playwright Tests / test-playwright (3, 4) (push) Has been cancelled
Playwright Tests / test-playwright (4, 4) (push) Has been cancelled
Playwright Tests / merge-playwright-reports (push) Has been cancelled
Playwright Tests / alls-green-playwright (push) Has been cancelled
Issue Manager / issue-manager (push) Has been cancelled
Deploy to Staging / deploy (push) Has been cancelled
Conflict detector / main (push) Has been cancelled
Lint Backend / lint-backend (push) Has been cancelled
Playwright Tests / changes (push) Has been cancelled
Test Backend / test-backend (push) Has been cancelled
Test Docker Compose / test-docker-compose (push) Has been cancelled
Playwright Tests / test-playwright (1, 4) (push) Has been cancelled
Playwright Tests / test-playwright (2, 4) (push) Has been cancelled
Playwright Tests / test-playwright (3, 4) (push) Has been cancelled
Playwright Tests / test-playwright (4, 4) (push) Has been cancelled
Playwright Tests / merge-playwright-reports (push) Has been cancelled
Playwright Tests / alls-green-playwright (push) Has been cancelled
Issue Manager / issue-manager (push) Has been cancelled
This commit is contained in:
+15
-19
@@ -4,16 +4,15 @@ from logging.config import fileConfig
|
||||
from alembic import context
|
||||
from sqlalchemy import engine_from_config, pool
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
# 这是 Alembic 配置对象,提供对正在使用的 .ini 文件中值的访问。
|
||||
config = context.config
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
# 解析配置文件以进行 Python 日志记录。
|
||||
# 这行代码用于设置日志记录器。
|
||||
fileConfig(config.config_file_name)
|
||||
|
||||
# add your model's MetaData object here
|
||||
# for 'autogenerate' support
|
||||
# 在此处添加模型的 MetaData 对象
|
||||
# 以支持 'autogenerate'
|
||||
# from myapp import mymodel
|
||||
# target_metadata = mymodel.Base.metadata
|
||||
# target_metadata = None
|
||||
@@ -23,10 +22,9 @@ from app.core.config import settings # noqa
|
||||
|
||||
target_metadata = SQLModel.metadata
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
# 根据 env.py 的需要,可以从配置中获取其他值:
|
||||
# my_important_option = config.get_main_option("my_important_option")
|
||||
# ... etc.
|
||||
# ... 等等。
|
||||
|
||||
|
||||
def get_url():
|
||||
@@ -34,15 +32,13 @@ def get_url():
|
||||
|
||||
|
||||
def run_migrations_offline():
|
||||
"""Run migrations in 'offline' mode.
|
||||
"""在"离线"模式下运行迁移。
|
||||
|
||||
This configures the context with just a URL
|
||||
and not an Engine, though an Engine is acceptable
|
||||
here as well. By skipping the Engine creation
|
||||
we don't even need a DBAPI to be available.
|
||||
这使用 URL 而不是 Engine 来配置上下文,
|
||||
尽管 Engine 在这里也是可以接受的。
|
||||
通过跳过 Engine 的创建,我们甚至不需要 DBAPI 可用。
|
||||
|
||||
Calls to context.execute() here emit the given string to the
|
||||
script output.
|
||||
在这里调用 context.execute() 会将给定的字符串输出到脚本输出。
|
||||
|
||||
"""
|
||||
url = get_url()
|
||||
@@ -55,10 +51,10 @@ def run_migrations_offline():
|
||||
|
||||
|
||||
def run_migrations_online():
|
||||
"""Run migrations in 'online' mode.
|
||||
"""在"在线"模式下运行迁移。
|
||||
|
||||
In this scenario we need to create an Engine
|
||||
and associate a connection with the context.
|
||||
在这种情况下,我们需要创建一个 Engine
|
||||
并将连接与上下文关联。
|
||||
|
||||
"""
|
||||
configuration = config.get_section(config.config_ini_section)
|
||||
|
||||
@@ -36,13 +36,13 @@ def get_current_user(session: SessionDep, token: TokenDep) -> User:
|
||||
except (InvalidTokenError, ValidationError):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Could not validate credentials",
|
||||
detail="无法验证凭据",
|
||||
)
|
||||
user = session.get(User, token_data.sub)
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
raise HTTPException(status_code=404, detail="用户未找到")
|
||||
if not user.is_active:
|
||||
raise HTTPException(status_code=400, detail="Inactive user")
|
||||
raise HTTPException(status_code=400, detail="用户未激活")
|
||||
return user
|
||||
|
||||
|
||||
@@ -52,6 +52,6 @@ CurrentUser = Annotated[User, Depends(get_current_user)]
|
||||
def get_current_active_superuser(current_user: CurrentUser) -> User:
|
||||
if not current_user.is_superuser:
|
||||
raise HTTPException(
|
||||
status_code=403, detail="The user doesn't have enough privileges"
|
||||
status_code=403, detail="用户权限不足"
|
||||
)
|
||||
return current_user
|
||||
|
||||
@@ -15,7 +15,7 @@ def read_items(
|
||||
session: SessionDep, current_user: CurrentUser, skip: int = 0, limit: int = 100
|
||||
) -> Any:
|
||||
"""
|
||||
Retrieve items.
|
||||
获取 Item 列表。
|
||||
"""
|
||||
|
||||
if current_user.is_superuser:
|
||||
@@ -44,13 +44,13 @@ def read_items(
|
||||
@router.get("/{id}", response_model=ItemPublic)
|
||||
def read_item(session: SessionDep, current_user: CurrentUser, id: uuid.UUID) -> Any:
|
||||
"""
|
||||
Get item by ID.
|
||||
通过 ID 获取 Item。
|
||||
"""
|
||||
item = session.get(Item, id)
|
||||
if not item:
|
||||
raise HTTPException(status_code=404, detail="Item not found")
|
||||
raise HTTPException(status_code=404, detail="项目未找到")
|
||||
if not current_user.is_superuser and (item.owner_id != current_user.id):
|
||||
raise HTTPException(status_code=400, detail="Not enough permissions")
|
||||
raise HTTPException(status_code=400, detail="权限不足")
|
||||
return item
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ def create_item(
|
||||
*, session: SessionDep, current_user: CurrentUser, item_in: ItemCreate
|
||||
) -> Any:
|
||||
"""
|
||||
Create new item.
|
||||
创建新的 Item。
|
||||
"""
|
||||
item = Item.model_validate(item_in, update={"owner_id": current_user.id})
|
||||
session.add(item)
|
||||
@@ -77,13 +77,13 @@ def update_item(
|
||||
item_in: ItemUpdate,
|
||||
) -> Any:
|
||||
"""
|
||||
Update an item.
|
||||
更新 Item。
|
||||
"""
|
||||
item = session.get(Item, id)
|
||||
if not item:
|
||||
raise HTTPException(status_code=404, detail="Item not found")
|
||||
raise HTTPException(status_code=404, detail="项目未找到")
|
||||
if not current_user.is_superuser and (item.owner_id != current_user.id):
|
||||
raise HTTPException(status_code=400, detail="Not enough permissions")
|
||||
raise HTTPException(status_code=400, detail="权限不足")
|
||||
update_dict = item_in.model_dump(exclude_unset=True)
|
||||
item.sqlmodel_update(update_dict)
|
||||
session.add(item)
|
||||
@@ -97,13 +97,13 @@ def delete_item(
|
||||
session: SessionDep, current_user: CurrentUser, id: uuid.UUID
|
||||
) -> Message:
|
||||
"""
|
||||
Delete an item.
|
||||
删除 Item。
|
||||
"""
|
||||
item = session.get(Item, id)
|
||||
if not item:
|
||||
raise HTTPException(status_code=404, detail="Item not found")
|
||||
raise HTTPException(status_code=404, detail="项目未找到")
|
||||
if not current_user.is_superuser and (item.owner_id != current_user.id):
|
||||
raise HTTPException(status_code=400, detail="Not enough permissions")
|
||||
raise HTTPException(status_code=400, detail="权限不足")
|
||||
session.delete(item)
|
||||
session.commit()
|
||||
return Message(message="Item deleted successfully")
|
||||
return Message(message="项目删除成功")
|
||||
|
||||
@@ -26,15 +26,15 @@ def login_access_token(
|
||||
session: SessionDep, form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
|
||||
) -> Token:
|
||||
"""
|
||||
OAuth2 compatible token login, get an access token for future requests
|
||||
OAuth2 兼容的令牌登录,获取用于后续请求的访问令牌
|
||||
"""
|
||||
user = crud.authenticate(
|
||||
session=session, email=form_data.username, password=form_data.password
|
||||
)
|
||||
if not user:
|
||||
raise HTTPException(status_code=400, detail="Incorrect email or password")
|
||||
raise HTTPException(status_code=400, detail="邮箱或密码错误")
|
||||
elif not user.is_active:
|
||||
raise HTTPException(status_code=400, detail="Inactive user")
|
||||
raise HTTPException(status_code=400, detail="用户未激活")
|
||||
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
return Token(
|
||||
access_token=security.create_access_token(
|
||||
@@ -46,7 +46,7 @@ def login_access_token(
|
||||
@router.post("/login/test-token", response_model=UserPublic)
|
||||
def test_token(current_user: CurrentUser) -> Any:
|
||||
"""
|
||||
Test access token
|
||||
测试访问令牌
|
||||
"""
|
||||
return current_user
|
||||
|
||||
@@ -54,14 +54,14 @@ def test_token(current_user: CurrentUser) -> Any:
|
||||
@router.post("/password-recovery/{email}")
|
||||
def recover_password(email: str, session: SessionDep) -> Message:
|
||||
"""
|
||||
Password Recovery
|
||||
密码找回
|
||||
"""
|
||||
user = crud.get_user_by_email(session=session, email=email)
|
||||
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="The user with this email does not exist in the system.",
|
||||
detail="系统中不存在该邮箱的用户。",
|
||||
)
|
||||
password_reset_token = generate_password_reset_token(email=email)
|
||||
email_data = generate_reset_password_email(
|
||||
@@ -72,30 +72,30 @@ def recover_password(email: str, session: SessionDep) -> Message:
|
||||
subject=email_data.subject,
|
||||
html_content=email_data.html_content,
|
||||
)
|
||||
return Message(message="Password recovery email sent")
|
||||
return Message(message="密码找回邮件已发送")
|
||||
|
||||
|
||||
@router.post("/reset-password/")
|
||||
def reset_password(session: SessionDep, body: NewPassword) -> Message:
|
||||
"""
|
||||
Reset password
|
||||
重置密码
|
||||
"""
|
||||
email = verify_password_reset_token(token=body.token)
|
||||
if not email:
|
||||
raise HTTPException(status_code=400, detail="Invalid token")
|
||||
raise HTTPException(status_code=400, detail="无效的令牌")
|
||||
user = crud.get_user_by_email(session=session, email=email)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="The user with this email does not exist in the system.",
|
||||
detail="系统中不存在该邮箱的用户。",
|
||||
)
|
||||
elif not user.is_active:
|
||||
raise HTTPException(status_code=400, detail="Inactive user")
|
||||
raise HTTPException(status_code=400, detail="用户未激活")
|
||||
hashed_password = get_password_hash(password=body.new_password)
|
||||
user.hashed_password = hashed_password
|
||||
session.add(user)
|
||||
session.commit()
|
||||
return Message(message="Password updated successfully")
|
||||
return Message(message="密码更新成功")
|
||||
|
||||
|
||||
@router.post(
|
||||
@@ -105,14 +105,14 @@ def reset_password(session: SessionDep, body: NewPassword) -> Message:
|
||||
)
|
||||
def recover_password_html_content(email: str, session: SessionDep) -> Any:
|
||||
"""
|
||||
HTML Content for Password Recovery
|
||||
获取密码找回邮件的 HTML 内容
|
||||
"""
|
||||
user = crud.get_user_by_email(session=session, email=email)
|
||||
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="The user with this username does not exist in the system.",
|
||||
detail="系统中不存在该用户名的用户。",
|
||||
)
|
||||
password_reset_token = generate_password_reset_token(email=email)
|
||||
email_data = generate_reset_password_email(
|
||||
|
||||
@@ -23,7 +23,7 @@ class PrivateUserCreate(BaseModel):
|
||||
@router.post("/users/", response_model=UserPublic)
|
||||
def create_user(user_in: PrivateUserCreate, session: SessionDep) -> Any:
|
||||
"""
|
||||
Create a new user.
|
||||
创建新用户。
|
||||
"""
|
||||
|
||||
user = User(
|
||||
|
||||
@@ -36,7 +36,7 @@ router = APIRouter(prefix="/users", tags=["users"])
|
||||
)
|
||||
def read_users(session: SessionDep, skip: int = 0, limit: int = 100) -> Any:
|
||||
"""
|
||||
Retrieve users.
|
||||
获取用户列表。
|
||||
"""
|
||||
|
||||
count_statement = select(func.count()).select_from(User)
|
||||
@@ -53,13 +53,13 @@ def read_users(session: SessionDep, skip: int = 0, limit: int = 100) -> Any:
|
||||
)
|
||||
def create_user(*, session: SessionDep, user_in: UserCreate) -> Any:
|
||||
"""
|
||||
Create new user.
|
||||
创建新用户。
|
||||
"""
|
||||
user = crud.get_user_by_email(session=session, email=user_in.email)
|
||||
if user:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="The user with this email already exists in the system.",
|
||||
detail="系统中已存在该邮箱的用户。",
|
||||
)
|
||||
|
||||
user = crud.create_user(session=session, user_create=user_in)
|
||||
@@ -80,14 +80,14 @@ def update_user_me(
|
||||
*, session: SessionDep, user_in: UserUpdateMe, current_user: CurrentUser
|
||||
) -> Any:
|
||||
"""
|
||||
Update own user.
|
||||
更新当前用户信息。
|
||||
"""
|
||||
|
||||
if user_in.email:
|
||||
existing_user = crud.get_user_by_email(session=session, email=user_in.email)
|
||||
if existing_user and existing_user.id != current_user.id:
|
||||
raise HTTPException(
|
||||
status_code=409, detail="User with this email already exists"
|
||||
status_code=409, detail="该邮箱已被其他用户使用"
|
||||
)
|
||||
user_data = user_in.model_dump(exclude_unset=True)
|
||||
current_user.sqlmodel_update(user_data)
|
||||
@@ -102,25 +102,25 @@ def update_password_me(
|
||||
*, session: SessionDep, body: UpdatePassword, current_user: CurrentUser
|
||||
) -> Any:
|
||||
"""
|
||||
Update own password.
|
||||
更新当前用户密码。
|
||||
"""
|
||||
if not verify_password(body.current_password, current_user.hashed_password):
|
||||
raise HTTPException(status_code=400, detail="Incorrect password")
|
||||
raise HTTPException(status_code=400, detail="密码错误")
|
||||
if body.current_password == body.new_password:
|
||||
raise HTTPException(
|
||||
status_code=400, detail="New password cannot be the same as the current one"
|
||||
status_code=400, detail="新密码不能与当前密码相同"
|
||||
)
|
||||
hashed_password = get_password_hash(body.new_password)
|
||||
current_user.hashed_password = hashed_password
|
||||
session.add(current_user)
|
||||
session.commit()
|
||||
return Message(message="Password updated successfully")
|
||||
return Message(message="密码更新成功")
|
||||
|
||||
|
||||
@router.get("/me", response_model=UserPublic)
|
||||
def read_user_me(current_user: CurrentUser) -> Any:
|
||||
"""
|
||||
Get current user.
|
||||
获取当前用户信息。
|
||||
"""
|
||||
return current_user
|
||||
|
||||
@@ -128,27 +128,27 @@ def read_user_me(current_user: CurrentUser) -> Any:
|
||||
@router.delete("/me", response_model=Message)
|
||||
def delete_user_me(session: SessionDep, current_user: CurrentUser) -> Any:
|
||||
"""
|
||||
Delete own user.
|
||||
删除当前用户。
|
||||
"""
|
||||
if current_user.is_superuser:
|
||||
raise HTTPException(
|
||||
status_code=403, detail="Super users are not allowed to delete themselves"
|
||||
status_code=403, detail="超级用户不允许删除自己"
|
||||
)
|
||||
session.delete(current_user)
|
||||
session.commit()
|
||||
return Message(message="User deleted successfully")
|
||||
return Message(message="用户删除成功")
|
||||
|
||||
|
||||
@router.post("/signup", response_model=UserPublic)
|
||||
def register_user(session: SessionDep, user_in: UserRegister) -> Any:
|
||||
"""
|
||||
Create new user without the need to be logged in.
|
||||
无需登录即可创建新用户(用户注册)。
|
||||
"""
|
||||
user = crud.get_user_by_email(session=session, email=user_in.email)
|
||||
if user:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="The user with this email already exists in the system",
|
||||
detail="系统中已存在该邮箱的用户",
|
||||
)
|
||||
user_create = UserCreate.model_validate(user_in)
|
||||
user = crud.create_user(session=session, user_create=user_create)
|
||||
@@ -160,7 +160,7 @@ def read_user_by_id(
|
||||
user_id: uuid.UUID, session: SessionDep, current_user: CurrentUser
|
||||
) -> Any:
|
||||
"""
|
||||
Get a specific user by id.
|
||||
通过 ID 获取指定用户。
|
||||
"""
|
||||
user = session.get(User, user_id)
|
||||
if user == current_user:
|
||||
@@ -168,7 +168,7 @@ def read_user_by_id(
|
||||
if not current_user.is_superuser:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="The user doesn't have enough privileges",
|
||||
detail="用户权限不足",
|
||||
)
|
||||
return user
|
||||
|
||||
@@ -185,20 +185,20 @@ def update_user(
|
||||
user_in: UserUpdate,
|
||||
) -> Any:
|
||||
"""
|
||||
Update a user.
|
||||
更新用户信息。
|
||||
"""
|
||||
|
||||
db_user = session.get(User, user_id)
|
||||
if not db_user:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail="The user with this id does not exist in the system",
|
||||
detail="系统中不存在该ID的用户",
|
||||
)
|
||||
if user_in.email:
|
||||
existing_user = crud.get_user_by_email(session=session, email=user_in.email)
|
||||
if existing_user and existing_user.id != user_id:
|
||||
raise HTTPException(
|
||||
status_code=409, detail="User with this email already exists"
|
||||
status_code=409, detail="该邮箱已被其他用户使用"
|
||||
)
|
||||
|
||||
db_user = crud.update_user(session=session, db_user=db_user, user_in=user_in)
|
||||
@@ -210,17 +210,17 @@ def delete_user(
|
||||
session: SessionDep, current_user: CurrentUser, user_id: uuid.UUID
|
||||
) -> Message:
|
||||
"""
|
||||
Delete a user.
|
||||
删除用户。
|
||||
"""
|
||||
user = session.get(User, user_id)
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
raise HTTPException(status_code=404, detail="用户未找到")
|
||||
if user == current_user:
|
||||
raise HTTPException(
|
||||
status_code=403, detail="Super users are not allowed to delete themselves"
|
||||
status_code=403, detail="超级用户不允许删除自己"
|
||||
)
|
||||
statement = delete(Item).where(col(Item.owner_id) == user_id)
|
||||
session.exec(statement) # type: ignore
|
||||
session.delete(user)
|
||||
session.commit()
|
||||
return Message(message="User deleted successfully")
|
||||
return Message(message="用户删除成功")
|
||||
|
||||
@@ -15,7 +15,7 @@ router = APIRouter(prefix="/utils", tags=["utils"])
|
||||
)
|
||||
def test_email(email_to: EmailStr) -> Message:
|
||||
"""
|
||||
Test emails.
|
||||
测试邮件发送。
|
||||
"""
|
||||
email_data = generate_test_email(email_to=email_to)
|
||||
send_email(
|
||||
@@ -23,7 +23,7 @@ def test_email(email_to: EmailStr) -> Message:
|
||||
subject=email_data.subject,
|
||||
html_content=email_data.html_content,
|
||||
)
|
||||
return Message(message="Test email sent")
|
||||
return Message(message="测试邮件已发送")
|
||||
|
||||
|
||||
@router.get("/health-check/")
|
||||
|
||||
@@ -22,7 +22,7 @@ wait_seconds = 1
|
||||
def init(db_engine: Engine) -> None:
|
||||
try:
|
||||
with Session(db_engine) as session:
|
||||
# Try to create session to check if DB is awake
|
||||
# 尝试创建会话以检查数据库是否已就绪
|
||||
session.exec(select(1))
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
@@ -30,9 +30,9 @@ def init(db_engine: Engine) -> None:
|
||||
|
||||
|
||||
def main() -> None:
|
||||
logger.info("Initializing service")
|
||||
logger.info("正在初始化服务")
|
||||
init(engine)
|
||||
logger.info("Service finished initializing")
|
||||
logger.info("服务初始化完成")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -25,14 +25,14 @@ def parse_cors(v: Any) -> list[str] | str:
|
||||
|
||||
class Settings(BaseSettings):
|
||||
model_config = SettingsConfigDict(
|
||||
# Use top level .env file (one level above ./backend/)
|
||||
# 使用顶层的 .env 文件(在 ./backend/ 上一级目录)
|
||||
env_file="../.env",
|
||||
env_ignore_empty=True,
|
||||
extra="ignore",
|
||||
)
|
||||
API_V1_STR: str = "/api/v1"
|
||||
SECRET_KEY: str = secrets.token_urlsafe(32)
|
||||
# 60 minutes * 24 hours * 8 days = 8 days
|
||||
# 60 分钟 * 24 小时 * 8 天 = 8 天
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8
|
||||
FRONTEND_HOST: str = "http://localhost:5173"
|
||||
ENVIRONMENT: Literal["local", "staging", "production"] = "local"
|
||||
@@ -97,8 +97,8 @@ class Settings(BaseSettings):
|
||||
def _check_default_secret(self, var_name: str, value: str | None) -> None:
|
||||
if value == "changethis":
|
||||
message = (
|
||||
f'The value of {var_name} is "changethis", '
|
||||
"for security, please change it, at least for deployments."
|
||||
f'{var_name} 的值为 "changethis",'
|
||||
"出于安全考虑,请修改它,至少在部署时需要修改。"
|
||||
)
|
||||
if self.ENVIRONMENT == "local":
|
||||
warnings.warn(message, stacklevel=1)
|
||||
|
||||
@@ -7,18 +7,17 @@ from app.models import User, UserCreate
|
||||
engine = create_engine(str(settings.SQLALCHEMY_DATABASE_URI))
|
||||
|
||||
|
||||
# make sure all SQLModel models are imported (app.models) before initializing DB
|
||||
# otherwise, SQLModel might fail to initialize relationships properly
|
||||
# for more details: https://github.com/fastapi/full-stack-fastapi-template/issues/28
|
||||
# 确保在初始化数据库之前已导入所有 SQLModel 模型(app.models)
|
||||
# 否则,SQLModel 可能无法正确初始化关系
|
||||
# 更多详情请参考:https://github.com/fastapi/full-stack-fastapi-template/issues/28
|
||||
|
||||
|
||||
def init_db(session: Session) -> None:
|
||||
# Tables should be created with Alembic migrations
|
||||
# But if you don't want to use migrations, create
|
||||
# the tables un-commenting the next lines
|
||||
# 数据库表应该通过 Alembic 迁移创建
|
||||
# 但如果你不想使用迁移,可以取消注释下面的代码来创建表
|
||||
# from sqlmodel import SQLModel
|
||||
|
||||
# This works because the models are already imported and registered from app.models
|
||||
# 这样可以正常工作,因为模型已经从 app.models 导入并注册
|
||||
# SQLModel.metadata.create_all(engine)
|
||||
|
||||
user = session.exec(
|
||||
|
||||
@@ -14,9 +14,9 @@ def init() -> None:
|
||||
|
||||
|
||||
def main() -> None:
|
||||
logger.info("Creating initial data")
|
||||
logger.info("正在创建初始数据")
|
||||
init()
|
||||
logger.info("Initial data created")
|
||||
logger.info("初始数据创建完成")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ app = FastAPI(
|
||||
generate_unique_id_function=custom_generate_unique_id,
|
||||
)
|
||||
|
||||
# Set all CORS enabled origins
|
||||
# 设置所有允许 CORS 的源
|
||||
if settings.all_cors_origins:
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
|
||||
+13
-13
@@ -4,7 +4,7 @@ from pydantic import EmailStr
|
||||
from sqlmodel import Field, Relationship, SQLModel
|
||||
|
||||
|
||||
# Shared properties
|
||||
# 共享属性
|
||||
class UserBase(SQLModel):
|
||||
email: EmailStr = Field(unique=True, index=True, max_length=255)
|
||||
is_active: bool = True
|
||||
@@ -12,7 +12,7 @@ class UserBase(SQLModel):
|
||||
full_name: str | None = Field(default=None, max_length=255)
|
||||
|
||||
|
||||
# Properties to receive via API on creation
|
||||
# 创建用户时通过 API 接收的属性
|
||||
class UserCreate(UserBase):
|
||||
password: str = Field(min_length=8, max_length=128)
|
||||
|
||||
@@ -23,7 +23,7 @@ class UserRegister(SQLModel):
|
||||
full_name: str | None = Field(default=None, max_length=255)
|
||||
|
||||
|
||||
# Properties to receive via API on update, all are optional
|
||||
# 更新用户时通过 API 接收的属性,所有字段均为可选
|
||||
class UserUpdate(UserBase):
|
||||
email: EmailStr | None = Field(default=None, max_length=255) # type: ignore
|
||||
password: str | None = Field(default=None, min_length=8, max_length=128)
|
||||
@@ -39,14 +39,14 @@ class UpdatePassword(SQLModel):
|
||||
new_password: str = Field(min_length=8, max_length=128)
|
||||
|
||||
|
||||
# Database model, database table inferred from class name
|
||||
# 数据库模型,数据库表名从类名推断
|
||||
class User(UserBase, table=True):
|
||||
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
||||
hashed_password: str
|
||||
items: list["Item"] = Relationship(back_populates="owner", cascade_delete=True)
|
||||
|
||||
|
||||
# Properties to return via API, id is always required
|
||||
# 通过 API 返回的属性,id 始终必填
|
||||
class UserPublic(UserBase):
|
||||
id: uuid.UUID
|
||||
|
||||
@@ -56,23 +56,23 @@ class UsersPublic(SQLModel):
|
||||
count: int
|
||||
|
||||
|
||||
# Shared properties
|
||||
# 共享属性
|
||||
class ItemBase(SQLModel):
|
||||
title: str = Field(min_length=1, max_length=255)
|
||||
description: str | None = Field(default=None, max_length=255)
|
||||
|
||||
|
||||
# Properties to receive on item creation
|
||||
# 创建 Item 时接收的属性
|
||||
class ItemCreate(ItemBase):
|
||||
pass
|
||||
|
||||
|
||||
# Properties to receive on item update
|
||||
# 更新 Item 时接收的属性
|
||||
class ItemUpdate(ItemBase):
|
||||
title: str | None = Field(default=None, min_length=1, max_length=255) # type: ignore
|
||||
|
||||
|
||||
# Database model, database table inferred from class name
|
||||
# 数据库模型,数据库表名从类名推断
|
||||
class Item(ItemBase, table=True):
|
||||
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
||||
owner_id: uuid.UUID = Field(
|
||||
@@ -81,7 +81,7 @@ class Item(ItemBase, table=True):
|
||||
owner: User | None = Relationship(back_populates="items")
|
||||
|
||||
|
||||
# Properties to return via API, id is always required
|
||||
# 通过 API 返回的属性,id 始终必填
|
||||
class ItemPublic(ItemBase):
|
||||
id: uuid.UUID
|
||||
owner_id: uuid.UUID
|
||||
@@ -92,18 +92,18 @@ class ItemsPublic(SQLModel):
|
||||
count: int
|
||||
|
||||
|
||||
# Generic message
|
||||
# 通用消息
|
||||
class Message(SQLModel):
|
||||
message: str
|
||||
|
||||
|
||||
# JSON payload containing access token
|
||||
# 包含访问令牌的 JSON 载荷
|
||||
class Token(SQLModel):
|
||||
access_token: str
|
||||
token_type: str = "bearer"
|
||||
|
||||
|
||||
# Contents of JWT token
|
||||
# JWT 令牌内容
|
||||
class TokenPayload(SQLModel):
|
||||
sub: str | None = None
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ wait_seconds = 1
|
||||
)
|
||||
def init(db_engine: Engine) -> None:
|
||||
try:
|
||||
# Try to create session to check if DB is awake
|
||||
# 尝试创建会话以检查数据库是否已就绪
|
||||
with Session(db_engine) as session:
|
||||
session.exec(select(1))
|
||||
except Exception as e:
|
||||
@@ -30,9 +30,9 @@ def init(db_engine: Engine) -> None:
|
||||
|
||||
|
||||
def main() -> None:
|
||||
logger.info("Initializing service")
|
||||
logger.info("正在初始化服务")
|
||||
init(engine)
|
||||
logger.info("Service finished initializing")
|
||||
logger.info("服务初始化完成")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -36,7 +36,7 @@ def send_email(
|
||||
subject: str = "",
|
||||
html_content: str = "",
|
||||
) -> None:
|
||||
assert settings.emails_enabled, "no provided configuration for email variables"
|
||||
assert settings.emails_enabled, "未提供邮件相关配置变量"
|
||||
message = emails.Message(
|
||||
subject=subject,
|
||||
html=html_content,
|
||||
|
||||
Reference in New Issue
Block a user