feat: 左侧导航改版与 Python 镜像管理

- 应用外壳换成 stash-ui Layout:左侧导航(任务调度 / Python 镜像)+ 页面标题区,窄屏折叠为顶部导航
- 新增 python_mirrors 表与 CRUD/设默认接口,内置清华、阿里云、中科大、PyPI 官方
- Python 脚本任务运行时注入默认镜像的 PIP_INDEX_URL / UV_INDEX_URL,任务自身环境变量优先
- 新增镜像管理页与表单,支持新增、编辑、启用/停用、设默认、删除
- 重新生成 OpenAPI 与 TS 类型;补齐后端镜像测试、前端单测与 e2e,修复窄屏页面被宽表格撑开
This commit is contained in:
2026-11-03 22:12:11 +08:00
parent d7a6e17531
commit 29fc579275
25 changed files with 2491 additions and 47 deletions
+352
View File
@@ -89,6 +89,260 @@
}
}
},
"/api/v1/python/mirrors": {
"get": {
"tags": [
"python-mirrors"
],
"operationId": "list_python_mirrors",
"responses": {
"200": {
"description": "Python package mirrors",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PythonMirrorResponse"
}
}
}
}
}
}
},
"post": {
"tags": [
"python-mirrors"
],
"operationId": "create_python_mirror",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreatePythonMirrorRequest"
}
}
},
"required": true
},
"responses": {
"201": {
"description": "Mirror created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PythonMirrorResponse"
}
}
}
},
"400": {
"description": "Validation failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"409": {
"description": "Mirror URL already exists",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
},
"/api/v1/python/mirrors/{id}": {
"get": {
"tags": [
"python-mirrors"
],
"operationId": "get_python_mirror",
"parameters": [
{
"name": "id",
"in": "path",
"description": "Mirror identifier",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Mirror detail",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PythonMirrorResponse"
}
}
}
},
"404": {
"description": "Mirror not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
},
"put": {
"tags": [
"python-mirrors"
],
"operationId": "update_python_mirror",
"parameters": [
{
"name": "id",
"in": "path",
"description": "Mirror identifier",
"required": true,
"schema": {
"type": "string"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UpdatePythonMirrorRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Mirror updated",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PythonMirrorResponse"
}
}
}
},
"400": {
"description": "Validation failed",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"404": {
"description": "Mirror not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"409": {
"description": "Mirror URL already exists",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
},
"delete": {
"tags": [
"python-mirrors"
],
"operationId": "delete_python_mirror",
"parameters": [
{
"name": "id",
"in": "path",
"description": "Mirror identifier",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"204": {
"description": "Mirror deleted"
},
"404": {
"description": "Mirror not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
},
"/api/v1/python/mirrors/{id}/default": {
"post": {
"tags": [
"python-mirrors"
],
"operationId": "set_default_python_mirror",
"parameters": [
{
"name": "id",
"in": "path",
"description": "Mirror identifier",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Default mirror updated",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PythonMirrorResponse"
}
}
}
},
"404": {
"description": "Mirror not found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
}
}
},
"/api/v1/python/venv": {
"post": {
"tags": [
@@ -876,6 +1130,31 @@
"skip"
]
},
"CreatePythonMirrorRequest": {
"type": "object",
"required": [
"name",
"index_url"
],
"properties": {
"enabled": {
"type": "boolean"
},
"index_url": {
"type": "string"
},
"name": {
"type": "string"
},
"trusted_host": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": false
},
"CreateTaskRequest": {
"type": "object",
"required": [
@@ -1095,6 +1374,53 @@
}
}
},
"PythonMirrorResponse": {
"type": "object",
"description": "一个 Python 包索引镜像(pip / uv)。",
"required": [
"id",
"name",
"index_url",
"enabled",
"is_default",
"created_at",
"updated_at"
],
"properties": {
"created_at": {
"type": "string",
"format": "date-time"
},
"enabled": {
"type": "boolean"
},
"id": {
"type": "string"
},
"index_url": {
"type": "string",
"description": "pip 的 `--index-url`,运行时注入 `PIP_INDEX_URL`"
},
"is_default": {
"type": "boolean",
"description": "当前默认镜像;同一时间最多一个"
},
"name": {
"type": "string"
},
"trusted_host": {
"type": [
"string",
"null"
],
"description": "pip 的 `--trusted-host`;https 源通常为空"
},
"updated_at": {
"type": "string",
"format": "date-time"
}
}
},
"RetryPolicy": {
"type": "object",
"properties": {
@@ -1539,6 +1865,32 @@
}
}
},
"UpdatePythonMirrorRequest": {
"type": "object",
"required": [
"name",
"index_url",
"enabled"
],
"properties": {
"enabled": {
"type": "boolean"
},
"index_url": {
"type": "string"
},
"name": {
"type": "string"
},
"trusted_host": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": false
},
"UpdateTaskRequest": {
"type": "object",
"required": [