feat: Enhance models and README with additional features and improvements
- Updated README to include KodBox in project vision. - Added model descriptions for clarity in Download, File, Folder, Group, Node, Order, Policy, Redeem, Report, Request, Response, Setting, Share, SourceLink, StoragePack, Tag, Task, User, and WebDAV. - Changed optional fields from Optional[...] to the new union type syntax (e.g., str | None). - Improved foreign key references in models for consistency. - Refactored relationships in models to use singular forms where appropriate. - Updated login service to reflect changes in request model types.
This commit is contained in:
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
本来此项目并没有这么快开始,但由于我曾经使用过的某个类似产品的作者吃相实在难看,故决定自己实现一个。
|
本来此项目并没有这么快开始,但由于我曾经使用过的某个类似产品的作者吃相实在难看,故决定自己实现一个。
|
||||||
|
|
||||||
此项目的愿景是集百家之长(Cloudreve + Alist + FnOS)。你也可以考虑付费支持我们的发展 -> `DiskNext Pro`.
|
此项目的愿景是集百家之长(Cloudreve + Alist + FnOS + KodBox)。你也可以考虑付费支持我们的发展 -> `DiskNext Pro`.
|
||||||
|
|
||||||
目前正处于 `OMEGA` 实验阶段,比 `Alpha` 版还更早期,仅供测试。
|
目前正处于 `OMEGA` 实验阶段,比 `Alpha` 版还更早期,仅供测试。
|
||||||
|
|
||||||
@@ -25,12 +25,12 @@
|
|||||||
- 在线预览/编辑多种文件,包括但不限于视频、图片、音频、PDF、ePub、Office、Markdown、图表等
|
- 在线预览/编辑多种文件,包括但不限于视频、图片、音频、PDF、ePub、Office、Markdown、图表等
|
||||||
- 自定义主题色、深浅色主题、PWA、i18n
|
- 自定义主题色、深浅色主题、PWA、i18n
|
||||||
|
|
||||||
## :alembic: 技术栈
|
## ⚗️ 技术栈
|
||||||
|
|
||||||
* [Python](https://www.python.org/) + [FastAPI](https://fastapi.tiangolo.com/)
|
* [Python](https://www.python.org/) + [FastAPI](https://fastapi.tiangolo.com/)
|
||||||
|
|
||||||
<!-- * [React](https://github.com/facebook/react) + [Redux](https://github.com/reduxjs/redux) + [Material-UI](https://github.com/mui-org/material-ui) -->
|
<!-- * [React](https://github.com/facebook/react) + [Redux](https://github.com/reduxjs/redux) + [Material-UI](https://github.com/mui-org/material-ui) -->
|
||||||
|
|
||||||
## :scroll: 许可证
|
## 📜 许可证
|
||||||
|
|
||||||
GPL V3
|
GPL V3
|
||||||
@@ -11,7 +11,8 @@ class DownloadBase(SQLModelBase):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class Download(DownloadBase, UUIDTableBase, table=True):
|
class Download(DownloadBase, UUIDTableBase, table=True):
|
||||||
__tablename__ = 'downloads'
|
"""离线下载任务模型"""
|
||||||
|
|
||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
UniqueConstraint("node_id", "g_id", name="uq_download_node_gid"),
|
UniqueConstraint("node_id", "g_id", name="uq_download_node_gid"),
|
||||||
)
|
)
|
||||||
@@ -21,18 +22,18 @@ class Download(DownloadBase, UUIDTableBase, table=True):
|
|||||||
source: str = Field(description="来源URL或标识")
|
source: str = Field(description="来源URL或标识")
|
||||||
total_size: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="总大小(字节)")
|
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="已下载大小(字节)")
|
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")
|
g_id: str | None = Field(default=None, index=True, description="Aria2 GID")
|
||||||
speed: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="下载速度 (bytes/s)")
|
speed: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="下载速度 (bytes/s)")
|
||||||
parent: Optional[str] = Field(default=None, description="父任务标识")
|
parent: str | None = Field(default=None, description="父任务标识")
|
||||||
attrs: Optional[str] = Field(default=None, description="额外属性 (JSON格式)")
|
attrs: str | None = Field(default=None, description="额外属性 (JSON格式)")
|
||||||
# attrs 示例: {"gid":"65c5faf38374cc63","status":"removed","totalLength":"0","completedLength":"0","uploadLength":"0","bitfield":"","downloadSpeed":"0","uploadSpeed":"0","infoHash":"ca159db2b1e78f6e95fd972be72251f967f639d4","numSeeders":"0","seeder":"","pieceLength":"16384","numPieces":"0","connections":"0","errorCode":"31","errorMessage":"","followedBy":null,"belongsTo":"","dir":"/data/ccaaDown/aria2/7a208304-9126-46d2-ba47-a6959f236a07","files":[{"index":"1","path":"[METADATA]zh-cn_windows_11_consumer_editions_version_21h2_updated_aug_2022_x64_dvd_a29983d5.iso","length":"0","completedLength":"0","selected":"true","uris":[]}],"bittorrent":{"announceList":[["udp://tracker.opentrackr.org:1337/announce"],["udp://9.rarbg.com:2810/announce"],["udp://tracker.openbittorrent.com:6969/announce"],["https://opentracker.i2p.rocks:443/announce"],["http://tracker.openbittorrent.com:80/announce"],["udp://open.stealth.si:80/announce"],["udp://tracker.torrent.eu.org:451/announce"],["udp://exodus.desync.com:6969/announce"],["udp://tracker.tiny-vps.com:6969/announce"],["udp://tracker.pomf.se:80/announce"],["udp://tracker.moeking.me:6969/announce"],["udp://tracker.dler.org:6969/announce"],["udp://open.demonii.com:1337/announce"],["udp://explodie.org:6969/announce"],["udp://chouchou.top:8080/announce"],["udp://bt.oiyo.tk:6969/announce"],["https://tracker.nanoha.org:443/announce"],["https://tracker.lilithraws.org:443/announce"],["http://tracker3.ctix.cn:8080/announce"],["http://tracker.nucozer-tracker.ml:2710/announce"]],"comment":"","creationDate":0,"mode":"","info":{"name":""}}}
|
# attrs 示例: {"gid":"65c5faf38374cc63","status":"removed","totalLength":"0","completedLength":"0","uploadLength":"0","bitfield":"","downloadSpeed":"0","uploadSpeed":"0","infoHash":"ca159db2b1e78f6e95fd972be72251f967f639d4","numSeeders":"0","seeder":"","pieceLength":"16384","numPieces":"0","connections":"0","errorCode":"31","errorMessage":"","followedBy":null,"belongsTo":"","dir":"/data/ccaaDown/aria2/7a208304-9126-46d2-ba47-a6959f236a07","files":[{"index":"1","path":"[METADATA]zh-cn_windows_11_consumer_editions_version_21h2_updated_aug_2022_x64_dvd_a29983d5.iso","length":"0","completedLength":"0","selected":"true","uris":[]}],"bittorrent":{"announceList":[["udp://tracker.opentrackr.org:1337/announce"],["udp://9.rarbg.com:2810/announce"],["udp://tracker.openbittorrent.com:6969/announce"],["https://opentracker.i2p.rocks:443/announce"],["http://tracker.openbittorrent.com:80/announce"],["udp://open.stealth.si:80/announce"],["udp://tracker.torrent.eu.org:451/announce"],["udp://exodus.desync.com:6969/announce"],["udp://tracker.tiny-vps.com:6969/announce"],["udp://tracker.pomf.se:80/announce"],["udp://tracker.moeking.me:6969/announce"],["udp://tracker.dler.org:6969/announce"],["udp://open.demonii.com:1337/announce"],["udp://explodie.org:6969/announce"],["udp://chouchou.top:8080/announce"],["udp://bt.oiyo.tk:6969/announce"],["https://tracker.nanoha.org:443/announce"],["https://tracker.lilithraws.org:443/announce"],["http://tracker3.ctix.cn:8080/announce"],["http://tracker.nucozer-tracker.ml:2710/announce"]],"comment":"","creationDate":0,"mode":"","info":{"name":""}}}
|
||||||
error: Optional[str] = Field(default=None, description="错误信息")
|
error: str | None = Field(default=None, description="错误信息")
|
||||||
dst: str = Field(description="目标存储路径")
|
dst: str = Field(description="目标存储路径")
|
||||||
|
|
||||||
# 外键
|
# 外键
|
||||||
user_id: int = Field(foreign_key="users.id", index=True, description="所属用户ID")
|
user_id: int = Field(foreign_key="user.id", index=True, description="所属用户ID")
|
||||||
task_id: Optional[int] = Field(default=None, foreign_key="tasks.id", index=True, description="关联的任务ID")
|
task_id: int | None = Field(default=None, foreign_key="task.id", index=True, description="关联的任务ID")
|
||||||
node_id: int = Field(foreign_key="nodes.id", index=True, description="执行下载的节点ID")
|
node_id: int = Field(foreign_key="node.id", index=True, description="执行下载的节点ID")
|
||||||
|
|
||||||
# 关系
|
# 关系
|
||||||
user: "User" = Relationship(back_populates="downloads")
|
user: "User" = Relationship(back_populates="downloads")
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
|
|
||||||
from typing import Optional, TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from sqlmodel import Field, Relationship, UniqueConstraint, CheckConstraint, Index
|
from sqlmodel import Field, Relationship, UniqueConstraint, CheckConstraint, Index
|
||||||
from .base import TableBase
|
from .base import TableBase
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .user import User
|
from .user import User
|
||||||
@@ -11,7 +10,6 @@ if TYPE_CHECKING:
|
|||||||
from .source_link import SourceLink
|
from .source_link import SourceLink
|
||||||
|
|
||||||
class File(TableBase, table=True):
|
class File(TableBase, table=True):
|
||||||
__tablename__ = 'files'
|
|
||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
UniqueConstraint("folder_id", "name", name="uq_file_folder_name_active"),
|
UniqueConstraint("folder_id", "name", name="uq_file_folder_name_active"),
|
||||||
CheckConstraint("name NOT LIKE '%/%' AND name NOT LIKE '%\\%'", name="ck_file_name_no_slash"),
|
CheckConstraint("name NOT LIKE '%/%' AND name NOT LIKE '%\\%'", name="ck_file_name_no_slash"),
|
||||||
@@ -21,18 +19,18 @@ class File(TableBase, table=True):
|
|||||||
)
|
)
|
||||||
|
|
||||||
name: str = Field(max_length=255, description="文件名")
|
name: str = Field(max_length=255, description="文件名")
|
||||||
source_name: Optional[str] = Field(default=None, description="源文件名")
|
source_name: str | None = Field(default=None, description="源文件名")
|
||||||
size: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="文件大小(字节)")
|
size: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="文件大小(字节)")
|
||||||
upload_session_id: Optional[str] = Field(default=None, max_length=255, unique=True, index=True, description="分块上传会话ID")
|
upload_session_id: str | None = Field(default=None, max_length=255, unique=True, index=True, description="分块上传会话ID")
|
||||||
file_metadata: Optional[str] = Field(default=None, description="文件元数据 (JSON格式)") # 后续可以考虑模型继承?
|
file_metadata: str | None = Field(default=None, description="文件元数据 (JSON格式)") # 后续可以考虑模型继承?
|
||||||
|
|
||||||
# 外键
|
# 外键
|
||||||
user_id: int = Field(foreign_key="users.id", index=True, description="所属用户ID")
|
user_id: int = Field(foreign_key="user.id", index=True, description="所属用户ID")
|
||||||
folder_id: int = Field(foreign_key="folders.id", index=True, description="所在目录ID")
|
folder_id: int = Field(foreign_key="folder.id", index=True, description="所在目录ID")
|
||||||
policy_id: int = Field(foreign_key="policies.id", index=True, description="所属存储策略ID")
|
policy_id: int = Field(foreign_key="policy.id", index=True, description="所属存储策略ID")
|
||||||
|
|
||||||
# 关系
|
# 关系
|
||||||
user: list["User"] = Relationship(back_populates="files")
|
user: "User" = Relationship(back_populates="files")
|
||||||
folder: list["Folder"] = Relationship(back_populates="files")
|
folder: "Folder" = Relationship(back_populates="files")
|
||||||
policy: list["Policy"] = Relationship(back_populates="files")
|
policy: "Policy" = Relationship(back_populates="files")
|
||||||
source_links: list["SourceLink"] = Relationship(back_populates="file")
|
source_links: list["SourceLink"] = Relationship(back_populates="file")
|
||||||
@@ -10,7 +10,6 @@ if TYPE_CHECKING:
|
|||||||
from .file import File
|
from .file import File
|
||||||
|
|
||||||
class Folder(TableBase, table=True):
|
class Folder(TableBase, table=True):
|
||||||
__tablename__ = 'folders'
|
|
||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
UniqueConstraint(
|
UniqueConstraint(
|
||||||
"owner_id",
|
"owner_id",
|
||||||
@@ -27,9 +26,9 @@ class Folder(TableBase, table=True):
|
|||||||
name: str = Field(max_length=255, nullable=False, description="目录名")
|
name: str = Field(max_length=255, nullable=False, description="目录名")
|
||||||
|
|
||||||
# 外键
|
# 外键
|
||||||
parent_id: Optional[int] = Field(default=None, foreign_key="folders.id", index=True, description="父目录ID")
|
parent_id: int | None = Field(default=None, foreign_key="folder.id", index=True, description="父目录ID")
|
||||||
owner_id: int = Field(foreign_key="users.id", index=True, description="所有者用户ID")
|
owner_id: int = Field(foreign_key="user.id", index=True, description="所有者用户ID")
|
||||||
policy_id: int = Field(foreign_key="policies.id", index=True, description="所属存储策略ID")
|
policy_id: int = Field(foreign_key="policy.id", index=True, description="所属存储策略ID")
|
||||||
|
|
||||||
# 关系
|
# 关系
|
||||||
owner: "User" = Relationship(back_populates="folders")
|
owner: "User" = Relationship(back_populates="folders")
|
||||||
|
|||||||
@@ -8,24 +8,24 @@ if TYPE_CHECKING:
|
|||||||
from .user import User
|
from .user import User
|
||||||
|
|
||||||
class GroupOptions(SQLModel):
|
class GroupOptions(SQLModel):
|
||||||
archive_download: Optional[bool] = False
|
archive_download: bool | None = False
|
||||||
archive_task: Optional[bool] = False
|
archive_task: bool | None = False
|
||||||
share_download: Optional[bool] = False
|
share_download: bool | None = False
|
||||||
share_free: Optional[bool] = False
|
share_free: bool | None = False
|
||||||
webdav_proxy: Optional[bool] = False
|
webdav_proxy: bool | None = False
|
||||||
aria2: Optional[bool] = False
|
aria2: bool | None = False
|
||||||
relocate: Optional[bool] = False
|
relocate: bool | None = False
|
||||||
source_batch: Optional[int] = 10
|
source_batch: int | None = 10
|
||||||
redirected_source: Optional[bool] = False
|
redirected_source: bool | None = False
|
||||||
available_nodes: Optional[List[int]] = []
|
available_nodes: List[int] | None = []
|
||||||
select_node: Optional[bool] = False
|
select_node: bool | None = False
|
||||||
advance_delete: Optional[bool] = False
|
advance_delete: bool | None = False
|
||||||
|
|
||||||
class Group(TableBase, table=True):
|
class Group(TableBase, table=True):
|
||||||
__tablename__ = 'groups'
|
"""用户组模型"""
|
||||||
|
|
||||||
name: str = Field(max_length=255, unique=True, description="用户组名")
|
name: str = Field(max_length=255, unique=True, description="用户组名")
|
||||||
policies: Optional[str] = Field(default=None, max_length=255, description="允许的策略ID列表,逗号分隔")
|
policies: str | None = Field(default=None, max_length=255, description="允许的策略ID列表,逗号分隔")
|
||||||
max_storage: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="最大存储空间(字节)")
|
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="是否允许创建分享")
|
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")
|
web_dav_enabled: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}, description="是否允许使用WebDAV")
|
||||||
@@ -34,11 +34,11 @@ class Group(TableBase, table=True):
|
|||||||
options: GroupOptions = Field(default=GroupOptions, sa_column=Column(JSON), description="其他选项")
|
options: GroupOptions = Field(default=GroupOptions, sa_column=Column(JSON), description="其他选项")
|
||||||
|
|
||||||
# 关系:一个组可以有多个用户
|
# 关系:一个组可以有多个用户
|
||||||
users: List["User"] = Relationship(
|
user: List["User"] = Relationship(
|
||||||
back_populates="group",
|
back_populates="group",
|
||||||
sa_relationship_kwargs={"foreign_keys": "User.group_id"}
|
sa_relationship_kwargs={"foreign_keys": "User.group_id"}
|
||||||
)
|
)
|
||||||
previous_users: List["User"] = Relationship(
|
previous_user: List["User"] = Relationship(
|
||||||
back_populates="previous_group",
|
back_populates="previous_group",
|
||||||
sa_relationship_kwargs={"foreign_keys": "User.previous_group_id"}
|
sa_relationship_kwargs={"foreign_keys": "User.previous_group_id"}
|
||||||
)
|
)
|
||||||
@@ -46,13 +46,13 @@ class Group(TableBase, table=True):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
async def create(
|
async def create(
|
||||||
group: Optional["Group"] = None,
|
group: Optional["Group"] = None,
|
||||||
name: Optional[str] = None,
|
name: str | None = None,
|
||||||
policies: Optional[str] = None,
|
policies: str | None = None,
|
||||||
max_storage: int = 0,
|
max_storage: int = 0,
|
||||||
share_enabled: bool = False,
|
share_enabled: bool = False,
|
||||||
web_dav_enabled: bool = False,
|
web_dav_enabled: bool = False,
|
||||||
speed_limit: int = 0,
|
speed_limit: int = 0,
|
||||||
options: Optional[dict] = None,
|
options: dict | None = None,
|
||||||
) -> "Group":
|
) -> "Group":
|
||||||
"""
|
"""
|
||||||
向数据库内添加用户组。如果提供了 `group` 参数,则使用该对象,否则创建一个新的用户组对象。
|
向数据库内添加用户组。如果提供了 `group` 参数,则使用该对象,否则创建一个新的用户组对象。
|
||||||
@@ -128,13 +128,13 @@ class Group(TableBase, table=True):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
async def set(
|
async def set(
|
||||||
id: int,
|
id: int,
|
||||||
name: Optional[str] = None,
|
name: str | None = None,
|
||||||
policies: Optional[str] = None,
|
policies: str | None = None,
|
||||||
max_storage: Optional[int] = None,
|
max_storage: int | None = None,
|
||||||
share_enabled: Optional[bool] = None,
|
share_enabled: bool | None = None,
|
||||||
web_dav_enabled: Optional[bool] = None,
|
web_dav_enabled: bool | None = None,
|
||||||
speed_limit: Optional[int] = None,
|
speed_limit: int | None = None,
|
||||||
options: Optional[str] = None
|
options: str | None = None
|
||||||
) -> Optional["Group"]:
|
) -> Optional["Group"]:
|
||||||
"""
|
"""
|
||||||
更新用户组信息。
|
更新用户组信息。
|
||||||
@@ -142,19 +142,19 @@ class Group(TableBase, table=True):
|
|||||||
:param id: 用户组ID
|
:param id: 用户组ID
|
||||||
:type id: int
|
:type id: int
|
||||||
:param name: 用户组名
|
:param name: 用户组名
|
||||||
:type name: Optional[str]
|
:type name: str | None
|
||||||
:param policies: 允许的策略ID列表,逗号分隔
|
:param policies: 允许的策略ID列表,逗号分隔
|
||||||
:type policies: Optional[str]
|
:type policies: str | None
|
||||||
:param max_storage: 最大存储空间(字节)
|
:param max_storage: 最大存储空间(字节)
|
||||||
:type max_storage: Optional[int]
|
:type max_storage: int | None
|
||||||
:param share_enabled: 是否允许创建分享
|
:param share_enabled: 是否允许创建分享
|
||||||
:type share_enabled: Optional[bool]
|
:type share_enabled: bool | None
|
||||||
:param web_dav_enabled: 是否允许使用WebDAV
|
:param web_dav_enabled: 是否允许使用WebDAV
|
||||||
:type web_dav_enabled: Optional[bool]
|
:type web_dav_enabled: bool | None
|
||||||
:param speed_limit: 速度限制 (KB/s), 0为不限制
|
:param speed_limit: 速度限制 (KB/s), 0为不限制
|
||||||
:type speed_limit: Optional[int]
|
:type speed_limit: int | None
|
||||||
:param options: 其他选项 (JSON格式)
|
:param options: 其他选项 (JSON格式)
|
||||||
:type options: Optional[str]
|
:type options: str | None
|
||||||
:return: 更新后的用户组对象或 None
|
:return: 更新后的用户组对象或 None
|
||||||
:rtype: Optional[Group]
|
:rtype: Optional[Group]
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,23 +1,22 @@
|
|||||||
|
|
||||||
from typing import Optional, TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from sqlmodel import Field, Relationship, text, Column, func, DateTime
|
from sqlmodel import Field, Relationship, text
|
||||||
from .base import TableBase
|
from .base import TableBase
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .download import Download
|
from .download import Download
|
||||||
|
|
||||||
class Node(TableBase, table=True):
|
class Node(TableBase, table=True):
|
||||||
__tablename__ = 'nodes'
|
"""节点模型"""
|
||||||
|
|
||||||
status: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="节点状态: 0=正常, 1=离线")
|
status: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="节点状态: 0=正常, 1=离线")
|
||||||
name: str = Field(max_length=255, unique=True, description="节点名称")
|
name: str = Field(max_length=255, unique=True, description="节点名称")
|
||||||
type: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="节点类型")
|
type: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="节点类型")
|
||||||
server: str = Field(max_length=255, description="节点地址(IP或域名)")
|
server: str = Field(max_length=255, description="节点地址(IP或域名)")
|
||||||
slave_key: Optional[str] = Field(default=None, description="从机通讯密钥")
|
slave_key: str | None = Field(default=None, description="从机通讯密钥")
|
||||||
master_key: Optional[str] = Field(default=None, description="主机通讯密钥")
|
master_key: str | None = Field(default=None, description="主机通讯密钥")
|
||||||
aria2_enabled: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}, description="是否启用Aria2")
|
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格式)")
|
aria2_options: str | None = Field(default=None, description="Aria2配置 (JSON格式)")
|
||||||
rank: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="节点排序权重")
|
rank: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="节点排序权重")
|
||||||
|
|
||||||
# 关系
|
# 关系
|
||||||
|
|||||||
@@ -1,26 +1,25 @@
|
|||||||
|
|
||||||
from typing import Optional, TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from sqlmodel import Field, Relationship, Column, func, DateTime
|
from sqlmodel import Field, Relationship
|
||||||
from .base import TableBase
|
from .base import TableBase
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .user import User
|
from .user import User
|
||||||
|
|
||||||
class Order(TableBase, table=True):
|
class Order(TableBase, table=True):
|
||||||
__tablename__ = 'orders'
|
"""订单模型"""
|
||||||
|
|
||||||
order_no: str = Field(max_length=255, unique=True, index=True, description="订单号,唯一")
|
order_no: str = Field(max_length=255, unique=True, index=True, description="订单号,唯一")
|
||||||
type: int = Field(description="订单类型")
|
type: int = Field(description="订单类型")
|
||||||
method: Optional[str] = Field(default=None, max_length=255, description="支付方式")
|
method: str | None = Field(default=None, max_length=255, description="支付方式")
|
||||||
product_id: Optional[int] = Field(default=None, description="商品ID")
|
product_id: int | None = Field(default=None, description="商品ID")
|
||||||
num: int = Field(default=1, sa_column_kwargs={"server_default": "1"}, description="购买数量")
|
num: int = Field(default=1, sa_column_kwargs={"server_default": "1"}, description="购买数量")
|
||||||
name: str = Field(max_length=255, description="商品名称")
|
name: str = Field(max_length=255, description="商品名称")
|
||||||
price: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, 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=已取消")
|
status: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="订单状态: 0=待支付, 1=已完成, 2=已取消")
|
||||||
|
|
||||||
# 外键
|
# 外键
|
||||||
user_id: int = Field(foreign_key="users.id", index=True, description="所属用户ID")
|
user_id: int = Field(foreign_key="user.id", index=True, description="所属用户ID")
|
||||||
|
|
||||||
# 关系
|
# 关系
|
||||||
user: "User" = Relationship(back_populates="orders")
|
user: "User" = Relationship(back_populates="orders")
|
||||||
@@ -1,30 +1,29 @@
|
|||||||
|
|
||||||
from typing import Optional, List, TYPE_CHECKING
|
from typing import Optional, List, TYPE_CHECKING
|
||||||
from sqlmodel import Field, Relationship, text, Column, func, DateTime
|
from sqlmodel import Field, Relationship, text
|
||||||
from .base import TableBase
|
from .base import TableBase
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .file import File
|
from .file import File
|
||||||
from .folder import Folder
|
from .folder import Folder
|
||||||
|
|
||||||
class Policy(TableBase, table=True):
|
class Policy(TableBase, table=True):
|
||||||
__tablename__ = 'policies'
|
"""存储策略模型"""
|
||||||
|
|
||||||
name: str = Field(max_length=255, unique=True, description="策略名称")
|
name: str = Field(max_length=255, unique=True, description="策略名称")
|
||||||
type: str = Field(max_length=255, description="存储类型 (e.g. 'local', 's3')")
|
type: str = Field(max_length=255, description="存储类型 (e.g. 'local', 's3')")
|
||||||
server: Optional[str] = Field(default=None, max_length=255, description="服务器地址(本地策略为路径)")
|
server: str | None = Field(default=None, max_length=255, description="服务器地址(本地策略为路径)")
|
||||||
bucket_name: Optional[str] = Field(default=None, max_length=255, description="存储桶名称")
|
bucket_name: str | None = Field(default=None, max_length=255, description="存储桶名称")
|
||||||
is_private: bool = Field(default=True, sa_column_kwargs={"server_default": text("true")}, 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")
|
base_url: str | None = Field(default=None, max_length=255, description="访问文件的基础URL")
|
||||||
access_key: Optional[str] = Field(default=None, description="Access Key")
|
access_key: str | None = Field(default=None, description="Access Key")
|
||||||
secret_key: Optional[str] = Field(default=None, description="Secret Key")
|
secret_key: str | None = Field(default=None, description="Secret Key")
|
||||||
max_size: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="允许上传的最大文件尺寸(字节)")
|
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="是否自动重命名")
|
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="目录命名规则")
|
dir_name_rule: str | None = Field(default=None, max_length=255, description="目录命名规则")
|
||||||
file_name_rule: Optional[str] = Field(default=None, max_length=255, description="文件命名规则")
|
file_name_rule: str | None = Field(default=None, max_length=255, description="文件命名规则")
|
||||||
is_origin_link_enable: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}, 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格式)")
|
options: str | None = Field(default=None, description="其他选项 (JSON格式)")
|
||||||
# options 示例: {"token":"","file_type":null,"mimetype":"","od_redirect":"http://127.0.0.1:8000/...","chunk_size":52428800,"s3_path_style":false}
|
# options 示例: {"token":"","file_type":null,"mimetype":"","od_redirect":"http://127.0.0.1:8000/...","chunk_size":52428800,"s3_path_style":false}
|
||||||
|
|
||||||
# 关系
|
# 关系
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
|
from sqlmodel import Field, text
|
||||||
from typing import Optional
|
|
||||||
from sqlmodel import Field, text, Column, func, DateTime
|
|
||||||
from .base import TableBase
|
from .base import TableBase
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
class Redeem(TableBase, table=True):
|
class Redeem(TableBase, table=True):
|
||||||
__tablename__ = 'redeems'
|
"""兑换码模型"""
|
||||||
|
|
||||||
type: int = Field(description="兑换码类型")
|
type: int = Field(description="兑换码类型")
|
||||||
product_id: Optional[int] = Field(default=None, description="关联的商品/权益ID")
|
product_id: int | None = Field(default=None, description="关联的商品/权益ID")
|
||||||
num: int = Field(default=1, sa_column_kwargs={"server_default": "1"}, description="可兑换数量/时长等")
|
num: int = Field(default=1, sa_column_kwargs={"server_default": "1"}, description="可兑换数量/时长等")
|
||||||
code: str = Field(unique=True, index=True, description="兑换码,唯一")
|
code: str = Field(unique=True, index=True, description="兑换码,唯一")
|
||||||
used: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}, description="是否已使用")
|
used: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}, description="是否已使用")
|
||||||
@@ -1,20 +1,19 @@
|
|||||||
|
|
||||||
from typing import Optional, TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from sqlmodel import Field, Relationship, Column, func, DateTime
|
from sqlmodel import Field, Relationship
|
||||||
from .base import TableBase
|
from .base import TableBase
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .share import Share
|
from .share import Share
|
||||||
|
|
||||||
class Report(TableBase, table=True):
|
class Report(TableBase, table=True):
|
||||||
__tablename__ = 'reports'
|
"""举报模型"""
|
||||||
|
|
||||||
reason: int = Field(description="举报原因代码")
|
reason: int = Field(description="举报原因代码")
|
||||||
description: Optional[str] = Field(default=None, max_length=255, description="补充描述")
|
description: str | None = Field(default=None, max_length=255, description="补充描述")
|
||||||
|
|
||||||
# 外键
|
# 外键
|
||||||
share_id: int = Field(foreign_key="shares.id", index=True, description="被举报的分享ID")
|
share_id: int = Field(foreign_key="share.id", index=True, description="被举报的分享ID")
|
||||||
|
|
||||||
# 关系
|
# 关系
|
||||||
share: "Share" = Relationship(back_populates="reports")
|
share: "Share" = Relationship(back_populates="reports")
|
||||||
@@ -13,5 +13,5 @@ class LoginRequest(BaseModel):
|
|||||||
"""
|
"""
|
||||||
username: str = Field(..., description="用户名或邮箱")
|
username: str = Field(..., description="用户名或邮箱")
|
||||||
password: str = Field(..., description="用户密码")
|
password: str = Field(..., description="用户密码")
|
||||||
captcha: Optional[str] = Field(None, description="验证码")
|
captcha: str | None = Field(None, description="验证码")
|
||||||
twoFaCode: Optional[str] = Field(None, description="两步验证代码")
|
twoFaCode: str | None = Field(None, description="两步验证代码")
|
||||||
@@ -13,7 +13,7 @@ class ResponseModel(BaseModel):
|
|||||||
'''
|
'''
|
||||||
code: int = Field(default=0, description="系统内部状态码, 0表示成功,其他表示失败", lt=60000, gt=0)
|
code: int = Field(default=0, description="系统内部状态码, 0表示成功,其他表示失败", lt=60000, gt=0)
|
||||||
data: Union[dict, list, str, int, float, None] = Field(None, description="响应数据")
|
data: Union[dict, list, str, int, float, None] = Field(None, description="响应数据")
|
||||||
msg: Optional[str] = Field(default=None, description="响应消息,可以是错误消息或信息提示")
|
msg: str | None = Field(default=None, description="响应消息,可以是错误消息或信息提示")
|
||||||
instance_id: str = Field(default_factory=lambda: str(uuid4()), description="实例ID,用于标识请求的唯一性")
|
instance_id: str = Field(default_factory=lambda: str(uuid4()), description="实例ID,用于标识请求的唯一性")
|
||||||
|
|
||||||
class ThemeModel(BaseModel):
|
class ThemeModel(BaseModel):
|
||||||
@@ -81,12 +81,12 @@ class SiteConfigModel(ResponseModel):
|
|||||||
title: str = Field(default="DiskNext", description="网站标题")
|
title: str = Field(default="DiskNext", description="网站标题")
|
||||||
themes: dict = Field(default_factory=dict, description="网站主题配置")
|
themes: dict = Field(default_factory=dict, description="网站主题配置")
|
||||||
default_theme: str = Field(default="default", description="默认主题RGB色号")
|
default_theme: str = Field(default="default", description="默认主题RGB色号")
|
||||||
site_notice: Optional[str] = Field(default=None, description="网站公告")
|
site_notice: str | None = Field(default=None, description="网站公告")
|
||||||
user: dict = Field(default_factory=dict, description="用户信息")
|
user: dict = Field(default_factory=dict, description="用户信息")
|
||||||
logo_light: Optional[str] = Field(default=None, description="网站Logo URL")
|
logo_light: str | None = Field(default=None, description="网站Logo URL")
|
||||||
logo_dark: Optional[str] = Field(default=None, description="网站Logo URL(深色模式)")
|
logo_dark: str | None = Field(default=None, description="网站Logo URL(深色模式)")
|
||||||
captcha_type: Literal['none', 'default', 'gcaptcha', 'cloudflare turnstile'] = Field(default='none', description="验证码类型")
|
captcha_type: Literal['none', 'default', 'gcaptcha', 'cloudflare turnstile'] = Field(default='none', description="验证码类型")
|
||||||
captcha_key: Optional[str] = Field(default=None, description="验证码密钥")
|
captcha_key: str | None = Field(default=None, description="验证码密钥")
|
||||||
|
|
||||||
class AuthnModel(BaseModel):
|
class AuthnModel(BaseModel):
|
||||||
'''
|
'''
|
||||||
@@ -100,7 +100,7 @@ class UserSettingModel(BaseModel):
|
|||||||
用户设置模型
|
用户设置模型
|
||||||
'''
|
'''
|
||||||
authn: Optional[AuthnModel] = Field(default=None, description="认证信息")
|
authn: Optional[AuthnModel] = Field(default=None, description="认证信息")
|
||||||
group_expires: Optional[datetime] = Field(default=None, description="用户组过期时间")
|
group_expires: datetime | None = Field(default=None, description="用户组过期时间")
|
||||||
prefer_theme: str = Field(default="#5898d4", description="用户首选主题")
|
prefer_theme: str = Field(default="#5898d4", description="用户首选主题")
|
||||||
qq: str | bool = Field(default=False, description="QQ号")
|
qq: str | bool = Field(default=False, description="QQ号")
|
||||||
themes: dict = Field(default_factory=dict, description="用户主题配置")
|
themes: dict = Field(default_factory=dict, description="用户主题配置")
|
||||||
|
|||||||
@@ -34,18 +34,19 @@ SETTINGS_TYPE = Literal[
|
|||||||
|
|
||||||
# 数据库模型
|
# 数据库模型
|
||||||
class Setting(TableBase, table=True):
|
class Setting(TableBase, table=True):
|
||||||
__tablename__ = 'settings'
|
"""设置模型"""
|
||||||
|
|
||||||
__table_args__ = (UniqueConstraint("type", "name", name="uq_setting_type_name"),)
|
__table_args__ = (UniqueConstraint("type", "name", name="uq_setting_type_name"),)
|
||||||
|
|
||||||
type: str = Field(max_length=255, description="设置类型/分组")
|
type: str = Field(max_length=255, description="设置类型/分组")
|
||||||
name: str = Field(max_length=255, description="设置项名称")
|
name: str = Field(max_length=255, description="设置项名称")
|
||||||
value: Optional[str] = Field(default=None, description="设置值")
|
value: str | None = Field(default=None, description="设置值")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def add(
|
async def add(
|
||||||
type: SETTINGS_TYPE = None,
|
type: SETTINGS_TYPE = None,
|
||||||
name: str = None,
|
name: str = None,
|
||||||
value: Optional[str] = None
|
value: str | None = None
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
向数据库内添加设置项目。
|
向数据库内添加设置项目。
|
||||||
@@ -55,7 +56,7 @@ class Setting(TableBase, table=True):
|
|||||||
:param name: 设置项名称
|
:param name: 设置项名称
|
||||||
:type name: str
|
:type name: str
|
||||||
:param value: 设置值,默认为 None
|
:param value: 设置值,默认为 None
|
||||||
:type value: Optional[str]
|
:type value: str | None
|
||||||
"""
|
"""
|
||||||
from .database import get_session
|
from .database import get_session
|
||||||
|
|
||||||
@@ -114,7 +115,7 @@ class Setting(TableBase, table=True):
|
|||||||
async def set(
|
async def set(
|
||||||
type: SETTINGS_TYPE,
|
type: SETTINGS_TYPE,
|
||||||
name: str,
|
name: str,
|
||||||
value: Optional[str] = None
|
value: str | None = None
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
更新指定类型和名称的设置项的值。
|
更新指定类型和名称的设置项的值。
|
||||||
@@ -124,7 +125,7 @@ class Setting(TableBase, table=True):
|
|||||||
:param name: 设置项名称
|
:param name: 设置项名称
|
||||||
:type name: str
|
:type name: str
|
||||||
:param value: 新的设置值,默认为 None
|
:param value: 新的设置值,默认为 None
|
||||||
:type value: Optional[str]
|
:type value: str | None
|
||||||
|
|
||||||
:raises ValueError: 如果设置项不存在,则抛出异常
|
:raises ValueError: 如果设置项不存在,则抛出异常
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ if TYPE_CHECKING:
|
|||||||
from .report import Report
|
from .report import Report
|
||||||
|
|
||||||
class Share(TableBase, table=True):
|
class Share(TableBase, table=True):
|
||||||
__tablename__ = 'shares'
|
"""分享模型"""
|
||||||
|
|
||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
UniqueConstraint("code", name="uq_share_code"),
|
UniqueConstraint("code", name="uq_share_code"),
|
||||||
CheckConstraint("(file_id IS NOT NULL) <> (folder_id IS NOT NULL)", name="ck_share_xor"),
|
CheckConstraint("(file_id IS NOT NULL) <> (folder_id IS NOT NULL)", name="ck_share_xor"),
|
||||||
@@ -19,22 +20,22 @@ class Share(TableBase, table=True):
|
|||||||
)
|
)
|
||||||
|
|
||||||
code: str = Field(max_length=64, nullable=False, index=True, description="分享码")
|
code: str = Field(max_length=64, nullable=False, index=True, description="分享码")
|
||||||
password: Optional[str] = Field(default=None, max_length=255, description="分享密码(加密后)")
|
password: str | None = Field(default=None, max_length=255, description="分享密码(加密后)")
|
||||||
|
|
||||||
is_dir: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}, description="是否为目录分享")
|
is_dir: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}, description="是否为目录分享")
|
||||||
file_id: Optional[int] = Field(default=None, foreign_key="files.id", index=True, description="文件ID(二选一)")
|
file_id: int | None = Field(default=None, foreign_key="file.id", index=True, description="文件ID(二选一)")
|
||||||
folder_id: Optional[int] = Field(default=None, foreign_key="folders.id", index=True, description="目录ID(二选一)")
|
folder_id: int | None = Field(default=None, foreign_key="folder.id", index=True, description="目录ID(二选一)")
|
||||||
|
|
||||||
views: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="浏览次数")
|
views: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="浏览次数")
|
||||||
downloads: 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为不限制)")
|
remain_downloads: int | None = Field(default=None, description="剩余下载次数 (NULL为不限制)")
|
||||||
expires: Optional[datetime] = Field(default=None, description="过期时间 (NULL为永不过期)")
|
expires: datetime | None = Field(default=None, description="过期时间 (NULL为永不过期)")
|
||||||
preview_enabled: bool = Field(default=True, sa_column_kwargs={"server_default": text("true")}, description="是否允许预览")
|
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="源名称(冗余字段,便于展示)")
|
source_name: str | None = Field(default=None, max_length=255, index=True, description="源名称(冗余字段,便于展示)")
|
||||||
score: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="兑换此分享所需的积分")
|
score: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="兑换此分享所需的积分")
|
||||||
|
|
||||||
# 外键
|
# 外键
|
||||||
user_id: int = Field(foreign_key="users.id", index=True, description="创建分享的用户ID")
|
user_id: int = Field(foreign_key="user.id", index=True, description="创建分享的用户ID")
|
||||||
|
|
||||||
# 关系
|
# 关系
|
||||||
user: "User" = Relationship(back_populates="shares")
|
user: "User" = Relationship(back_populates="shares")
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ if TYPE_CHECKING:
|
|||||||
from .file import File
|
from .file import File
|
||||||
|
|
||||||
class SourceLink(TableBase, table=True):
|
class SourceLink(TableBase, table=True):
|
||||||
__tablename__ = 'source_links'
|
"""链接模型"""
|
||||||
|
|
||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
Index("ix_sourcelink_file_name", "file_id", "name"),
|
Index("ix_sourcelink_file_name", "file_id", "name"),
|
||||||
)
|
)
|
||||||
@@ -17,7 +18,7 @@ class SourceLink(TableBase, table=True):
|
|||||||
downloads: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="通过此链接的下载次数")
|
downloads: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="通过此链接的下载次数")
|
||||||
|
|
||||||
# 外键
|
# 外键
|
||||||
file_id: int = Field(foreign_key="files.id", index=True, description="关联的文件ID")
|
file_id: int = Field(foreign_key="file.id", index=True, description="关联的文件ID")
|
||||||
|
|
||||||
# 关系
|
# 关系
|
||||||
file: "File" = Relationship(back_populates="source_links")
|
file: "File" = Relationship(back_populates="source_links")
|
||||||
@@ -9,15 +9,15 @@ if TYPE_CHECKING:
|
|||||||
from .user import User
|
from .user import User
|
||||||
|
|
||||||
class StoragePack(TableBase, table=True):
|
class StoragePack(TableBase, table=True):
|
||||||
__tablename__ = 'storage_packs'
|
"""容量包模型"""
|
||||||
|
|
||||||
name: str = Field(max_length=255, description="容量包名称")
|
name: str = Field(max_length=255, description="容量包名称")
|
||||||
active_time: Optional[datetime] = Field(default=None, description="激活时间")
|
active_time: datetime | None = Field(default=None, description="激活时间")
|
||||||
expired_time: Optional[datetime] = Field(default=None, index=True, description="过期时间")
|
expired_time: datetime | None = Field(default=None, index=True, description="过期时间")
|
||||||
size: int = Field(description="容量包大小(字节)")
|
size: int = Field(description="容量包大小(字节)")
|
||||||
|
|
||||||
# 外键
|
# 外键
|
||||||
user_id: int = Field(foreign_key="users.id", index=True, description="所属用户ID")
|
user_id: int = Field(foreign_key="user.id", index=True, description="所属用户ID")
|
||||||
|
|
||||||
# 关系
|
# 关系
|
||||||
user: "User" = Relationship(back_populates="storage_packs")
|
user: "User" = Relationship(back_populates="storage_packs")
|
||||||
@@ -8,17 +8,18 @@ if TYPE_CHECKING:
|
|||||||
from .user import User
|
from .user import User
|
||||||
|
|
||||||
class Tag(TableBase, table=True):
|
class Tag(TableBase, table=True):
|
||||||
__tablename__ = 'tags'
|
"""标签模型"""
|
||||||
|
|
||||||
__table_args__ = (UniqueConstraint("name", "user_id", name="uq_tag_name_user"),)
|
__table_args__ = (UniqueConstraint("name", "user_id", name="uq_tag_name_user"),)
|
||||||
|
|
||||||
name: str = Field(max_length=255, description="标签名称")
|
name: str = Field(max_length=255, description="标签名称")
|
||||||
icon: Optional[str] = Field(default=None, max_length=255, description="标签图标")
|
icon: str | None = Field(default=None, max_length=255, description="标签图标")
|
||||||
color: Optional[str] = Field(default=None, max_length=255, description="标签颜色")
|
color: str | None = Field(default=None, max_length=255, description="标签颜色")
|
||||||
type: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="标签类型: 0=手动, 1=自动")
|
type: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="标签类型: 0=手动, 1=自动")
|
||||||
expression: Optional[str] = Field(default=None, description="自动标签的匹配表达式")
|
expression: str | None = Field(default=None, description="自动标签的匹配表达式")
|
||||||
|
|
||||||
# 外键
|
# 外键
|
||||||
user_id: int = Field(foreign_key="users.id", index=True, description="所属用户ID")
|
user_id: int = Field(foreign_key="user.id", index=True, description="所属用户ID")
|
||||||
|
|
||||||
# 关系
|
# 关系
|
||||||
user: "User" = Relationship(back_populates="tags")
|
user: "User" = Relationship(back_populates="tags")
|
||||||
@@ -9,7 +9,8 @@ if TYPE_CHECKING:
|
|||||||
from .download import Download
|
from .download import Download
|
||||||
|
|
||||||
class Task(TableBase, table=True):
|
class Task(TableBase, table=True):
|
||||||
__tablename__ = 'tasks'
|
"""任务模型"""
|
||||||
|
|
||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
CheckConstraint("progress BETWEEN 0 AND 100", name="ck_task_progress_range"),
|
CheckConstraint("progress BETWEEN 0 AND 100", name="ck_task_progress_range"),
|
||||||
)
|
)
|
||||||
@@ -17,11 +18,11 @@ class Task(TableBase, table=True):
|
|||||||
status: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="任务状态: 0=排队中, 1=处理中, 2=完成, 3=错误")
|
status: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="任务状态: 0=排队中, 1=处理中, 2=完成, 3=错误")
|
||||||
type: int = Field(description="任务类型")
|
type: int = Field(description="任务类型")
|
||||||
progress: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="任务进度 (0-100)")
|
progress: int = Field(default=0, sa_column_kwargs={"server_default": "0"}, description="任务进度 (0-100)")
|
||||||
error: Optional[str] = Field(default=None, description="错误信息")
|
error: str | None = Field(default=None, description="错误信息")
|
||||||
props: Optional[str] = Field(default=None, description="任务属性 (JSON格式)")
|
props: str | None = Field(default=None, description="任务属性 (JSON格式)")
|
||||||
|
|
||||||
# 外键
|
# 外键
|
||||||
user_id: int = Field(foreign_key="users.id", index=True, description="所属用户ID")
|
user_id: int = Field(foreign_key="user.id", index=True, description="所属用户ID")
|
||||||
|
|
||||||
# 关系
|
# 关系
|
||||||
user: "User" = Relationship(back_populates="tasks")
|
user: "User" = Relationship(back_populates="tasks")
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from .task import Task
|
|||||||
from .webdav import WebDAV
|
from .webdav import WebDAV
|
||||||
|
|
||||||
class User(TableBase, table=True):
|
class User(TableBase, table=True):
|
||||||
__tablename__ = 'users'
|
"""用户模型"""
|
||||||
|
|
||||||
email: str = Field(max_length=100, unique=True, index=True)
|
email: str = Field(max_length=100, unique=True, index=True)
|
||||||
"""用户邮箱,唯一"""
|
"""用户邮箱,唯一"""
|
||||||
@@ -25,44 +25,56 @@ class User(TableBase, table=True):
|
|||||||
|
|
||||||
nick: str | None = Field(default=None, max_length=50)
|
nick: str | None = Field(default=None, max_length=50)
|
||||||
"""用户昵称"""
|
"""用户昵称"""
|
||||||
|
|
||||||
password: str = Field(max_length=255)
|
password: str = Field(max_length=255)
|
||||||
"""用户密码(加密后)"""
|
"""用户密码(加密后)"""
|
||||||
status: bool | None = Field(default=None, sa_column_kwargs={"server_default": "0"})
|
|
||||||
"""用户状态: True=正常, None=未激活, False=封禁"""
|
status: bool = Field(default=True, sa_column_kwargs={"server_default": "true"})
|
||||||
|
"""用户状态: True=正常, False=封禁"""
|
||||||
|
|
||||||
storage: int = Field(default=0, sa_column_kwargs={"server_default": "0"})
|
storage: int = Field(default=0, sa_column_kwargs={"server_default": "0"})
|
||||||
"""已用存储空间(字节)"""
|
"""已用存储空间(字节)"""
|
||||||
|
|
||||||
two_factor: str | None = Field(default=None, max_length=255)
|
two_factor: str | None = Field(default=None, max_length=255)
|
||||||
"""两步验证密钥"""
|
"""两步验证密钥"""
|
||||||
|
|
||||||
avatar: str | None = Field(default=None, max_length=255)
|
avatar: str | None = Field(default=None, max_length=255)
|
||||||
"""头像地址"""
|
"""头像地址"""
|
||||||
|
|
||||||
options: str | None = Field(default=None)
|
options: str | None = Field(default=None)
|
||||||
"""用户个人设置 (JSON格式)"""
|
"""用户个人设置 (JSON格式)"""
|
||||||
|
|
||||||
authn: str | None = Field(default=None)
|
authn: str | None = Field(default=None)
|
||||||
"""WebAuthn 凭证"""
|
"""WebAuthn 凭证"""
|
||||||
|
|
||||||
open_id: str | None = Field(default=None, max_length=255, unique=True, index=True)
|
open_id: str | None = Field(default=None, max_length=255, unique=True, index=True)
|
||||||
"""第三方登录OpenID"""
|
"""第三方登录OpenID"""
|
||||||
|
|
||||||
score: int = Field(default=0, sa_column_kwargs={"server_default": "0"})
|
score: int = Field(default=0, sa_column_kwargs={"server_default": "0"})
|
||||||
"""用户积分"""
|
"""用户积分"""
|
||||||
|
|
||||||
group_expires: datetime | None = Field(default=None)
|
group_expires: datetime | None = Field(default=None)
|
||||||
"""当前用户组过期时间"""
|
"""当前用户组过期时间"""
|
||||||
|
|
||||||
phone: str | None = Field(default=None, max_length=255, unique=True, index=True)
|
phone: str | None = Field(default=None, max_length=255, unique=True, index=True)
|
||||||
"""手机号"""
|
"""手机号"""
|
||||||
|
|
||||||
# 外键
|
# 外键
|
||||||
group_id: int = Field(foreign_key="groups.id", index=True)
|
group_id: int = Field(foreign_key="group.id", index=True)
|
||||||
"""所属用户组ID"""
|
"""所属用户组ID"""
|
||||||
previous_group_id: int | None = Field(default=None, foreign_key="groups.id")
|
|
||||||
|
previous_group_id: int | None = Field(default=None, foreign_key="group.id")
|
||||||
"""之前的用户组ID(用于过期后恢复)"""
|
"""之前的用户组ID(用于过期后恢复)"""
|
||||||
|
|
||||||
# 关系
|
# 关系
|
||||||
group: "Group" = Relationship(
|
group: "Group" = Relationship(
|
||||||
back_populates="users",
|
back_populates="user",
|
||||||
sa_relationship_kwargs={
|
sa_relationship_kwargs={
|
||||||
"foreign_keys": "User.group_id"
|
"foreign_keys": "User.group_id"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
previous_group: Optional["Group"] = Relationship(
|
previous_group: Optional["Group"] = Relationship(
|
||||||
back_populates="previous_users",
|
back_populates="previous_user",
|
||||||
sa_relationship_kwargs={
|
sa_relationship_kwargs={
|
||||||
"foreign_keys": "User.previous_group_id"
|
"foreign_keys": "User.previous_group_id"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,17 +7,18 @@ if TYPE_CHECKING:
|
|||||||
from .user import User
|
from .user import User
|
||||||
|
|
||||||
class WebDAV(TableBase, table=True):
|
class WebDAV(TableBase, table=True):
|
||||||
__tablename__ = 'webdavs'
|
"""WebDAV账户模型"""
|
||||||
|
|
||||||
__table_args__ = (UniqueConstraint("name", "user_id", name="uq_webdav_name_user"),)
|
__table_args__ = (UniqueConstraint("name", "user_id", name="uq_webdav_name_user"),)
|
||||||
|
|
||||||
name: str = Field(max_length=255, description="WebDAV账户名")
|
name: str = Field(max_length=255, description="WebDAV账户名")
|
||||||
password: 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="根目录路径")
|
root: str = Field(default="/", sa_column_kwargs={"server_default": "'/'"}, description="根目录路径")
|
||||||
readonly: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}, 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="是否使用代理下载")
|
use_proxy: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}, description="是否使用代理下载")
|
||||||
|
|
||||||
# 外键
|
# 外键
|
||||||
user_id: int = Field(foreign_key="users.id", index=True, description="所属用户ID")
|
user_id: int = Field(foreign_key="user.id", index=True, description="所属用户ID")
|
||||||
|
|
||||||
# 关系
|
# 关系
|
||||||
user: "User" = Relationship(back_populates="webdavs")
|
user: "User" = Relationship(back_populates="webdavs")
|
||||||
@@ -18,9 +18,9 @@ async def Login(LoginRequest: LoginRequest) -> TokenModel | bool | None:
|
|||||||
:param password: 用户密码
|
:param password: 用户密码
|
||||||
:type password: str
|
:type password: str
|
||||||
:param captcha: 验证码
|
:param captcha: 验证码
|
||||||
:type captcha: Optional[str]
|
:type captcha: str | None
|
||||||
:param twoFaCode: 两步验证代码
|
:param twoFaCode: 两步验证代码
|
||||||
:type twoFaCode: Optional[str]
|
:type twoFaCode: str | None
|
||||||
|
|
||||||
:return: TokenModel 对象或状态码或 None
|
:return: TokenModel 对象或状态码或 None
|
||||||
:rtype: TokenModel | int | None
|
:rtype: TokenModel | int | None
|
||||||
|
|||||||
Reference in New Issue
Block a user