Refactor password handling and model typing
Replaced custom password generation and verification logic with a new pkg/password.py module using Argon2 for secure hashing. Updated model field types to use PEP 604 union syntax (e.g., str | None) and improved type annotations. Refactored admin and session routes to use new password utilities and direct model methods for CRUD operations. Removed legacy tool-based password functions and cleaned up .idea project files.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# model/base.py
|
||||
from datetime import datetime, timezone
|
||||
from typing import Optional, Type, TypeVar, Union, Literal, List
|
||||
from typing import Type, TypeVar, Union, Literal, List
|
||||
|
||||
from sqlalchemy import DateTime, BinaryExpression, ClauseElement
|
||||
from sqlalchemy.orm import selectinload
|
||||
@@ -27,7 +27,7 @@ class TableBase(AsyncAttrs, SQLModel):
|
||||
sa_column_kwargs={"default": utcnow, "onupdate": utcnow},
|
||||
default_factory=utcnow
|
||||
)
|
||||
deleted_at: Optional[datetime] = Field(
|
||||
deleted_at: datetime | None = Field(
|
||||
default=None,
|
||||
description="删除时间",
|
||||
sa_column={"nullable": True}
|
||||
@@ -148,4 +148,4 @@ class TableBase(AsyncAttrs, SQLModel):
|
||||
|
||||
# 需要“自增 id 主键”的模型才混入它;Setting 不混入
|
||||
class IdMixin(SQLModel):
|
||||
id: Optional[int] = Field(default=None, primary_key=True, description="主键ID")
|
||||
id: int | None = Field(default=None, primary_key=True, description="主键ID")
|
||||
@@ -42,12 +42,25 @@ class Database:
|
||||
async with _async_session_factory() as session:
|
||||
yield session
|
||||
|
||||
@staticmethod
|
||||
@asynccontextmanager
|
||||
async def session_context() -> AsyncGenerator[AsyncSession, None]:
|
||||
"""
|
||||
提供异步上下文管理器用于直接获取数据库会话
|
||||
|
||||
使用示例:
|
||||
async with Database.session_context() as session:
|
||||
# 执行数据库操作
|
||||
pass
|
||||
"""
|
||||
async with _async_session_factory() as session:
|
||||
yield session
|
||||
|
||||
async def init_db(self, url: str = ASYNC_DATABASE_URL):
|
||||
"""创建数据库结构"""
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(SQLModel.metadata.create_all)
|
||||
|
||||
# For internal use, create a temporary context manager
|
||||
get_session_cm = asynccontextmanager(self.get_session)
|
||||
async with get_session_cm() as session:
|
||||
async with self.session_context() as session:
|
||||
await migration(session) # 执行迁移脚本
|
||||
@@ -1,5 +1,4 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
|
||||
class Item(BaseModel):
|
||||
id: int
|
||||
@@ -9,8 +8,7 @@ class Item(BaseModel):
|
||||
icon: str
|
||||
status: str
|
||||
phone: int
|
||||
lost_description: Optional[str]
|
||||
find_ip: Optional[str]
|
||||
lost_description: str | None
|
||||
find_ip: str | None
|
||||
create_time: str
|
||||
lost_time: Optional[str]
|
||||
|
||||
lost_time: str | None
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from loguru import logger
|
||||
from sqlmodel import select
|
||||
from .setting import Setting
|
||||
import tool
|
||||
from pkg.password import Password
|
||||
|
||||
default_settings: list[Setting] = [
|
||||
Setting(type='string', name='version', value='1.0.0'),
|
||||
@@ -18,11 +18,11 @@ async def migration(session):
|
||||
return
|
||||
|
||||
# 生成初始密码与密钥
|
||||
admin_password = tool.generate_password()
|
||||
admin_password = Password.generate()
|
||||
logger.warning(f"密码(请牢记,后续不再显示): {admin_password}")
|
||||
|
||||
settings.append(Setting(type='string', name='password', value=tool.hash_password(admin_password)))
|
||||
settings.append(Setting(type='string', name='SECRET_KEY', value=tool.generate_password(64)))
|
||||
settings.append(Setting(type='string', name='password', value=Password.hash(admin_password)))
|
||||
settings.append(Setting(type='string', name='SECRET_KEY', value=Password.generate(64)))
|
||||
|
||||
# 读取库里已存在的 name,避免主键冲突
|
||||
names = [s.name for s in settings]
|
||||
|
||||
@@ -1,45 +1,29 @@
|
||||
# my_project/models/download.py
|
||||
|
||||
from typing import Literal, Optional, TYPE_CHECKING
|
||||
from sqlmodel import Field, Column, SQLModel, String, DateTime
|
||||
from typing import Literal
|
||||
from sqlmodel import Field, Column, String, DateTime
|
||||
from .base import TableBase, IdMixin
|
||||
from datetime import datetime
|
||||
|
||||
"""
|
||||
原建表语句:
|
||||
|
||||
CREATE TABLE IF NOT EXISTS fr_objects (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
key TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
icon TEXT,
|
||||
status TEXT,
|
||||
phone TEXT,
|
||||
context TEXT,
|
||||
find_ip TEXT,
|
||||
create_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
lost_at TIMESTAMP
|
||||
"""
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
class Object(IdMixin, TableBase, table=True):
|
||||
__tablename__ = 'fr_objects'
|
||||
|
||||
key: str = Field(index=True, nullable=False, description="物品外部ID")
|
||||
type: Literal['object', 'box'] = Field(
|
||||
default='object',
|
||||
type: Literal['normal', 'car'] = Field(
|
||||
default='normal',
|
||||
description="物品类型",
|
||||
sa_column=Column(String, default='object', nullable=False)
|
||||
sa_column=Column(String, default='normal', nullable=False)
|
||||
)
|
||||
name: str = Field(nullable=False, description="物品名称")
|
||||
icon: Optional[str] = Field(default=None, description="物品图标")
|
||||
status: Optional[str] = Field(default=None, description="物品状态")
|
||||
phone: Optional[str] = Field(default=None, description="联系电话")
|
||||
context: Optional[str] = Field(default=None, description="物品描述")
|
||||
find_ip: Optional[str] = Field(default=None, description="最后一次发现的IP地址")
|
||||
lost_at: Optional[datetime] = Field(
|
||||
icon: str | None = Field(default=None, description="物品图标")
|
||||
status: Literal['ok', 'lost'] = Field(
|
||||
default='ok',
|
||||
description="物品状态",
|
||||
sa_column=Column(String, default='ok', nullable=False)
|
||||
)
|
||||
phone: str | None = Field(default=None, description="联系电话")
|
||||
context: str | None = Field(default=None, description="物品描述")
|
||||
find_ip: str | None = Field(default=None, description="最后一次发现的IP地址")
|
||||
lost_at: datetime | None = Field(
|
||||
default=None,
|
||||
description="物品标记为丢失的时间",
|
||||
sa_column=Column(DateTime, nullable=True)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Literal, Optional
|
||||
from typing import Literal
|
||||
|
||||
class DefaultResponse(BaseModel):
|
||||
code: int = 0
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# model/setting.py
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
from sqlmodel import Field
|
||||
from .base import TableBase
|
||||
|
||||
@@ -12,12 +11,8 @@ CREATE TABLE IF NOT EXISTS fr_settings (
|
||||
)
|
||||
"""
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
class Setting(TableBase, table=True):
|
||||
__tablename__ = 'fr_settings'
|
||||
|
||||
type: str = Field(index=True, nullable=False, description="设置类型")
|
||||
name: str = Field(primary_key=True, nullable=False, description="设置名称") # name 为唯一主键
|
||||
value: Optional[str] = Field(description="设置值")
|
||||
value: str | None = Field(description="设置值")
|
||||
|
||||
Reference in New Issue
Block a user