diff --git a/.gitignore b/.gitignore index d56c817..feb467d 100644 --- a/.gitignore +++ b/.gitignore @@ -665,10 +665,8 @@ FodyWeavers.xsd config.json res_data GsData.db -GenshinUID -StarRailUID data -plugins/* -!plugins/core_command -!plugins/gs_test.py +gsuid_core/plugins/* +!gsuid_core/plugins/core_command +!gsuid_core/plugins/gs_test.py logs diff --git a/gsuid_core/core.py b/gsuid_core/core.py index efae4ea..a2dd80d 100644 --- a/gsuid_core/core.py +++ b/gsuid_core/core.py @@ -23,6 +23,14 @@ from gsuid_core.utils.plugins_config.models import ( # noqa: E402 from gsuid_core.utils.plugins_config.gs_config import ( # noqa: E402 all_config_list, ) +from gsuid_core.utils.plugins_update._plugins import ( # noqa: E402 + check_status, + check_plugins, + install_plugin, + update_plugins, + check_can_update, + get_plugins_list, +) app = FastAPI() HOST = core_config.get_config('HOST') @@ -95,6 +103,47 @@ def main(): value = data[name] all_config_list[config_name].set_config(name, value) + @app.get('/genshinuid/api/getPlugins') + @site.auth.requires('admin') + async def _get_plugins(request: Request): + tasks = [] + plugins_list = await get_plugins_list() + for name in plugins_list: + plugin = plugins_list[name] + link = plugin['link'] + plugin_name = link.split('/')[-1] + # git_path = f'{proxy_url}{link}.git' + sample = { + 'label': plugin_name, + 'key': name, + 'status': check_status(plugin_name), + 'remark': plugin['info'], + } + tasks.append(sample) + + return tasks + + @app.post('/genshinuid/api/updatePlugins') + @site.auth.requires('admin') + async def _update_plugins(request: Request, data: Dict): + repo = check_plugins(data['label']) + if repo: + if check_can_update(repo): + try: + update_plugins(data['label']) + retcode = 0 + except: # noqa:E722 + retcode = -1 + else: + retcode = 0 + else: + try: + retcode = await install_plugin(data['key']) + retcode = 0 + except: # noqa:E722 + retcode = -1 + return {'status': retcode, 'msg': '', 'data': {}} + site.mount_app(app) uvicorn.run( @@ -102,23 +151,23 @@ def main(): host=HOST, port=PORT, log_config={ - "version": 1, - "disable_existing_loggers": False, - "handlers": { - "default": { - "class": "gsuid_core.logger.LoguruHandler", + 'version': 1, + 'disable_existing_loggers': False, + 'handlers': { + 'default': { + 'class': 'gsuid_core.logger.LoguruHandler', }, }, - "loggers": { - "uvicorn.error": {"handlers": ["default"], "level": "INFO"}, - "uvicorn.access": { - "handlers": ["default"], - "level": "INFO", + 'loggers': { + 'uvicorn.error': {'handlers': ['default'], 'level': 'INFO'}, + 'uvicorn.access': { + 'handlers': ['default'], + 'level': 'INFO', }, }, }, ) -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/gsuid_core/plugins/core_command/install_plugins/__init__.py b/gsuid_core/plugins/core_command/install_plugins/__init__.py index aea6ffd..11771dc 100644 --- a/gsuid_core/plugins/core_command/install_plugins/__init__.py +++ b/gsuid_core/plugins/core_command/install_plugins/__init__.py @@ -1,8 +1,7 @@ from gsuid_core.sv import SV from gsuid_core.bot import Bot from gsuid_core.models import Event - -from ._plugins import ( +from gsuid_core.utils.plugins_update._plugins import ( refresh_list, update_plugins, get_plugins_url, diff --git a/gsuid_core/utils/cookie_manager/add_ck.py b/gsuid_core/utils/cookie_manager/add_ck.py index d3ef7d6..a27a98b 100644 --- a/gsuid_core/utils/cookie_manager/add_ck.py +++ b/gsuid_core/utils/cookie_manager/add_ck.py @@ -231,6 +231,10 @@ async def _deal_ck(bot_id: str, mes: str, user_id: str) -> str: if is_add_stoken: im_list.append(f'添加Stoken成功,stuid={account_id},stoken={stoken}') + + if uid is None: + uid = '0' + await sqla.insert_user_data( user_id, uid, sr_uid, account_cookie, app_cookie ) diff --git a/gsuid_core/plugins/core_command/install_plugins/_plugins.py b/gsuid_core/utils/plugins_update/_plugins.py similarity index 69% rename from gsuid_core/plugins/core_command/install_plugins/_plugins.py rename to gsuid_core/utils/plugins_update/_plugins.py index 5a7cde0..dc24858 100644 --- a/gsuid_core/plugins/core_command/install_plugins/_plugins.py +++ b/gsuid_core/utils/plugins_update/_plugins.py @@ -2,7 +2,7 @@ from typing import Dict, List, Union, Optional import aiohttp from git.repo import Repo -from git.exc import GitCommandError +from git.exc import GitCommandError, InvalidGitRepositoryError from gsuid_core.logger import logger @@ -27,6 +27,12 @@ async def refresh_list() -> List[str]: return refresh_list +async def get_plugins_list() -> Dict[str, Dict[str, str]]: + if not plugins_list: + await refresh_list() + return plugins_list + + async def get_plugins_url(name: str) -> Optional[Dict[str, str]]: if not plugins_list: await refresh_list() @@ -50,9 +56,54 @@ def install_plugins(plugins: Dict[str, str]) -> str: if path.exists(): return '该插件已经安装过了!' Repo.clone_from(git_path, path, single_branch=True, depth=1) + logger.info(f'插件{plugin_name}安装成功!') return f'插件{plugin_name}安装成功!发送[gs重启]以应用!' +async def install_plugin(plugin_name: str) -> int: + url = await get_plugins_url(plugin_name) + if url is None: + return -1 + install_plugins(url) + return 0 + + +def check_plugins(plugin_name: str) -> Optional[Repo]: + path = PLUGINS_PATH / plugin_name + if path.exists(): + try: + repo = Repo(path) + except InvalidGitRepositoryError: + return None + return repo + else: + return None + + +def check_can_update(repo: Repo) -> bool: + try: + remote = repo.remote() # 获取远程仓库 + remote.fetch() # 从远程获取最新版本 + except GitCommandError as e: + logger.error(f'发生Git命令错误{e}!') + return False + local_commit = repo.commit() # 获取本地最新提交 + remote_commit = remote.fetch()[0].commit # 获取远程最新提交 + if local_commit.hexsha == remote_commit.hexsha: # 比较本地和远程的提交哈希值 + return False + return True + + +def check_status(plugin_name: str) -> int: + repo = check_plugins(plugin_name) + if repo is None: + return 3 + if check_can_update(repo): + return 1 + else: + return 4 + + def update_plugins( plugin_name: str, level: int = 0, @@ -66,10 +117,9 @@ def update_plugins( plugin_name = _name break - path = PLUGINS_PATH / plugin_name - if not path.exists(): + repo = check_plugins(plugin_name) + if not repo: return '更新失败, 不存在该插件!' - repo = Repo(path) # type: ignore o = repo.remotes.origin if level >= 2: diff --git a/gsuid_core/plugins/core_command/install_plugins/api.py b/gsuid_core/utils/plugins_update/api.py similarity index 84% rename from gsuid_core/plugins/core_command/install_plugins/api.py rename to gsuid_core/utils/plugins_update/api.py index b87da28..9ae1402 100644 --- a/gsuid_core/plugins/core_command/install_plugins/api.py +++ b/gsuid_core/utils/plugins_update/api.py @@ -7,4 +7,4 @@ plugins_lib = 'https://docs.gsuid.gbots.work/plugin_list.json' proxy_url = 'https://ghproxy.com/' -PLUGINS_PATH = Path(__file__).parents[2] +PLUGINS_PATH = Path(__file__).parents[2] / 'plugins' diff --git a/gsuid_core/webconsole/create_task_panel.py b/gsuid_core/webconsole/create_task_panel.py new file mode 100644 index 0000000..efacb39 --- /dev/null +++ b/gsuid_core/webconsole/create_task_panel.py @@ -0,0 +1,20 @@ +def get_tasks_panel(): + return { + 'type': 'tasks', + 'name': 'tasks', + 'items': [{'label': '加载中, 请稍等...', 'key': 'wait', 'status': 2}], + 'id': 'u:571849ba0356', + 'initialStatusCode': 0, + 'readyStatusCode': 1, + 'loadingStatusCode': 2, + 'errorStatusCode': 3, + 'finishStatusCode': 4, + 'canRetryStatusCode': 5, + 'statusTextMap': ['未开始', '可更新', '安装中', '未安装', '已最新', '出错'], + 'taskNameLabel': '插件列表', + 'operationLabel': '操作', + 'remarkLabel': '备注说明', + 'btnText': '安装/更新', + 'submitApi': 'post:/genshinuid/api/updatePlugins', + 'checkApi': 'get:/genshinuid/api/getPlugins', + } diff --git a/gsuid_core/webconsole/models.py b/gsuid_core/webconsole/models.py index cec5687..04315eb 100644 --- a/gsuid_core/webconsole/models.py +++ b/gsuid_core/webconsole/models.py @@ -1,3 +1,5 @@ +from typing import TypedDict + from fastapi_user_auth.auth.models import User from fastapi_amis_admin.models.fields import Field @@ -8,3 +10,10 @@ class WebUser(User, table=True): parent_id: int = Field( None, title='Superior', foreign_key='auth_user.id' ) # type:ignore + + +class Task(TypedDict): + label: str + key: str + status: int + remark: str diff --git a/gsuid_core/webconsole/mount_app.py b/gsuid_core/webconsole/mount_app.py index 316726f..e9ffef2 100644 --- a/gsuid_core/webconsole/mount_app.py +++ b/gsuid_core/webconsole/mount_app.py @@ -43,6 +43,7 @@ from gsuid_core.utils.cookie_manager.add_ck import _deal_ck from gsuid_core.webconsole.html import gsuid_webconsole_help from gsuid_core.webconsole.create_sv_panel import get_sv_page from gsuid_core.version import __version__ as GenshinUID_version +from gsuid_core.webconsole.create_task_panel import get_tasks_panel from gsuid_core.webconsole.create_config_panel import get_config_page from gsuid_core.utils.database.models import GsBind, GsPush, GsUser, GsCache from gsuid_core.webconsole.login_page import ( # noqa # 不要删 @@ -395,7 +396,7 @@ class SVManagePage(GsAdminPage): class ConfigManagePage(GsAdminPage): page_schema = PageSchema( label=('修改设定'), - icon='fa fa-sliders', + icon='fa fa-cogs', url='/ConfigManage', isDefaultPage=True, sort=100, @@ -403,5 +404,17 @@ class ConfigManagePage(GsAdminPage): page = Page.parse_obj(get_config_page()) +@site.register_admin +class PluginsManagePage(GsAdminPage): + page_schema = PageSchema( + label=('插件管理'), + icon='fa fa-puzzle-piece', + url='/ConfigManage', + isDefaultPage=True, + sort=100, + ) + page = Page.parse_obj(get_tasks_panel()) + + # 取消注册默认管理类 site.unregister_admin(admin.HomeAdmin, APIDocsApp) diff --git a/poetry.lock b/poetry.lock index c25a688..8894bcb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -667,14 +667,14 @@ reference = "mirrors" [[package]] name = "fastapi-amis-admin" -version = "0.5.5" +version = "0.5.6" description = "FastAPI-Amis-Admin is a high-performance, efficient and easily extensible FastAPI admin framework. Inspired by Django-admin, and has as many powerful functions as Django-admin." category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "fastapi_amis_admin-0.5.5-py3-none-any.whl", hash = "sha256:8faeece0962a7db0f807e68c09fa45ed75e79ba55c9990e734de486ee6bbf4c1"}, - {file = "fastapi_amis_admin-0.5.5.tar.gz", hash = "sha256:b3c57f42fad800906cb39e0d1ea67d597747c09875ae74f20fda1d516e8b2580"}, + {file = "fastapi_amis_admin-0.5.6-py3-none-any.whl", hash = "sha256:df9160d4b28f2a2165c17ec678eddc2bfe7323c77c9dfdd8217a7e8b6895e99d"}, + {file = "fastapi_amis_admin-0.5.6.tar.gz", hash = "sha256:8bcb74d11d4e1b605b2d3eccecf308a6c5ffce65d4d8bef300de6764f0b64107"}, ] [package.dependencies] @@ -1054,14 +1054,14 @@ reference = "mirrors" [[package]] name = "identify" -version = "2.5.23" +version = "2.5.24" description = "File identification library for Python" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "identify-2.5.23-py2.py3-none-any.whl", hash = "sha256:17d9351c028a781456965e781ed2a435755cac655df1ebd930f7186b54399312"}, - {file = "identify-2.5.23.tar.gz", hash = "sha256:50b01b9d5f73c6b53e5fa2caf9f543d3e657a9d0bbdeb203ebb8d45960ba7433"}, + {file = "identify-2.5.24-py2.py3-none-any.whl", hash = "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"}, + {file = "identify-2.5.24.tar.gz", hash = "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4"}, ] [package.extras] diff --git a/requirements.txt b/requirements.txt index 9b00c9c..d9aa4eb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,7 +17,7 @@ click==8.1.3 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" colorama==0.4.6 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" and platform_system == "Windows" or python_full_version >= "3.8.1" and python_full_version < "4.0.0" and sys_platform == "win32" dnspython==2.3.0 ; python_full_version >= "3.8.1" and python_version < "4.0" email-validator==2.0.0.post2 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" -fastapi-amis-admin==0.5.5 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" +fastapi-amis-admin==0.5.6 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" fastapi-user-auth==0.5.0 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" fastapi==0.95.1 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0" frozenlist==1.3.3 ; python_full_version >= "3.8.1" and python_full_version < "4.0.0"