初始提交 - FutureOSS v1.0 插件化运行时框架

一切皆为插件的开发者工具运行时框架

🧩 核心特性:
  - 插件热插拔 (importlib 动态加载)
  - 依赖自动解析 (拓扑排序 + 循环检测)
  - 企业级稳定 (熔断/降级/重试/隔离)
  - 事件驱动 (发布/订阅事件总线)
  - 完整配置 (YAML 配置 + 热重载)
This commit is contained in:
Falck
2026-04-06 09:57:10 +08:00
commit 76147bae94
174 changed files with 15626 additions and 0 deletions

View File

@@ -0,0 +1,158 @@
"""Web 工具包 - 路由注册、静态文件服务、前端事件(不负责渲染)"""
import json
from pathlib import Path
from oss.plugin.types import Plugin, register_plugin_type, Response
from .router import WebRouter
from .static import StaticFileHandler
from .template import TemplateEngine
class WebToolkitPlugin(Plugin):
"""Web 工具包插件 - 提供网站前端所有服务"""
def __init__(self):
self.router = None
self.static_handler = None
self.template_engine = None
self.http_api = None
self.http_tcp = None
self.storage = None
self.config = {} # 从 config.json 读取
self.root_dir = None
def init(self, deps: dict = None):
"""初始化 - 读取 config.json 配置"""
self.router = WebRouter()
self.template_engine = TemplateEngine()
self._load_config()
self.static_handler = StaticFileHandler(root=str(self.root_dir))
print(f"[web-toolkit] 配置加载完成: root_dir={self.root_dir}")
def start(self):
"""启动"""
# 注册路由到 http-api
if self.http_api:
http_instance = self.http_api
if hasattr(http_instance, "router"):
# 精确路由先注册,参数化路由后注册
http_instance.router.get(
self.config.get("website_prefix", "/website") + "/",
self._serve_website_index
)
http_instance.router.get(
self.config.get("website_prefix", "/website") + "/:path",
self._serve_static
)
http_instance.router.get(
self.config.get("static_prefix", "/static") + "/:path",
self._serve_static
)
# 注册路由到 http-tcp
if self.http_tcp:
tcp_instance = self.http_tcp
if hasattr(tcp_instance, "router"):
tcp_instance.router.get(
self.config.get("website_prefix", "/website") + "/",
self._serve_website_index
)
tcp_instance.router.get(
self.config.get("website_prefix", "/website") + "/:path",
self._serve_static
)
tcp_instance.router.get(
self.config.get("static_prefix", "/static") + "/:path",
self._serve_static
)
print("[web-toolkit] Web 工具包已启动")
def stop(self):
"""停止"""
pass
def set_http_api(self, instance):
"""设置 HTTP API 实例"""
self.http_api = instance
def set_http_tcp(self, instance):
"""设置 HTTP TCP 实例"""
self.http_tcp = instance
def set_plugin_storage(self, instance):
"""设置 plugin-storage 实例(唯一文件读写入口)"""
self.storage = instance
def set_static_dir(self, path: str):
"""设置静态文件目录"""
self.static_handler.set_root(path)
def set_template_dir(self, path: str):
"""设置模板目录"""
template_root = Path(path)
if template_root.exists():
self.template_engine.set_root(str(template_root))
def _load_config(self):
"""读取 config.json解析网站根目录"""
config_path = Path("./data/web-toolkit/config.json")
if not config_path.exists():
print("[web-toolkit] 警告: config.json 不存在,使用默认配置")
self.config = {
"root_dir": "../website",
"index_file": "index.html",
"static_prefix": "/static",
"website_prefix": "/website",
}
else:
with open(config_path, "r", encoding="utf-8") as f:
self.config = json.load(f)
# 解析根目录(相对于 config.json 的路径)
root_relative = self.config.get("root_dir", "../website")
self.root_dir = (config_path.parent / root_relative).resolve()
# 初始化模板引擎
template_dir = self.config.get("template_dir", "")
if template_dir:
template_path = self.root_dir / template_dir
if template_path.exists():
self.template_engine.set_root(str(template_path))
def _serve_website_index(self, request):
"""提供 website 目录首页"""
index_file = self.config.get("index_file", "index.html")
if self.root_dir:
path = self.root_dir / index_file
if path.exists():
content = path.read_text(encoding="utf-8")
return Response(
status=200,
headers={"Content-Type": "text/html; charset=utf-8"},
body=content
)
return Response(status=404, body="Index file not found")
def _serve_static(self, request):
"""提供静态文件"""
path = request.path
website_prefix = self.config.get("website_prefix", "/website")
static_prefix = self.config.get("static_prefix", "/static")
if path.startswith(website_prefix + "/"):
filename = path[len(website_prefix) + 1:]
elif path.startswith(static_prefix + "/"):
filename = path[len(static_prefix) + 1:]
else:
filename = path.lstrip("/")
if not filename:
return self._serve_website_index(request)
return self.static_handler.serve(filename)
register_plugin_type("WebToolkitPlugin", WebToolkitPlugin)
def New():
return WebToolkitPlugin()