diff --git a/example/daemon.py b/example/daemon.py index 32d6da9..d627e31 100644 --- a/example/daemon.py +++ b/example/daemon.py @@ -1,4 +1,5 @@ from mcsmapi import MCSMAPI +from mcsmapi.models.daemon import DaemonConfig mcsm = MCSMAPI("http://localhost:23333") @@ -6,7 +7,7 @@ # mcsm.login_with_apikey("apikey") -daemon_object = mcsm.daemon() +daemon_object = mcsm.daemon # show Daemon list @@ -14,13 +15,12 @@ # 创建节点 daemonId = daemon_object.add( - { - "ip": "localhost", - "port": 24444, - "prefix": "", - "remarks": "Unnamed Node", - "available": True, - } + DaemonConfig( + ip="localhost", + port=24444, + prefix="", + remarks="Unnamed Node", + ) ) # 删除节点 daemon_object.delete(daemonId) diff --git a/example/instance.py b/example/instance.py index 24bc48f..8e4fbaa 100644 --- a/example/instance.py +++ b/example/instance.py @@ -4,7 +4,7 @@ api.login("admin", "547cABC9bf88@") -instance_object = api.instance() +instance_object = api.instance instance_list = instance_object.search("xxx") @@ -37,7 +37,7 @@ f.rename("new_name") # copy file -f.copy("new_path") +f.copy_to("new_path") # move file f.move("new_path") diff --git a/example/overview.py b/example/overview.py index f741ec2..bbfe646 100644 --- a/example/overview.py +++ b/example/overview.py @@ -7,7 +7,7 @@ # mcsm.login_with_apikey("apikey") # Get dashboard data -overview = mcsm.overview() +overview = mcsm.overview overview_data = overview.overview() mcsm_version = overview_data.version @@ -19,12 +19,14 @@ print(remote.ip) print(remote.port) print(remote.prefix) - print(remote.available) - print(remote.version) - print(remote.process.cpu) - print(remote.system.freemem) - print(remote.system.hostname) - print(remote.system.loadavg) + if remote.available: + print(remote.version) + print(remote.process.cpu) # type: ignore + print(remote.system.freemem) # type: ignore + print(remote.system.hostname) # type: ignore + print(remote.system.loadavg) # type: ignore + else: + print("Not available") remote = remotes[0] diff --git a/example/use_case/get_all_instance_info.py b/example/use_case/get_all_instance_info.py index f21997f..81b1a44 100644 --- a/example/use_case/get_all_instance_info.py +++ b/example/use_case/get_all_instance_info.py @@ -17,19 +17,18 @@ # Get all instance info def get_all_instance_info(daemon_id: str): # Get all instance info from the daemon - instance_object = mcsm.instance() + instance_object = mcsm.instance instance_list = instance_object.search(daemonId=daemon_id).data - - # Create a dictionary to store instance info - instance_dict = {} - for instance in instance_list: - instance_dict[instance.instanceUuid] = { + + return { + instance.instanceUuid: { "name": instance.config.nickname, "status": instance.status, "daemonId": instance.daemonId, "uuid": instance.instanceUuid, } - return instance_dict + for instance in instance_list + } # Example usage daemon_id = "your_daemon_id" # Please change to your MCSM panel daemon id. @@ -41,4 +40,3 @@ def get_all_instance_info(daemon_id: str): # Optional: Save info to a file in the current directory with open("instance_info.json", "w") as f: content = json.dump(instance_info, f, indent=4) - f.write(content) diff --git a/example/use_case/search_instance_by_name.py b/example/use_case/search_instance_by_name.py index cf74b54..d4887f4 100644 --- a/example/use_case/search_instance_by_name.py +++ b/example/use_case/search_instance_by_name.py @@ -1,5 +1,4 @@ from mcsmapi import MCSMAPI -import json baseurl = "http://localhost:23333" # Change to your MCSM panel address and port(include http or https etc.). @@ -17,7 +16,7 @@ # Get instance def get_instance(daemon_id: str, instance_name: str): # Get specific instance info from the daemon by name - instance_object = mcsm.instance() + instance_object = mcsm.instance instance_list = instance_object.search(daemonId=daemon_id, instance_name=instance_name).data # Error handling @@ -41,17 +40,16 @@ def get_instance(daemon_id: str, instance_name: str): # Print instance status print(instance.config.nickname) print(instance.status) -print(instance.space) # Optional: # start -#instance.start() +instance.start() # stop -#instance.stop() +instance.stop() # restart -#instance.restart() +instance.restart() # kill -#instance.kill() +instance.kill() diff --git a/example/user.py b/example/user.py index 70881d3..cd8793c 100644 --- a/example/user.py +++ b/example/user.py @@ -4,7 +4,7 @@ api.login("admin", "547cABC9bf88@") -userManager = api.user() +userManager = api.user users = userManager.search() @@ -13,7 +13,7 @@ print(user.userName) # 查找第一个管理员账号 -user = api.user().search(role=10).data[0] +user = userManager.search(role=10).data[0] # 封禁管理员账号 user.update({"permission": -1}) diff --git a/mcsmapi/__init__.py b/mcsmapi/__init__.py index 6b1e5d5..847db77 100644 --- a/mcsmapi/__init__.py +++ b/mcsmapi/__init__.py @@ -34,20 +34,26 @@ def login_with_apikey(self, apikey: str): self.authentication = "apikey" return self + @property def overview(self): return Overview() + @property def instance(self): return Instance() + @property def user(self) : return User() + @property def daemon(self): return Daemon() + @property def file(self): return File() + @property def image(self): return Image() diff --git a/mcsmapi/apis/daemon.py b/mcsmapi/apis/daemon.py index b82f0c0..4af1844 100644 --- a/mcsmapi/apis/daemon.py +++ b/mcsmapi/apis/daemon.py @@ -51,18 +51,18 @@ def system() -> list[DaemonSystemInfo]: return [DaemonSystemInfo(**daemon) for daemon in daemons] @staticmethod - def add(config: dict[str, Any]) -> str: + def add(config: DaemonConfig) -> str: """ 新增一个节点 - :params config: 节点的配置信息,以字典形式提供,缺失内容由DaemonConfig模型补全 + :params config: 节点的配置信息 :returns: 新增节点的UUID """ return send( "POST", f"{ApiPool.SERVICE}/remote_service", - data=DaemonConfig(**config).model_dump(), + data=config.model_dump(), ) @staticmethod diff --git a/mcsmapi/apis/file.py b/mcsmapi/apis/file.py index 1aa6dc2..a313c2a 100644 --- a/mcsmapi/apis/file.py +++ b/mcsmapi/apis/file.py @@ -126,7 +126,7 @@ async def upload(daemonId: str, uuid: str, file: bytes, upload_dir: str) -> bool return True @staticmethod - def copy(daemonId: str, uuid: str, copy_map: dict[str, str]) -> bool: + def copy_to(daemonId: str, uuid: str, copy_map: dict[str, str]) -> bool: """ 复制多个文件夹或文件到指定位置 @@ -156,7 +156,7 @@ def copyOne(daemonId: str, uuid: str, source: str, target: str) -> bool: :returns: 操作成功后返回True """ - return File.copy(daemonId, uuid, {source: target}) + return File.copy_to(daemonId, uuid, {source: target}) @staticmethod def move(daemonId: str, uuid: str, copy_map: dict[str, str]) -> bool: diff --git a/mcsmapi/apis/schedule.py b/mcsmapi/apis/schedule.py new file mode 100644 index 0000000..483b2aa --- /dev/null +++ b/mcsmapi/apis/schedule.py @@ -0,0 +1,71 @@ +from mcsmapi.pool import ApiPool +from mcsmapi.request import send +from mcsmapi.models.schedule import ScheduleDetail, SchedulePostBody + + +class Schedule: + + @staticmethod + def list(daemonId: str, uuid: str) -> list[ScheduleDetail]: + """ + 获取实例计划任务列表 + + :param daemonId: 节点ID + :param uuid: 实例ID + + :returns: 计划任务列表 + """ + result = send( + "GET", + f"{ApiPool.SCHEDULE}", + params={"daemonId": daemonId, "uuid": uuid}, + ) + return [ScheduleDetail(**r, daemonId=daemonId) for r in result] + + @staticmethod + def delete(daemonId: str, uuid: str, task_name: str) -> bool: + """ + 删除计划任务 + + :param daemonId: 节点ID + :param uuid: 实例ID + :param task_name: 计划任务名称 + + :returns: 是否成功 + """ + return send( + "DELETE", + f"{ApiPool.SCHEDULE}", + params={"daemonId": daemonId, "uuid": uuid, "task_name": task_name}, + ) + + @staticmethod + def create(daemonId: str, uuid: str, config: SchedulePostBody) -> bool: + """ + 创建计划任务 + + :param daemonId: 节点ID + :param uuid: 实例ID + :param config: 计划任务配置 + + :returns: 是否成功 + """ + return send( + "POST", + f"{ApiPool.SCHEDULE}", + params={"daemonId": daemonId, "uuid": uuid}, + data=config.model_dump(), + ) + + @staticmethod + def update(daemonId: str, uuid: str, config: SchedulePostBody) -> bool: + """ + 更新计划任务 + + :param daemonId: 节点ID + :param uuid: 实例ID + :param config: 计划任务配置 + + :returns: 是否成功 + """ + return Schedule.create(daemonId, uuid, config) diff --git a/mcsmapi/models/__init__.py b/mcsmapi/models/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/mcsmapi/models/daemon.py b/mcsmapi/models/daemon.py index 582c211..89d04bf 100644 --- a/mcsmapi/models/daemon.py +++ b/mcsmapi/models/daemon.py @@ -48,23 +48,8 @@ class DaemonSetting(BaseModel): """未知""" port: int """节点监听端口""" - - -class DaemonSystemInfo(BaseModel): - """节点系统信息""" - - version: str | None = None - """远程节点版本""" - process: ProcessInfo | None = None - """远程节点的基本信息""" - instance: InstanceStat | None = None - """远程节点实例基本信息""" - system: SystemInfo | None = None - """远程节点系统信息""" - cpuMemChart: list[CpuMemChart] | None = None - """cpu和内存使用趋势""" - config: DaemonSetting - """节点系统配置信息""" + maxDownloadFromUrlFileCount: int + """允许同时下载的远程下载任务数量""" class DaemonOperation(BaseModel): @@ -150,6 +135,33 @@ class DaemonConfig(BaseModel): """远程节点的apiKey""" +class DaemonSystemInfo(DaemonOperation): + """节点信息""" + + version: str | None = None + """节点版本""" + process: ProcessInfo | None = None + """节点进程信息""" + instance: InstanceStat | None = None + """节点实例统计信息""" + system: SystemInfo | None = None + """节点系统信息""" + cpuMemChart: list[CpuMemChart] | None = None + """cpu和内存使用趋势""" + config: DaemonSetting | None = None + """节点配置信息""" + available: bool + """节点可用状态""" + ip: str + """节点ip""" + port: int + """节点端口""" + prefix: str + """节点路径前缀""" + remarks: str + """节点备注""" + + class DaemonStatus(DaemonOperation): """节点状态信息""" diff --git a/mcsmapi/models/file.py b/mcsmapi/models/file.py index 03b4012..e4ee881 100644 --- a/mcsmapi/models/file.py +++ b/mcsmapi/models/file.py @@ -59,7 +59,7 @@ def delete(self): self.daemonId, self.uuid, [os.path.join(self.target, self.name)] ) - def copy2(self, target: str): + def copy_to(self, target: str): """ 复制该文件或文件夹到目标路径 diff --git a/mcsmapi/models/instance.py b/mcsmapi/models/instance.py index d61a275..3e94fd1 100644 --- a/mcsmapi/models/instance.py +++ b/mcsmapi/models/instance.py @@ -2,6 +2,7 @@ from typing import Any, TypedDict from pydantic import BaseModel, field_validator from mcsmapi.models.image import DockerConfig +from mcsmapi.models.schedule import ScheduleDetail, SchedulePostBody class CRLFType(IntEnum): @@ -275,6 +276,28 @@ def files( self.daemonId, self.instanceUuid, target, page, page_size, file_name ) + def list_schedule(self) -> list[ScheduleDetail]: + """ + 获取实例的计划任务列表 + + :returns: 计划任务列表 + """ + from mcsmapi.apis.schedule import Schedule + + return Schedule.list(self.daemonId, self.instanceUuid) + + def create_schedule(self, config: SchedulePostBody) -> bool: + """ + 创建计划任务 + + :param config: 计划任务配置 + + :returns: 是否成功 + """ + from mcsmapi.apis.schedule import Schedule + + return Schedule.create(self.daemonId, self.instanceUuid, config) + class InstanceCreateResult(BaseModel): """实例创建结果""" diff --git a/mcsmapi/models/schedule.py b/mcsmapi/models/schedule.py new file mode 100644 index 0000000..e871de4 --- /dev/null +++ b/mcsmapi/models/schedule.py @@ -0,0 +1,80 @@ +from enum import Enum, IntEnum +from pydantic import BaseModel + + +class ScheduleActionType(Enum): + """计划任务动作""" + + COMMAND = "command" + """发送命令""" + DELAY = "delay" + """等待x秒""" + STOP = "stop" + """停止实例""" + START = "start" + """启动实例""" + RESTART = "restart" + """重启实例""" + KILL = "kill" + """强制停止实例""" + + +class ScheduleType(IntEnum): + """计划任务类型""" + + INTERVAL = 1 + """间隔时间任务""" + CYCLE = 2 + """周期时间任务""" + SPECIFY = 3 + """指定时间任务""" + + +class ScheduleAction(BaseModel): + """计划任务动作""" + + type: ScheduleActionType + """动作类型""" + payload: str + """动作参数""" + + +class SchedulePostBody(BaseModel): + """计划任务配置参数""" + + name: str + """计划任务名称""" + count: int + """执行次数, -1表无限""" + time: str + """除了间隔时间任务是间隔秒数,其他都是rcon字符串""" + actions: list[ScheduleAction] + """计划任务动作链""" + type: ScheduleType + + +class ScheduleDetail(SchedulePostBody): + """计划任务信息""" + + instanceUuid: str + """实例uuid""" + daemonId: str + """节点id""" + + def delete(self): + """删除计划任务""" + from mcsmapi.apis.schedule import Schedule + + return Schedule.delete(self.daemonId, self.instanceUuid, self.name) + + def update(self, config: SchedulePostBody): + """ + 更新计划任务 + + :param config: 计划任务配置 + + :returns: 是否成功 + """ + from mcsmapi.apis.schedule import Schedule + + return Schedule.update(self.daemonId, self.instanceUuid, config) diff --git a/mcsmapi/pool.py b/mcsmapi/pool.py index 5cba869..5e2a8e7 100644 --- a/mcsmapi/pool.py +++ b/mcsmapi/pool.py @@ -13,3 +13,4 @@ def __str__(self): FILE = "api/files" IMAGE = "api/environment" LOG = "api/overview/operation_logs" + SCHEDULE = "api/protected_schedule" diff --git a/mcsmapi/request.py b/mcsmapi/request.py index 62dd5c8..795afc2 100644 --- a/mcsmapi/request.py +++ b/mcsmapi/request.py @@ -12,25 +12,22 @@ class Request: session = requests.Session() apikey: str | None = None token: str | None = None + """token需和cookie配合使用, 两者有相符性校验""" @classmethod def set_mcsm_url(cls, url: str): - """设置类级别的 mcsm_url""" cls.mcsm_url = url @classmethod def set_timeout(cls, timeout: int): - """设置类级别的 timeout""" cls.timeout = timeout @classmethod def set_apikey(cls, apikey: str): - """设置类级别的 apikey""" cls.apikey = apikey @classmethod def set_token(cls, token: str): - """设置类级别的 token""" cls.token = token @classmethod diff --git a/pyproject.toml b/pyproject.toml index a57a9f3..de96866 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ requires = ["setuptools", "wheel"] [project] name = "mcsmapi" -version = "0.1.8.b4" +version = "0.1.8" description = "Shortcut the pypi package of MCSM./快捷操作MCSM的pypi包" readme = "README.md" authors = [