mirror of
https://github.com/insistence/RuoYi-Vue3-FastAPI.git
synced 2026-10-14 21:07:36 +00:00
* feat: 支持 v2 插件、原生扩展与签名发布 * fix: 修复测试 * feat: 增强宿主 SDK、运行观测与制品维护能力 * feat: 增强文件传输、配置观测与本地开发能力 * feat: 支持 SSE 实时事件流并完善开发脚手架 * feat: 完善长连接鉴权复核与关闭回收 * feat: 增强生命周期与任务关闭可靠性 * feat: 收紧健康检查契约并补齐前端与真实网络验收 * feat: 增加SDK溯源与离线更新工具并验证跨版本兼容 * feat: 增加持久化业务示例与受控 Python 交付 * feat: 增加统一运行诊断与多worker状态观测 * feat: 增加固定目标发布等待与自动验收 * fix: 修复多环境插件部署前缀与代理配置不一致 * fix: 修复测试
80 lines
2.6 KiB
Python
80 lines
2.6 KiB
Python
from collections.abc import AsyncGenerator
|
|
from contextlib import asynccontextmanager
|
|
from typing import Any
|
|
|
|
from fastapi import FastAPI, HTTPException, Request
|
|
from pydantic import BaseModel, Field
|
|
|
|
from plugins.core.sdk import PluginDefinition, PluginHostContext, PluginRequestContext, plugin_endpoint
|
|
|
|
|
|
class EchoBody(BaseModel):
|
|
"""
|
|
SDK 请求体模型,约束示例消息长度。
|
|
"""
|
|
|
|
message: str = Field(min_length=1, max_length=200)
|
|
|
|
|
|
async def echo(context: PluginRequestContext) -> dict[str, Any]:
|
|
return {**context.request.to_payload(), 'requestId': context.request_id}
|
|
|
|
|
|
def create_plugin(host: PluginHostContext) -> PluginDefinition:
|
|
"""
|
|
声明子应用工厂,资源初始化交给子应用生命周期。
|
|
|
|
:param host: 宿主提供的插件上下文
|
|
:return: 插件能力声明
|
|
"""
|
|
return PluginDefinition(app_factory=create_app)
|
|
|
|
|
|
def create_app(host: PluginHostContext) -> FastAPI:
|
|
"""
|
|
为当前 worker 创建独立的 ASGI 子应用。
|
|
|
|
:param host: 宿主提供的插件上下文
|
|
:return: 包含权限检查和生命周期管理的子应用
|
|
"""
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI) -> AsyncGenerator[dict[str, str], None]:
|
|
"""
|
|
演示插件自有资源的启动与关闭。
|
|
|
|
:param app: 正在启动或关闭的 ASGI 子应用
|
|
:return: 可供请求访问的生命周期状态
|
|
"""
|
|
host.logger.info('ASGI 示例启动')
|
|
try:
|
|
yield {'message': '子应用资源已就绪'}
|
|
finally:
|
|
host.logger.info('ASGI 示例关闭')
|
|
|
|
app = FastAPI(title='ASGI 插件示例', lifespan=lifespan, docs_url=None, redoc_url=None)
|
|
|
|
@app.get(
|
|
'/api/info',
|
|
summary='获取ASGI插件信息接口',
|
|
description='用于获取当前插件标识、宿主API版本和子应用资源状态',
|
|
)
|
|
async def info(request: Request) -> dict[str, str]:
|
|
context = request.state.plugin_context
|
|
try:
|
|
context.require_permission('asgi_demo:view')
|
|
except PermissionError as exc:
|
|
raise HTTPException(status_code=403, detail=str(exc)) from exc
|
|
return {'pluginId': host.plugin_id, 'hostApiVersion': host.api_version, 'message': request.state.message}
|
|
|
|
app.add_api_route(
|
|
'/api/echo/{item_id}',
|
|
plugin_endpoint(echo, permission='asgi_demo:view', body_model=EchoBody),
|
|
methods=['POST'],
|
|
summary='验证ASGI插件请求上下文接口',
|
|
description='返回校验后的请求体、路径参数、多值查询参数与宿主追踪标识',
|
|
response_model=dict[str, Any],
|
|
)
|
|
|
|
return app
|