Files
tick-stock-panel/docs/extension-packages.md
shy3130 ffe1ff0d3e feat(import): 统一导入备份入口并支持后端扩展包
集中处理策略与定义、配置备份、数据源和页面插件导入,保留旧入口跳转。新增可信后端及前后端完整包的预检、暂存、重启生效、失败回退和版本管理。

验证:扩展、资源、备份与页面定向 pytest 通过,前端 41 项测试及生产构建通过,并完成桌面、窄屏和明暗主题联调。

Signed-off-by: shy3130 <shy3130@qq.com>
2026-11-01 20:01:59 +08:00

103 lines
5.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 统一导入与外部后端扩展包
入口是「设置 → 导入与备份」(`/settings?tab=import`)。策略、因子、信号页的小盒子,以及数据源的「安装插件」都会进入同一页面。旧配置备份和页面插件链接继续重定向到这里。
## 导入与管理
选择或拖入文件后,先自动识别、预览,再确认写入:
- 策略 `.py`、信号/因子 `.json` 和资源 ZIP 可混合多选;复用原有依赖闭包、冲突比较、批次回滚和缓存刷新。
- `tick-config-backup` 配置备份按类别恢复。每次新预览都默认不选择密钥和访问凭证;需要迁移 API Key 时明确勾选。
- 根目录有 `plugin.yaml` 的数据源包继续使用 [Provider 安装契约](external-data-source-packages.md)。安装后在「已安装扩展」配置 Key、验证并选择数据能力;不会自动切换行情来源。
- `.js/.mjs` 页面产物、单个 `.tsx` 或本项目已有源码页面继续使用 [Page Pack SDK](page-sdk.md)。确认信任和同名替换,通过浏览器加载校验后刷新生效。源码编译仍受现有部署环境限制。
- 根目录有 `extension.yaml` 的 ZIP 使用下述后端扩展契约,可同时包含配套前端页面。安装仅暂存,重启主程序后生效。
「创建配置备份」和「批量导出资源」保留原有范围,不包含后台扩展的安装文件。行情数据、K 线和回测缓存不包含在配置备份中。外部数据源与后端扩展请单独保留原 ZIP。
## 后端 / 前后端完整包
ZIP 根目录直接放清单,不额外套目录。每包一个后端模块,可选一个自包含页面 ES 模块:
```text
research.zip
├── extension.yaml
├── backend/
│ ├── __init__.py
│ └── extension.py
└── frontend/
└── extension.mjs
```
```yaml
format: tick-extension-package
format_version: 1
id: example.research
display_name: 研究观察工作台
version: 1.0.0
extension_api_version: 1
author: 作者声明
description: 自定义研究服务与配套页面
backend: backend.extension
frontend: frontend/extension.mjs # 纯后端扩展省略此项
requires_app: '>=0.3.25'
requires_python: '>=3.11'
platforms: [any]
requires: [] # 仅检查已有依赖,不运行 pip/npm
```
`id` 为小写字母开头的字母、数字、下划线,可用点或连字符分段,最长 80 字符;`official.*`、`core.*` 保留。版本如 `1.0.0`;相同版本不同内容拒绝覆盖。更新必须提高版本,降级使用回退操作。
后端模块沿用 `app.extensions` 的真实契约:
```python
from fastapi import APIRouter
from app.extensions import BACKEND_EXTENSION_API_VERSION
EXTENSION_ID = "example.research"
EXTENSION_API_VERSION = BACKEND_EXTENSION_API_VERSION
def setup(registrar):
router = APIRouter(prefix="/api/extensions/example.research")
@router.get("/status")
def status():
return {"ready": True}
registrar.include_router(router)
def startup(context):
# 可选;核心仓库准备完成后运行。
# context 提供 api_version、data_dir、只读 repository 协议。
pass
```
`setup` / `startup` 是同步函数。可注册 HTTP 路由和 `NotificationFormatter`,不支持替换核心引擎。外置接口必须位于 `/api/extensions/<包id>/`,不能覆盖核心或其他扩展,不接受 WebSocket 路由。初始化使用 `startup(context)`,不要依赖路由器自身的 lifespan。
配套页面使用 Page Pack SDK v1,`default.id` 与包 `id` 相同。用户确认信任后,浏览器先加载候选页面检查导出契约,成功才提交安装;预览阶段不执行 JS 或 Python。页面路由最终由宿主注册表检查,启动时发生冲突会显示在「已安装扩展」,需修正包后重新安装。
## 生效与失败隔离
安装内容位于 `data/extensions/<id>/versions/<version>/`,状态原子写入 `state.json`。限 20 MiB 压缩包、80 MiB 解压总量、1000 条目;拒绝越界路径、重复/大小写冲突、符号链接、原生库、wheel 和安装脚本。不写入程序源码目录。
启动时验证文件摘要、兼容性、ID、接口命名空间和注册冲突,再运行启动钩子。成功后才发布路由、通知格式化器和配套页面。新后台失败时保留原版本,新页面不会先切换;单个扩展错误不阻断主程序或其他扩展。
启停、回退、更新和卸载在下一次主程序启动时应用;浏览器刷新不能替代后台重启。停用中回退不会自动启用。旧页面可能保留在已打开标签中,后台重启完成后需刷新浏览器同步。
失败隔离覆盖已提交的路由、策略注册与安装状态;可信代码自行产生的文件、网络等副作用无法自动撤销。扩展与主程序同权限,不是沙箱。管理使用面板登录会话,不向开放 API Token 授权;无密码时仅接受本机同源管理。
首版面向单进程部署;多 worker 应全部停止后统一维护和重启,不提供跨 worker 在线切换。升级后仍需满足包的兼容范围。
## API
| 请求 | 行为 |
| --- | --- |
| `POST /api/import-center/inspect` | 多文件 `files`,只读识别并委托既有预检 |
| `GET /api/extension-packages` | 安装、运行、待生效版本与错误 |
| `POST /api/extension-packages/install` | `file/digest/revision/trusted=true`,重新校验后暂存 |
| `POST /api/extension-packages/frontend` | 信任后读取候选页面供浏览器校验,不发布 |
| `POST /api/extension-packages/{id}/manage` | `enable/disable/rollback/cancel-update/remove` |
| `POST /api/import-center/page` | 页面上传/编译,增加摘要、信任和同名替换确认 |
| `POST /api/import-center/project` | 信任后编译现有项目页面,保留同名确认 |
配套页面复用 `/api/ext-pages` 清单与分发,标记 `managed_by`,只在对应后台版本就绪时提供。启停和卸载通过整包管理,不能拆开修改。