diff --git a/models/model.py b/models/model.py index cd6f7e2..824629e 100644 --- a/models/model.py +++ b/models/model.py @@ -1,215 +1,251 @@ -from datetime import datetime -from sqlalchemy import Column, Integer, String, Text, BigInteger, Boolean, DateTime, ForeignKey -from sqlalchemy.ext.declarative import declarative_base +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, autoincrement=True) - created_at = Column(DateTime, default=datetime.now) - updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now) - deleted_at = Column(DateTime, nullable=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=True) - type = Column(Integer, nullable=True) - source = Column(Text, nullable=True) - total_size = Column(BigInteger, nullable=True) - downloaded_size = Column(BigInteger, nullable=True) - g_id = Column(Text, nullable=True) - speed = Column(Integer, nullable=True) - parent = Column(Text, nullable=True) - attrs = Column(Text, nullable=True) - error = Column(Text, nullable=True) - dst = Column(Text, nullable=True) - user_id = Column(Integer, nullable=True) - task_id = Column(Integer, nullable=True) - node_id = Column(Integer, nullable=True) + 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=True) - source_name = Column(Text, nullable=True) - user_id = Column(Integer, nullable=True) - size = Column(BigInteger, nullable=True) - pic_info = Column(String(255), nullable=True) - folder_id = Column(Integer, nullable=True) - policy_id = Column(Integer, nullable=True) - upload_session_id = Column(String(255), nullable=True, unique=True) - metadata = Column(Text, nullable=True) + 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=True) - parent_id = Column(Integer, nullable=True, index=True) - owner_id = Column(Integer, nullable=True, index=True) - policy_id = Column(Integer, nullable=True) + name = Column(String(255), nullable=False, comment="目录名") - __table_args__ = {'uniqueConstraints': [('name', 'parent_id')]} + 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=True) - policies = Column(String(255), nullable=True) - max_storage = Column(BigInteger, nullable=True) - share_enabled = Column(Boolean, nullable=True) - web_dav_enabled = Column(Boolean, nullable=True) - speed_limit = Column(Integer, nullable=True) - options = Column(String(255), nullable=True) + 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=True) - name = Column(String(255), nullable=True) - type = Column(Integer, nullable=True) - server = Column(String(255), nullable=True) - slave_key = Column(Text, nullable=True) - master_key = Column(Text, nullable=True) - aria2_enabled = Column(Boolean, nullable=True) - aria2_options = Column(Text, nullable=True) - rank = Column(Integer, nullable=True) + 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' - user_id = Column(Integer, nullable=True) - order_no = Column(String(255), nullable=True, index=True) - type = Column(Integer, nullable=True) - method = Column(String(255), nullable=True) - product_id = Column(BigInteger, nullable=True) - num = Column(Integer, nullable=True) - name = Column(String(255), nullable=True) - price = Column(Integer, nullable=True) - status = Column(Integer, nullable=True) + 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=True) - type = Column(String(255), nullable=True) - server = Column(String(255), nullable=True) - bucket_name = Column(String(255), nullable=True) - is_private = Column(Boolean, nullable=True) - base_url = Column(String(255), nullable=True) - access_key = Column(Text, nullable=True) - secret_key = Column(Text, nullable=True) - max_size = Column(BigInteger, nullable=True) - auto_rename = Column(Boolean, nullable=True) - dir_name_rule = Column(String(255), nullable=True) - file_name_rule = Column(String(255), nullable=True) - is_origin_link_enable = Column(Boolean, nullable=True) - options = Column(Text, nullable=True) - -class Redeem(BaseModel): - __tablename__ = 'redeems' - - type = Column(Integer, nullable=True) - product_id = Column(BigInteger, nullable=True) - num = Column(Integer, nullable=True) - code = Column(Text, nullable=True) - used = Column(Boolean, nullable=True) - -class Report(BaseModel): - __tablename__ = 'reports' - - share_id = Column(Integer, nullable=True, index=True) - reason = Column(Integer, nullable=True) - description = Column(String(255), nullable=True) + 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 = Column(String(255), nullable=False) - name = Column(String(255), nullable=False, unique=True, index=True) - value = Column(Text, nullable=True) + # 优化点: 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), nullable=True) - is_dir = Column(Boolean, nullable=True) - user_id = Column(Integer, nullable=True) - source_id = Column(Integer, nullable=True) - views = Column(Integer, nullable=True) - downloads = Column(Integer, nullable=True) - remain_downloads = Column(Integer, nullable=True) - expires = Column(DateTime, nullable=True) - preview_enabled = Column(Boolean, nullable=True) - source_name = Column(String(255), nullable=True, index=True) - score = Column(Integer, nullable=True) - -class SourceLink(BaseModel): - __tablename__ = 'source_links' + 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="分享评分/权重") - file_id = Column(Integer, nullable=True) - name = Column(String(255), nullable=True) - downloads = Column(Integer, nullable=True) - -class StoragePack(BaseModel): - __tablename__ = 'storage_packs' - - name = Column(String(255), nullable=True) - user_id = Column(Integer, nullable=True) - active_time = Column(DateTime, nullable=True) - expired_time = Column(DateTime, nullable=True, index=True) - size = Column(BigInteger, nullable=True) - -class Tag(BaseModel): - __tablename__ = 'tags' - - name = Column(String(255), nullable=True) - icon = Column(String(255), nullable=True) - color = Column(String(255), nullable=True) - type = Column(Integer, nullable=True) - expression = Column(Text, nullable=True) - user_id = Column(Integer, nullable=True) + user_id = Column(Integer, ForeignKey('users.id'), nullable=False, index=True, comment="创建分享的用户ID") class Task(BaseModel): __tablename__ = 'tasks' - status = Column(Integer, nullable=True) - type = Column(Integer, nullable=True) - user_id = Column(Integer, nullable=True) - progress = Column(Integer, nullable=True) - error = Column(Text, nullable=True) - props = Column(Text, nullable=True) + 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=True, unique=True) - nick = Column(String(50), nullable=True) - password = Column(String(255), nullable=True) - status = Column(Integer, nullable=True) - group_id = Column(Integer, nullable=True) - storage = Column(BigInteger, nullable=True) - two_factor = Column(String(255), nullable=True) - avatar = Column(String(255), nullable=True) - options = Column(Text, nullable=True) - authn = Column(Text, nullable=True) - open_id = Column(String(255), nullable=True) - score = Column(Integer, nullable=True) - previous_group_id = Column(Integer, nullable=True) - group_expires = Column(DateTime, nullable=True) - notify_date = Column(DateTime, nullable=True) - phone = Column(String(255), nullable=True) + 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=True) - password = Column(String(255), nullable=True) - user_id = Column(Integer, nullable=True) - root = Column(Text, nullable=True) - readonly = Column(Boolean, nullable=True) - use_proxy = Column(Boolean, nullable=True) + 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__ = {'uniqueConstraints': [('password', 'user_id')]} + __table_args__ = ( + UniqueConstraint('name', 'user_id', name='uq_webdav_name_user'), + ) \ No newline at end of file diff --git a/routers/controllers/slave.py b/routers/controllers/slave.py new file mode 100644 index 0000000..093d6f8 --- /dev/null +++ b/routers/controllers/slave.py @@ -0,0 +1,225 @@ +from fastapi import APIRouter, Depends +from fastapi.responses import FileResponse +from middleware.auth import SignRequired +from models.response import ResponseModel + +slave_router = APIRouter( + prefix="/slave", + tags=["slave"], +) + +slave_aria2_router = APIRouter( + prefix="/aria2", + tags=["slave_aria2"], +) + +@slave_router.get( + path='/ping', + summary='测试用路由', + description='Test route for checking connectivity.', +) +def router_slave_ping() -> ResponseModel: + """ + Test route for checking connectivity. + + Returns: + ResponseModel: A response model indicating success. + """ + from pkg.conf.appmeta import BackendVersion + return ResponseModel(data=BackendVersion) + +@slave_router.post( + path='/post', + summary='上传', + description='Upload data to the server.', + dependencies=[Depends(SignRequired)], +) +def router_slave_post(data: str) -> ResponseModel: + """ + Upload data to the server. + + Args: + data (str): The data to be uploaded. + + Returns: + ResponseModel: A response model indicating success. + """ + ... + +@slave_router.get( + path='/get/{speed}/{path}/{name}', + summary='获取下载', +) +def router_slave_download(speed: int, path: str, name: str) -> ResponseModel: + """ + Get download information. + + Args: + speed (int): The speed of the download. + path (str): The path where the file is located. + name (str): The name of the file to be downloaded. + + Returns: + ResponseModel: A response model containing download information. + """ + ... + +@slave_router.get( + path='/download/{sign}', + summary='根据签名下载文件', + description='Download a file based on its signature.', + dependencies=[Depends(SignRequired)], +) +def router_slave_download_by_sign(sign: str) -> FileResponse: + """ + Download a file based on its signature. + + Args: + sign (str): The signature of the file to be downloaded. + + Returns: + FileResponse: A response containing the file to be downloaded. + """ + ... + +@slave_router.get( + path='/source/{speed}/{path}/{name}', + summary='获取文件外链', + description='Get the external link for a file based on its signature.', + dependencies=[Depends(SignRequired)], +) +def router_slave_source(speed: int, path: str, name: str) -> ResponseModel: + """ + Get the external link for a file based on its signature. + + Args: + speed (int): The speed of the download. + path (str): The path where the file is located. + name (str): The name of the file to be linked. + + Returns: + ResponseModel: A response model containing the external link for the file. + """ + ... + +@slave_router.get( + path='/source/{sign}', + summary='根据签名获取文件', + description='Get a file based on its signature.', + dependencies=[Depends(SignRequired)], +) +def router_slave_source_by_sign(sign: str) -> FileResponse: + """ + Get a file based on its signature. + + Args: + sign (str): The signature of the file to be retrieved. + + Returns: + FileResponse: A response containing the file to be retrieved. + """ + ... + +@slave_router.get( + path='/thumb/{id}', + summary='获取缩略图', + description='Get a thumbnail image based on its ID.', + dependencies=[Depends(SignRequired)], +) +def router_slave_thumb(id: str) -> ResponseModel: + """ + Get a thumbnail image based on its ID. + + Args: + id (str): The ID of the thumbnail image. + + Returns: + ResponseModel: A response model containing the Base64 encoded thumbnail image. + """ + ... + +@slave_router.delete( + path='/delete', + summary='删除文件', + description='Delete a file from the server.', + dependencies=[Depends(SignRequired)], +) +def router_slave_delete(path: str) -> ResponseModel: + """ + Delete a file from the server. + + Args: + path (str): The path of the file to be deleted. + + Returns: + ResponseModel: A response model indicating success or failure of the deletion. + """ + ... + +@slave_aria2_router.post( + path='/test', + summary='测试从机连接Aria2服务', + description='Test the connection to the Aria2 service from the slave.', + dependencies=[Depends(SignRequired)], +) +def router_slave_aria2_test() -> ResponseModel: + """ + Test the connection to the Aria2 service from the slave. + """ + ... + +@slave_aria2_router.get( + path='/get/{gid}', + summary='获取Aria2任务信息', + description='Get information about an Aria2 task by its GID.', + dependencies=[Depends(SignRequired)], +) +def router_slave_aria2_get(gid: str = None) -> ResponseModel: + """ + Get information about an Aria2 task by its GID. + + Args: + gid (str): The GID of the Aria2 task. + + Returns: + ResponseModel: A response model containing the task information. + """ + ... + +@slave_aria2_router.post( + path='/add', + summary='添加Aria2任务', + description='Add a new Aria2 task.', + dependencies=[Depends(SignRequired)], +) +def router_slave_aria2_add(gid: str, url: str, options: dict = None) -> ResponseModel: + """ + Add a new Aria2 task. + + Args: + gid (str): The GID for the new task. + url (str): The URL of the file to be downloaded. + options (dict, optional): Additional options for the task. + + Returns: + ResponseModel: A response model indicating success or failure of the task addition. + """ + ... + +@slave_aria2_router.delete( + path='/remove/{gid}', + summary='删除Aria2任务', + description='Remove an Aria2 task by its GID.', + dependencies=[Depends(SignRequired)], +) +def router_slave_aria2_remove(gid: str) -> ResponseModel: + """ + Remove an Aria2 task by its GID. + + Args: + gid (str): The GID of the Aria2 task to be removed. + + Returns: + ResponseModel: A response model indicating success or failure of the task removal. + """ + ... \ No newline at end of file diff --git a/routers/routers.py b/routers/routers.py index 8e180d0..23d54f5 100644 --- a/routers/routers.py +++ b/routers/routers.py @@ -12,7 +12,8 @@ from .controllers import ( vas, tag, webdav, - admin + admin, + slave ) Router: list[APIRouter] = [ @@ -39,5 +40,7 @@ Router: list[APIRouter] = [ admin.admin_aria2_router, admin.admin_policy_router, admin.admin_task_router, - admin.admin_vas_router + admin.admin_vas_router, + slave.slave_router, + slave.slave_aria2_router ] \ No newline at end of file