数据库创建

This commit is contained in:
2025-06-22 19:26:23 +08:00
parent 6094d8219e
commit f6825b670f
31 changed files with 1494 additions and 270 deletions

30
models/__init__.py Normal file
View File

@@ -0,0 +1,30 @@
# my_project/models/__init__.py
from . import response
# 将所有模型导入到这个包的命名空间中
from .base import BaseModel
from .download import Download
from .file import File
from .folder import Folder
from .group import Group
from .node import Node
from .order import Order
from .policy import Policy
from .redeem import Redeem
from .report import Report
from .setting import Setting
from .share import Share
from .source_link import SourceLink
from .storage_pack import StoragePack
from .tag import Tag
from .task import Task
from .user import User
from .webdav import WebDAV
# 可以定义一个 __all__ 列表来明确指定可以被 from .models import * 导入的内容
__all__ = [
"BaseModel", "Download", "File", "Folder", "Group", "Node", "Order",
"Policy", "Redeem", "Report", "Setting", "Share", "SourceLink",
"StoragePack", "Tag", "Task", "User", "WebDAV"
]

9
models/base.py Normal file
View File

@@ -0,0 +1,9 @@
# my_project/models/base.py
from typing import Optional
from sqlmodel import SQLModel, Field
class BaseModel(SQLModel):
__abstract__ = True
id: Optional[int] = Field(default=None, primary_key=True, description="主键ID")

31
models/database.py Normal file
View File

@@ -0,0 +1,31 @@
# my_project/database.py
from sqlmodel import SQLModel
from sqlalchemy.ext.asyncio import create_async_engine
from sqlmodel.ext.asyncio.session import AsyncSession
from sqlalchemy.orm import sessionmaker
ASYNC_DATABASE_URL = "sqlite+aiosqlite:///database.db"
engine = create_async_engine(
ASYNC_DATABASE_URL,
echo=True,
connect_args={"check_same_thread": False}
if ASYNC_DATABASE_URL.startswith("sqlite")
else None,
future=True,
# pool_size=POOL_SIZE,
# max_overflow=64,
)
_async_session_factory = sessionmaker(engine, class_=AsyncSession)
async def get_session():
async with _async_session_factory() as session:
yield session
async def init_db():
"""初始化数据库"""
# 创建所有表
async with engine.begin() as conn:
await conn.run_sync(SQLModel.metadata.create_all)

56
models/download.py Normal file
View File

@@ -0,0 +1,56 @@
# my_project/models/download.py
from typing import Optional, TYPE_CHECKING
from sqlmodel import Field, Relationship, Column, func, DateTime
from .base import BaseModel
from datetime import datetime
if TYPE_CHECKING:
from .user import User
from .task import Task
from .node import Node
class Download(BaseModel, table=True):
__tablename__ = 'downloads'
status: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="下载状态: 0=进行中, 1=完成, 2=错误")
type: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="任务类型")
source: str = Field(description="来源URL或标识")
total_size: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="总大小(字节)")
downloaded_size: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="已下载大小(字节)")
g_id: Optional[str] = Field(default=None, index=True, description="Aria2 GID")
speed: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="下载速度 (bytes/s)")
parent: Optional[str] = Field(default=None, description="父任务标识")
attrs: Optional[str] = Field(default=None, description="额外属性 (JSON格式)")
error: Optional[str] = Field(default=None, description="错误信息")
dst: str = Field(description="目标存储路径")
created_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
comment="创建时间",
),
)
updated_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
comment="更新时间",
),
)
# 外键
user_id: int = Field(foreign_key="users.id", index=True, description="所属用户ID")
task_id: Optional[int] = Field(default=None, foreign_key="tasks.id", index=True, description="关联的任务ID")
node_id: int = Field(foreign_key="nodes.id", index=True, description="执行下载的节点ID")
# 关系
user: "User" = Relationship(back_populates="downloads")
task: Optional["Task"] = Relationship(back_populates="downloads")
node: "Node" = Relationship(back_populates="downloads")

53
models/file.py Normal file
View File

@@ -0,0 +1,53 @@
# my_project/models/file.py
from typing import Optional, TYPE_CHECKING
from sqlmodel import Field, Relationship, Column, func, DateTime
from .base import BaseModel
from datetime import datetime
if TYPE_CHECKING:
from .user import User
from .folder import Folder
from .policy import Policy
from .source_link import SourceLink
class File(BaseModel, table=True):
__tablename__ = 'files'
name: str = Field(max_length=255, description="文件名")
source_name: Optional[str] = Field(default=None, description="源文件名")
size: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="文件大小(字节)")
pic_info: Optional[str] = Field(default=None, max_length=255, description="图片信息(如尺寸)")
upload_session_id: Optional[str] = Field(default=None, max_length=255, unique=True, index=True, description="分块上传会话ID")
file_metadata: Optional[str] = Field(default=None, description="文件元数据 (JSON格式)")
created_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
comment="创建时间",
),
)
updated_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
comment="更新时间",
),
)
# 外键
user_id: int = Field(foreign_key="users.id", index=True, description="所属用户ID")
folder_id: int = Field(foreign_key="folders.id", index=True, description="所在目录ID")
policy_id: int = Field(foreign_key="policies.id", index=True, description="所属存储策略ID")
# 关系
user: list["User"] = Relationship(back_populates="files")
folder: list["Folder"] = Relationship(back_populates="files")
policy: list["Policy"] = Relationship(back_populates="files")
source_links: list["SourceLink"] = Relationship(back_populates="file")

52
models/folder.py Normal file
View File

@@ -0,0 +1,52 @@
# my_project/models/folder.py
from typing import Optional, List, TYPE_CHECKING
from sqlmodel import Field, Relationship, UniqueConstraint, Column, func, DateTime
from .base import BaseModel
from datetime import datetime
if TYPE_CHECKING:
from .user import User
from .policy import Policy
from .file import File
class Folder(BaseModel, table=True):
__tablename__ = 'folders'
__table_args__ = (UniqueConstraint("name", "parent_id", name="uq_folder_name_parent"),)
name: str = Field(max_length=255, description="目录名")
created_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
comment="创建时间",
),
)
updated_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
comment="更新时间",
),
)
# 外键
parent_id: Optional[int] = Field(default=None, foreign_key="folders.id", index=True, description="父目录ID")
owner_id: int = Field(foreign_key="users.id", index=True, description="所有者用户ID")
policy_id: int = Field(foreign_key="policies.id", index=True, description="所属存储策略ID")
# 关系
owner: "User" = Relationship(back_populates="folders")
policy: "Policy" = Relationship(back_populates="folders")
# 自我引用关系
parent: Optional["Folder"] = Relationship(back_populates="children", sa_relationship_kwargs={"remote_side": "Folder.id"})
children: List["Folder"] = Relationship(back_populates="parent")
files: List["File"] = Relationship(back_populates="folder")

44
models/group.py Normal file
View File

@@ -0,0 +1,44 @@
# my_project/models/group.py
from typing import Optional, List, TYPE_CHECKING
from sqlmodel import Field, Relationship, text, Column, func, DateTime
from .base import BaseModel
from datetime import datetime
if TYPE_CHECKING:
from .user import User
class Group(BaseModel, table=True):
__tablename__ = 'groups'
name: str = Field(max_length=255, unique=True, description="用户组名")
policies: Optional[str] = Field(default=None, max_length=255, description="允许的策略ID列表逗号分隔")
max_storage: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="最大存储空间(字节)")
share_enabled: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}, description="是否允许创建分享")
web_dav_enabled: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}, description="是否允许使用WebDAV")
speed_limit: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="速度限制 (KB/s), 0为不限制")
options: Optional[str] = Field(default=None, description="其他选项 (JSON格式)")
created_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
comment="创建时间",
),
)
updated_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
comment="更新时间",
),
)
# 关系:一个组可以有多个用户
users: List["User"] = Relationship(back_populates="group")
previous_users: List["User"] = Relationship(back_populates="previous_group")

View File

@@ -1,251 +0,0 @@
from sqlalchemy import (
Column, Integer, String, Text, BigInteger, Boolean, DateTime,
ForeignKey, func, text, UniqueConstraint
)
from sqlalchemy.orm import declarative_base
Base = declarative_base()
class BaseModel(Base):
__abstract__ = True
id = Column(Integer, primary_key=True, comment="主键ID")
created_at = Column(
DateTime,
server_default=func.now(),
comment="创建时间"
)
updated_at = Column(
DateTime,
server_default=func.now(),
onupdate=func.now(),
server_onupdate=func.now(),
comment="更新时间"
)
deleted_at = Column(DateTime, nullable=True, comment="软删除时间")
class Download(BaseModel):
__tablename__ = 'downloads'
status = Column(Integer, nullable=False, server_default='0', comment="下载状态: 0=进行中, 1=完成, 2=错误")
type = Column(Integer, nullable=False, server_default='0', comment="任务类型")
source = Column(Text, nullable=False, comment="来源URL或标识")
total_size = Column(BigInteger, nullable=False, server_default='0', comment="总大小(字节)")
downloaded_size = Column(BigInteger, nullable=False, server_default='0', comment="已下载大小(字节)")
g_id = Column(Text, index=True, comment="Aria2 GID") # GID经常用于查询建议索引
speed = Column(Integer, nullable=False, server_default='0', comment="下载速度 (bytes/s)")
parent = Column(Text, comment="父任务标识")
attrs = Column(Text, comment="额外属性 (JSON格式)")
error = Column(Text, comment="错误信息")
dst = Column(Text, nullable=False, comment="目标存储路径")
user_id = Column(Integer, ForeignKey('users.id'), nullable=False, index=True, comment="所属用户ID")
task_id = Column(Integer, ForeignKey('tasks.id'), nullable=True, index=True, comment="关联的任务ID")
node_id = Column(Integer, ForeignKey('nodes.id'), nullable=False, index=True, comment="执行下载的节点ID")
class File(BaseModel):
__tablename__ = 'files'
name = Column(String(255), nullable=False, comment="文件名")
source_name = Column(Text, comment="源文件名")
size = Column(BigInteger, nullable=False, server_default='0', comment="文件大小(字节)")
pic_info = Column(String(255), comment="图片信息(如尺寸)")
upload_session_id = Column(String(255), unique=True, index=True, comment="分块上传会话ID")
metadata = Column(Text, comment="文件元数据 (JSON格式)")
user_id = Column(Integer, ForeignKey('users.id'), nullable=False, index=True, comment="所属用户ID")
folder_id = Column(Integer, ForeignKey('folders.id'), nullable=False, index=True, comment="所在目录ID")
policy_id = Column(Integer, ForeignKey('policies.id'), nullable=False, index=True, comment="所属存储策略ID")
class Folder(BaseModel):
__tablename__ = 'folders'
name = Column(String(255), nullable=False, comment="目录名")
parent_id = Column(Integer, ForeignKey('folders.id'), nullable=True, index=True, comment="父目录ID")
owner_id = Column(Integer, ForeignKey('users.id'), nullable=False, index=True, comment="所有者用户ID")
policy_id = Column(Integer, ForeignKey('policies.id'), nullable=False, index=True, comment="所属存储策略ID")
__table_args__ = (
UniqueConstraint('name', 'parent_id', name='uq_folder_name_parent'),
)
class Group(BaseModel):
__tablename__ = 'groups'
name = Column(String(255), nullable=False, unique=True, comment="用户组名")
policies = Column(String(255), comment="允许的策略ID列表逗号分隔")
max_storage = Column(BigInteger, nullable=False, server_default='0', comment="最大存储空间(字节)")
share_enabled = Column(Boolean, nullable=False, server_default=text('false'), comment="是否允许创建分享")
web_dav_enabled = Column(Boolean, nullable=False, server_default=text('false'), comment="是否允许使用WebDAV")
speed_limit = Column(Integer, nullable=False, server_default='0', comment="速度限制 (KB/s), 0为不限制")
options = Column(Text, comment="其他选项 (JSON格式)")
class Node(BaseModel):
__tablename__ = 'nodes'
status = Column(Integer, nullable=False, server_default='0', comment="节点状态: 0=正常, 1=离线")
name = Column(String(255), nullable=False, unique=True, comment="节点名称")
type = Column(Integer, nullable=False, server_default='0', comment="节点类型")
server = Column(String(255), nullable=False, comment="节点地址IP或域名")
slave_key = Column(Text, comment="从机通讯密钥")
master_key = Column(Text, comment="主机通讯密钥")
aria2_enabled = Column(Boolean, nullable=False, server_default=text('false'), comment="是否启用Aria2")
aria2_options = Column(Text, comment="Aria2配置 (JSON格式)")
rank = Column(Integer, nullable=False, server_default='0', comment="节点排序权重")
class Order(BaseModel):
__tablename__ = 'orders'
order_no = Column(String(255), nullable=False, unique=True, index=True, comment="订单号,唯一")
type = Column(Integer, nullable=False, comment="订单类型")
method = Column(String(255), comment="支付方式")
product_id = Column(BigInteger, comment="商品ID")
num = Column(Integer, nullable=False, server_default='1', comment="购买数量")
name = Column(String(255), nullable=False, comment="商品名称")
price = Column(Integer, nullable=False, server_default='0', comment="订单价格(分)")
status = Column(Integer, nullable=False, server_default='0', comment="订单状态: 0=待支付, 1=已完成, 2=已取消")
user_id = Column(Integer, ForeignKey('users.id'), nullable=False, index=True, comment="所属用户ID")
class Policy(BaseModel):
__tablename__ = 'policies'
name = Column(String(255), nullable=False, unique=True, comment="策略名称")
type = Column(String(255), nullable=False, comment="存储类型 (e.g. 'local', 's3')")
server = Column(String(255), comment="服务器地址(本地策略为路径)")
bucket_name = Column(String(255), comment="存储桶名称")
is_private = Column(Boolean, nullable=False, server_default=text('true'), comment="是否为私有空间")
base_url = Column(String(255), comment="访问文件的基础URL")
access_key = Column(Text, comment="Access Key")
secret_key = Column(Text, comment="Secret Key")
max_size = Column(BigInteger, nullable=False, server_default='0', comment="允许上传的最大文件尺寸(字节)")
auto_rename = Column(Boolean, nullable=False, server_default=text('false'), comment="是否自动重命名")
dir_name_rule = Column(String(255), comment="目录命名规则")
file_name_rule = Column(String(255), comment="文件命名规则")
is_origin_link_enable = Column(Boolean, nullable=False, server_default=text('false'), comment="是否开启源链接访问")
options = Column(Text, comment="其他选项 (JSON格式)")
class Setting(BaseModel):
__tablename__ = 'settings'
# 优化点: type和name的组合应该是唯一的
type = Column(String(255), nullable=False, comment="设置类型/分组")
name = Column(String(255), nullable=False, comment="设置项名称")
value = Column(Text, comment="设置值")
__table_args__ = (
UniqueConstraint('type', 'name', name='uq_setting_type_name'),
)
class Share(BaseModel):
__tablename__ = 'shares'
password = Column(String(255), comment="分享密码(加密后)")
is_dir = Column(Boolean, nullable=False, server_default=text('false'), comment="是否为目录分享")
source_id = Column(Integer, nullable=False, comment="源文件或目录的ID")
views = Column(Integer, nullable=False, server_default='0', comment="浏览次数")
downloads = Column(Integer, nullable=False, server_default='0', comment="下载次数")
remain_downloads = Column(Integer, comment="剩余下载次数 (NULL为不限制)")
expires = Column(DateTime, comment="过期时间 (NULL为永不过期)")
preview_enabled = Column(Boolean, nullable=False, server_default=text('true'), comment="是否允许预览")
source_name = Column(String(255), index=True, comment="源名称(冗余字段,便于展示)")
score = Column(Integer, nullable=False, server_default='0', comment="分享评分/权重")
user_id = Column(Integer, ForeignKey('users.id'), nullable=False, index=True, comment="创建分享的用户ID")
class Task(BaseModel):
__tablename__ = 'tasks'
status = Column(Integer, nullable=False, server_default='0', comment="任务状态: 0=排队中, 1=处理中, 2=完成, 3=错误")
type = Column(Integer, nullable=False, comment="任务类型")
progress = Column(Integer, nullable=False, server_default='0', comment="任务进度 (0-100)")
error = Column(Text, comment="错误信息")
props = Column(Text, comment="任务属性 (JSON格式)")
user_id = Column(Integer, ForeignKey('users.id'), nullable=False, index=True, comment="所属用户ID")
class User(BaseModel):
__tablename__ = 'users'
email = Column(String(100), nullable=False, unique=True, index=True, comment="用户邮箱,唯一")
nick = Column(String(50), comment="用户昵称")
password = Column(String(255), nullable=False, comment="用户密码(加密后)")
status = Column(Integer, nullable=False, server_default='0', comment="用户状态: 0=正常, 1=未激活, 2=封禁")
storage = Column(BigInteger, nullable=False, server_default='0', comment="已用存储空间(字节)")
two_factor = Column(String(255), comment="两步验证密钥")
avatar = Column(String(255), comment="头像地址")
options = Column(Text, comment="用户个人设置 (JSON格式)")
authn = Column(Text, comment="WebAuthn 凭证")
open_id = Column(String(255), unique=True, index=True, nullable=True, comment="第三方登录OpenID")
score = Column(Integer, nullable=False, server_default='0', comment="用户积分")
group_expires = Column(DateTime, comment="当前用户组过期时间")
phone = Column(String(255), unique=True, nullable=True, index=True, comment="手机号")
group_id = Column(Integer, ForeignKey('groups.id'), nullable=False, index=True, comment="所属用户组ID")
previous_group_id = Column(Integer, ForeignKey('groups.id'), nullable=True, comment="之前的用户组ID用于过期后恢复")
class Redeem(BaseModel):
__tablename__ = 'redeems'
type = Column(Integer, nullable=False, comment="兑换码类型")
product_id = Column(BigInteger, comment="关联的商品/权益ID")
num = Column(Integer, nullable=False, server_default='1', comment="可兑换数量/时长等")
code = Column(Text, nullable=False, unique=True, index=True, comment="兑换码,唯一")
used = Column(Boolean, nullable=False, server_default=text('false'), comment="是否已使用")
class Report(BaseModel):
__tablename__ = 'reports'
share_id = Column(Integer, ForeignKey('shares.id'), index=True, nullable=False, comment="被举报的分享ID")
reason = Column(Integer, nullable=False, comment="举报原因代码")
description = Column(String(255), comment="补充描述")
class SourceLink(BaseModel):
__tablename__ = 'source_links'
file_id = Column(Integer, ForeignKey('files.id'), nullable=False, index=True, comment="关联的文件ID")
name = Column(String(255), nullable=False, comment="链接名称")
downloads = Column(Integer, nullable=False, server_default='0', comment="通过此链接的下载次数")
class StoragePack(BaseModel):
__tablename__ = 'storage_packs'
name = Column(String(255), nullable=False, comment="容量包名称")
active_time = Column(DateTime, comment="激活时间")
expired_time = Column(DateTime, index=True, comment="过期时间")
size = Column(BigInteger, nullable=False, comment="容量包大小(字节)")
user_id = Column(Integer, ForeignKey('users.id'), nullable=False, index=True, comment="所属用户ID")
class Tag(BaseModel):
__tablename__ = 'tags'
name = Column(String(255), nullable=False, comment="标签名称")
icon = Column(String(255), comment="标签图标")
color = Column(String(255), comment="标签颜色")
type = Column(Integer, nullable=False, server_default='0', comment="标签类型: 0=手动, 1=自动")
expression = Column(Text, comment="自动标签的匹配表达式")
user_id = Column(Integer, ForeignKey('users.id'), nullable=False, index=True, comment="所属用户ID")
__table_args__ = (
UniqueConstraint('name', 'user_id', name='uq_tag_name_user'),
)
class WebDAV(BaseModel):
__tablename__ = 'webdavs'
name = Column(String(255), nullable=False, comment="WebDAV账户名")
password = Column(String(255), nullable=False, comment="WebDAV密码加密后")
root = Column(Text, nullable=False, server_default="'/'", comment="根目录路径")
readonly = Column(Boolean, nullable=False, server_default=text('false'), comment="是否只读")
use_proxy = Column(Boolean, nullable=False, server_default=text('false'), comment="是否使用代理下载")
user_id = Column(Integer, ForeignKey('users.id'), nullable=False, index=True, comment="所属用户ID")
__table_args__ = (
UniqueConstraint('name', 'user_id', name='uq_webdav_name_user'),
)

45
models/node.py Normal file
View File

@@ -0,0 +1,45 @@
# my_project/models/node.py
from typing import Optional, TYPE_CHECKING
from sqlmodel import Field, Relationship, text, Column, func, DateTime
from .base import BaseModel
from datetime import datetime
if TYPE_CHECKING:
from .download import Download
class Node(BaseModel, table=True):
__tablename__ = 'nodes'
status: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="节点状态: 0=正常, 1=离线")
name: str = Field(max_length=255, unique=True, description="节点名称")
type: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="节点类型")
server: str = Field(max_length=255, description="节点地址IP或域名")
slave_key: Optional[str] = Field(default=None, description="从机通讯密钥")
master_key: Optional[str] = Field(default=None, description="主机通讯密钥")
aria2_enabled: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}, description="是否启用Aria2")
aria2_options: Optional[str] = Field(default=None, description="Aria2配置 (JSON格式)")
rank: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="节点排序权重")
created_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
comment="创建时间",
),
)
updated_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
comment="更新时间",
),
)
# 关系
downloads: list["Download"] = Relationship(back_populates="node")

47
models/order.py Normal file
View File

@@ -0,0 +1,47 @@
# my_project/models/order.py
from typing import Optional, TYPE_CHECKING
from sqlmodel import Field, Relationship, Column, func, DateTime
from .base import BaseModel
from datetime import datetime
if TYPE_CHECKING:
from .user import User
class Order(BaseModel, table=True):
__tablename__ = 'orders'
order_no: str = Field(max_length=255, unique=True, index=True, description="订单号,唯一")
type: int = Field(description="订单类型")
method: Optional[str] = Field(default=None, max_length=255, description="支付方式")
product_id: Optional[int] = Field(default=None, description="商品ID")
num: int = Field(default=1, sa_column_kwargs={"server_default": "1"}, description="购买数量")
name: str = Field(max_length=255, description="商品名称")
price: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="订单价格(分)")
status: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="订单状态: 0=待支付, 1=已完成, 2=已取消")
created_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
comment="创建时间",
),
)
updated_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
comment="更新时间",
),
)
# 外键
user_id: int = Field(foreign_key="users.id", index=True, description="所属用户ID")
# 关系
user: "User" = Relationship(back_populates="orders")

52
models/policy.py Normal file
View File

@@ -0,0 +1,52 @@
# my_project/models/policy.py
from typing import Optional, List, TYPE_CHECKING
from sqlmodel import Field, Relationship, text, Column, func, DateTime
from .base import BaseModel
from datetime import datetime
if TYPE_CHECKING:
from .file import File
from .folder import Folder
class Policy(BaseModel, table=True):
__tablename__ = 'policies'
name: str = Field(max_length=255, unique=True, description="策略名称")
type: str = Field(max_length=255, description="存储类型 (e.g. 'local', 's3')")
server: Optional[str] = Field(default=None, max_length=255, description="服务器地址(本地策略为路径)")
bucket_name: Optional[str] = Field(default=None, max_length=255, description="存储桶名称")
is_private: bool = Field(default=True, sa_column_kwargs={"server_default": text("true")}, description="是否为私有空间")
base_url: Optional[str] = Field(default=None, max_length=255, description="访问文件的基础URL")
access_key: Optional[str] = Field(default=None, description="Access Key")
secret_key: Optional[str] = Field(default=None, description="Secret Key")
max_size: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="允许上传的最大文件尺寸(字节)")
auto_rename: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}, description="是否自动重命名")
dir_name_rule: Optional[str] = Field(default=None, max_length=255, description="目录命名规则")
file_name_rule: Optional[str] = Field(default=None, max_length=255, description="文件命名规则")
is_origin_link_enable: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}, description="是否开启源链接访问")
options: Optional[str] = Field(default=None, description="其他选项 (JSON格式)")
created_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
comment="创建时间",
),
)
updated_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
comment="更新时间",
),
)
# 关系
files: List["File"] = Relationship(back_populates="policy")
folders: List["Folder"] = Relationship(back_populates="policy")

35
models/redeem.py Normal file
View File

@@ -0,0 +1,35 @@
# my_project/models/redeem.py
from typing import Optional
from sqlmodel import Field, text, Column, func, DateTime
from .base import BaseModel
from datetime import datetime
class Redeem(BaseModel, table=True):
__tablename__ = 'redeems'
type: int = Field(description="兑换码类型")
product_id: Optional[int] = Field(default=None, description="关联的商品/权益ID")
num: int = Field(default=1, sa_column_kwargs={"server_default": "1"}, description="可兑换数量/时长等")
code: str = Field(unique=True, index=True, description="兑换码,唯一")
used: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}, description="是否已使用")
created_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
comment="创建时间",
),
)
updated_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
comment="更新时间",
),
)

41
models/report.py Normal file
View File

@@ -0,0 +1,41 @@
# my_project/models/report.py
from typing import Optional, TYPE_CHECKING
from sqlmodel import Field, Relationship, Column, func, DateTime
from .base import BaseModel
from datetime import datetime
if TYPE_CHECKING:
from .share import Share
class Report(BaseModel, table=True):
__tablename__ = 'reports'
reason: int = Field(description="举报原因代码")
description: Optional[str] = Field(default=None, max_length=255, description="补充描述")
created_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
comment="创建时间",
),
)
updated_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
comment="更新时间",
),
)
# 外键
share_id: int = Field(foreign_key="shares.id", index=True, description="被举报的分享ID")
# 关系
share: "Share" = Relationship(back_populates="reports")

View File

@@ -1,7 +1,20 @@
from pydantic import BaseModel, Field
from typing import Union, Optional
from typing import Literal, Union, Optional
from uuid import uuid4
class ResponseModel(BaseModel):
code: int = Field(default=0, description="系统内部状态码, 0表示成功其他表示失败", lt=60000, gt=0)
data: Union[dict, list, str, int, float, None] = Field(None, description="响应数据")
msg: Optional[str] = Field(default=None, description="响应消息,可以是错误消息或信息提示")
msg: Optional[str] = Field(default=None, description="响应消息,可以是错误消息或信息提示")
instance_id: str = Field(default_factory=lambda: str(uuid4()), description="实例ID用于标识请求的唯一性")
class SiteConfigModel(ResponseModel):
title: str = Field(default="DiskNext", description="网站标题")
themes: dict = Field(default_factory=dict, description="网站主题配置")
default_theme: str = Field(default="default", description="默认主题RGB色号")
site_notice: Optional[str] = Field(default=None, description="网站公告")
user: dict = Field(default_factory=dict, description="用户信息")
logo_light: Optional[str] = Field(default=None, description="网站Logo URL")
logo_dark: Optional[str] = Field(default=None, description="网站Logo URL深色模式")
captcha_type: Literal['none', 'default', 'gcaptcha', 'cloudflare turnstile'] = Field(default='none', description="验证码类型")
captcha_key: Optional[str] = Field(default=None, description="验证码密钥")

34
models/setting.py Normal file
View File

@@ -0,0 +1,34 @@
# my_project/models/setting.py
from typing import Optional
from sqlmodel import Field, UniqueConstraint, Column, func, DateTime
from .base import BaseModel
from datetime import datetime
class Setting(BaseModel, table=True):
__tablename__ = 'settings'
__table_args__ = (UniqueConstraint("type", "name", name="uq_setting_type_name"),)
type: str = Field(max_length=255, description="设置类型/分组")
name: str = Field(max_length=255, description="设置项名称")
value: Optional[str] = Field(default=None, description="设置值")
created_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
comment="创建时间",
),
)
updated_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
comment="更新时间",
),
)

61
models/share.py Normal file
View File

@@ -0,0 +1,61 @@
# my_project/models/share.py
from typing import Optional, TYPE_CHECKING
from datetime import datetime
from sqlmodel import Field, Relationship, text, Column, func, DateTime
from .base import BaseModel
from datetime import datetime
if TYPE_CHECKING:
from .user import User
from .report import Report
class Share(BaseModel, table=True):
__tablename__ = 'shares'
password: Optional[str] = Field(default=None, max_length=255, description="分享密码(加密后)")
is_dir: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}, description="是否为目录分享")
source_id: int = Field(description="源文件或目录的ID")
views: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="浏览次数")
downloads: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="下载次数")
remain_downloads: Optional[int] = Field(default=None, description="剩余下载次数 (NULL为不限制)")
expires: Optional[datetime] = Field(default=None, description="过期时间 (NULL为永不过期)")
preview_enabled: bool = Field(default=True, sa_column_kwargs={"server_default": text("true")}, description="是否允许预览")
source_name: Optional[str] = Field(default=None, max_length=255, index=True, description="源名称(冗余字段,便于展示)")
score: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="兑换此分享所需的积分")
created_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
comment="创建时间",
),
)
updated_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
comment="更新时间",
),
)
delete_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=True,
comment="删除时间",
),
)
# 外键
user_id: int = Field(foreign_key="users.id", index=True, description="创建分享的用户ID")
# 关系
user: "User" = Relationship(back_populates="shares")
reports: list["Report"] = Relationship(back_populates="share")

50
models/source_link.py Normal file
View File

@@ -0,0 +1,50 @@
# my_project/models/source_link.py
from typing import TYPE_CHECKING, Optional
from sqlmodel import Field, Relationship, Column, func, DateTime
from .base import BaseModel
from datetime import datetime
if TYPE_CHECKING:
from .file import File
class SourceLink(BaseModel, table=True):
__tablename__ = 'source_links'
name: str = Field(max_length=255, description="链接名称")
downloads: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="通过此链接的下载次数")
created_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
comment="创建时间",
),
)
updated_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
comment="更新时间",
),
)
delete_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=True,
comment="删除时间",
),
)
# 外键
file_id: int = Field(foreign_key="files.id", index=True, description="关联的文件ID")
# 关系
file: "File" = Relationship(back_populates="source_links")

52
models/storage_pack.py Normal file
View File

@@ -0,0 +1,52 @@
# my_project/models/storage_pack.py
from typing import Optional, TYPE_CHECKING
from datetime import datetime
from sqlmodel import Field, Relationship, Column, func, DateTime
from .base import BaseModel
from datetime import datetime
if TYPE_CHECKING:
from .user import User
class StoragePack(BaseModel, table=True):
__tablename__ = 'storage_packs'
name: str = Field(max_length=255, description="容量包名称")
active_time: Optional[datetime] = Field(default=None, description="激活时间")
expired_time: Optional[datetime] = Field(default=None, index=True, description="过期时间")
size: int = Field(description="容量包大小(字节)")
created_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
comment="创建时间",
),
)
updated_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
comment="更新时间",
),
)
delete_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=True,
comment="删除时间",
),
)
# 外键
user_id: int = Field(foreign_key="users.id", index=True, description="所属用户ID")
# 关系
user: "User" = Relationship(back_populates="storage_packs")

54
models/tag.py Normal file
View File

@@ -0,0 +1,54 @@
# my_project/models/tag.py
from typing import Optional, TYPE_CHECKING
from sqlmodel import Field, Relationship, UniqueConstraint, Column, func, DateTime
from .base import BaseModel
from datetime import datetime
if TYPE_CHECKING:
from .user import User
class Tag(BaseModel, table=True):
__tablename__ = 'tags'
__table_args__ = (UniqueConstraint("name", "user_id", name="uq_tag_name_user"),)
name: str = Field(max_length=255, description="标签名称")
icon: Optional[str] = Field(default=None, max_length=255, description="标签图标")
color: Optional[str] = Field(default=None, max_length=255, description="标签颜色")
type: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="标签类型: 0=手动, 1=自动")
expression: Optional[str] = Field(default=None, description="自动标签的匹配表达式")
created_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
comment="创建时间",
),
)
updated_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
comment="更新时间",
),
)
delete_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=True,
comment="删除时间",
),
)
# 外键
user_id: int = Field(foreign_key="users.id", index=True, description="所属用户ID")
# 关系
user: "User" = Relationship(back_populates="tags")

55
models/task.py Normal file
View File

@@ -0,0 +1,55 @@
# my_project/models/task.py
from typing import Optional, TYPE_CHECKING
from sqlmodel import Field, Relationship, Column, func, DateTime
from .base import BaseModel
from datetime import datetime
if TYPE_CHECKING:
from .user import User
from .download import Download
class Task(BaseModel, table=True):
__tablename__ = 'tasks'
status: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="任务状态: 0=排队中, 1=处理中, 2=完成, 3=错误")
type: int = Field(description="任务类型")
progress: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="任务进度 (0-100)")
error: Optional[str] = Field(default=None, description="错误信息")
props: Optional[str] = Field(default=None, description="任务属性 (JSON格式)")
created_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
comment="创建时间",
),
)
updated_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
comment="更新时间",
),
)
delete_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=True,
comment="删除时间",
),
)
# 外键
user_id: "int" = Field(foreign_key="users.id", index=True, description="所属用户ID")
# 关系
user: "User" = Relationship(back_populates="tasks")
downloads: list["Download"] = Relationship(back_populates="task")

83
models/user.py Normal file
View File

@@ -0,0 +1,83 @@
# my_project/models/user.py
from typing import Optional, TYPE_CHECKING
from datetime import datetime
from sqlmodel import Field, Relationship, Column, func, DateTime
from .base import BaseModel
# TYPE_CHECKING 用于解决循环导入问题,只在类型检查时导入
if TYPE_CHECKING:
from .group import Group
from .download import Download
from .file import File
from .folder import Folder
from .order import Order
from .share import Share
from .storage_pack import StoragePack
from .tag import Tag
from .task import Task
from .webdav import WebDAV
class User(BaseModel, table=True):
__tablename__ = 'users'
email: str = Field(max_length=100, unique=True, index=True, description="用户邮箱,唯一")
nick: Optional[str] = Field(default=None, max_length=50, description="用户昵称")
password: str = Field(max_length=255, description="用户密码(加密后)")
status: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="用户状态: 0=正常, 1=未激活, 2=封禁")
storage: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="已用存储空间(字节)")
two_factor: Optional[str] = Field(default=None, max_length=255, description="两步验证密钥")
avatar: Optional[str] = Field(default=None, max_length=255, description="头像地址")
options: Optional[str] = Field(default=None, description="用户个人设置 (JSON格式)")
authn: Optional[str] = Field(default=None, description="WebAuthn 凭证")
open_id: Optional[str] = Field(default=None, max_length=255, unique=True, index=True, description="第三方登录OpenID")
score: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="用户积分")
group_expires: Optional[datetime] = Field(default=None, description="当前用户组过期时间")
phone: Optional[str] = Field(default=None, max_length=255, unique=True, index=True, description="手机号")
created_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
comment="创建时间",
),
)
updated_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
comment="更新时间",
),
)
delete_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=True,
comment="删除时间",
),
)
# 外键
group_id: int = Field(foreign_key="groups.id", index=True, description="所属用户组ID")
previous_group_id: Optional[int] = Field(default=None, foreign_key="groups.id", description="之前的用户组ID用于过期后恢复")
# 关系
group: "Group" = Relationship(back_populates="users")
previous_group: Optional["Group"] = Relationship(back_populates="previous_users")
downloads: list["Download"] = Relationship(back_populates="user")
files: list["File"] = Relationship(back_populates="user")
folders: list["Folder"] = Relationship(back_populates="owner")
orders: list["Order"] = Relationship(back_populates="user")
shares: list["Share"] = Relationship(back_populates="user")
storage_packs: list["StoragePack"] = Relationship(back_populates="user")
tags: list["Tag"] = Relationship(back_populates="user")
tasks: list["Task"] = Relationship(back_populates="user")
webdavs: list["WebDAV"] = Relationship(back_populates="user")

54
models/webdav.py Normal file
View File

@@ -0,0 +1,54 @@
# my_project/models/webdav.py
from typing import Optional, TYPE_CHECKING
from sqlmodel import Field, Relationship, UniqueConstraint, text, Column, func, DateTime
from .base import BaseModel
from datetime import datetime
if TYPE_CHECKING:
from .user import User
class WebDAV(BaseModel, table=True):
__tablename__ = 'webdavs'
__table_args__ = (UniqueConstraint("name", "user_id", name="uq_webdav_name_user"),)
name: str = Field(max_length=255, description="WebDAV账户名")
password: str = Field(max_length=255, description="WebDAV密码加密后")
root: str = Field(default="/", sa_column_kwargs={"server_default": "'/'"}, description="根目录路径")
readonly: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}, description="是否只读")
use_proxy: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}, description="是否使用代理下载")
created_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
comment="创建时间",
),
)
updated_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=False,
server_default=func.now(),
onupdate=func.now(),
comment="更新时间",
),
)
delete_at: Optional[datetime] = Field(
default=None,
sa_column=Column(
DateTime,
nullable=True,
comment="删除时间",
),
)
# 外键
user_id: int = Field(foreign_key="users.id", index=True, description="所属用户ID")
# 关系
user: "User" = Relationship(back_populates="webdavs")