Phase 3: PluginWebMixin SDK + web plugin control panels
- New: sdk/plugin_web.py (PluginWebMixin: web pages, API routes, SSE) - New: services/web_panel/routes/plugin_web.py (3 endpoints) - New: plugins/example_plugin/dashboard.html (counter + SSE demo) - Updated: example_plugin uses PluginWebMixin - Tests: 28/28 passing
This commit is contained in:
@@ -1,22 +1,28 @@
|
||||
#!/usr/bin/env python3
|
||||
import logging
|
||||
import logging, os
|
||||
from typing import Dict
|
||||
from aiohttp import web
|
||||
try:
|
||||
from sdk.plugin_command_decorator import plugin_command, command
|
||||
from sdk.plugin_web import PluginWebMixin
|
||||
except ImportError:
|
||||
def plugin_command(n=None,d=None,p=None):
|
||||
def deco(f):
|
||||
f._is_plugin_command=True;f._command_name=n or f.__name__
|
||||
f._command_description=d or (f.__doc__ or "").strip();return f
|
||||
def deco(f):f._is_plugin_command=True;f._command_name=n or f.__name__;return f
|
||||
return deco
|
||||
command=plugin_command
|
||||
class PluginWebMixin:
|
||||
def register_web_page(self,*a,**k):pass
|
||||
def register_api_route(self,*a,**k):pass
|
||||
|
||||
logger=logging.getLogger(__name__)
|
||||
|
||||
class Plugin:
|
||||
def __init__(self, plugin_name=None, config=None, bridge=None, n=None, c=None):
|
||||
self.plugin_name=plugin_name or n;self.config=config or c;self.bridge=bridge
|
||||
self.network_bridge=None;self.is_running=False
|
||||
class Plugin(PluginWebMixin):
|
||||
def __init__(self, plugin_name=None, config=None, bridge=None):
|
||||
PluginWebMixin.__init__(self)
|
||||
self.plugin_name=plugin_name or "example"
|
||||
self.config=config or {}
|
||||
self.bridge=bridge
|
||||
self.network_bridge=None
|
||||
self.is_running=False
|
||||
|
||||
async def initialize(self):
|
||||
logger.info(f"init: {self.plugin_name}")
|
||||
@@ -28,17 +34,21 @@ class Plugin:
|
||||
"/api/example/info",self._api_info,methods=["GET"],require_auth=False)
|
||||
except Exception as e:
|
||||
logger.warning(f"network skip: {e}")
|
||||
# Load dashboard HTML from file
|
||||
html_path=os.path.join(os.path.dirname(__file__),"dashboard.html")
|
||||
if os.path.exists(html_path):
|
||||
with open(html_path) as hf:
|
||||
self.register_web_page("example_plugin","Example Plugin",hf.read(),icon="P")
|
||||
self.is_running=True
|
||||
|
||||
async def _api_info(self,req):
|
||||
from aiohttp import web
|
||||
return web.json_response({"plugin":self.plugin_name,"status":"running"})
|
||||
|
||||
@plugin_command(name="echo",description="echo input")
|
||||
async def cmd_echo(self,*args):
|
||||
return " ".join(args) if args else "echo: no input"
|
||||
|
||||
@plugin_command(name="plugin_status",description="show plugin status")
|
||||
@plugin_command(name="plugin_status",description="show status")
|
||||
async def cmd_status(self,*args):
|
||||
return f"{self.plugin_name} v{self.config.get('version','?')} - running"
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html><html lang="zh"><head><meta charset="UTF-8"><title>Example</title>
|
||||
<style>body{font-family:monospace;background:#0e1416;color:#e0e3e4;padding:16px}
|
||||
h2{color:#00bcd4}.card{background:#1a1f21;border-radius:8px;padding:12px;margin:8px 0}
|
||||
button{background:#00bcd4;color:#000;padding:8px 16px;border:none;border-radius:4px;cursor:pointer}
|
||||
.counter{font-size:48px;color:#4caf50;text-align:center;padding:20px}
|
||||
.log{background:#000;color:#0f0;padding:8px;border-radius:4px;max-height:200px;overflow-y:auto;font-size:12px}
|
||||
</style></head><body>
|
||||
<h2>Example Plugin Panel</h2>
|
||||
<div class="card"><h3>Counter</h3><div class="counter" id="count">0</div>
|
||||
<button onclick="inc()">+1</button> <button onclick="reset()">Reset</button></div>
|
||||
<div class="card"><h3>Events (SSE)</h3><div class="log" id="log">Waiting...</div></div>
|
||||
<script>let n=0;function inc(){n++;document.getElementById("count").textContent=n;fetch("/SenSu/plugin/example_plugin/event",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({type:"counter",value:n})})}function reset(){n=0;document.getElementById("count").textContent=0}const es=new EventSource("/SenSu/plugin/example_plugin/sse");es.addEventListener("counter",e=>{const d=JSON.parse(e.data);document.getElementById("log").innerHTML+=new Date().toLocaleTimeString()+" counter="+d.value+"<br>"})</script></body></html>
|
||||
Reference in New Issue
Block a user