Files
deepseek-web/src/admin_server.py
T

195 lines
7.5 KiB
Python

"""
DeepSeek Web Adapter 控制面板
提供 Web UI 管理:配置、Worker 注册、浏览器控制、日志查看
"""
import asyncio
import json
import logging
import threading
from pathlib import Path
from typing import Optional
from flask import Flask, render_template, request, jsonify
from flask_socketio import SocketIO
from .config import config, DeepSeekWebConfig, CONFIG_FILE
logger = logging.getLogger(__name__)
TEMPLATE_DIR = str(Path(__file__).parent.parent / "templates")
class AdminServer:
"""控制面板 HTTP 服务"""
def __init__(self, adapter=None):
self.adapter = adapter
self.app = Flask(__name__, template_folder=TEMPLATE_DIR, static_folder=None)
self.socketio = SocketIO(self.app, cors_allowed_origins="*", async_mode="threading")
self._adapter_loop = None
self._setup_routes()
def _setup_routes(self):
@self.app.route("/")
def index():
return render_template("admin.html")
@self.app.route("/api/config", methods=["GET"])
def get_config():
from dataclasses import asdict
return jsonify(asdict(config))
@self.app.route("/api/config", methods=["POST"])
def save_config():
try:
data = request.json
for key, value in data.items():
if hasattr(config, key):
setattr(config, key, value)
config.save()
return jsonify({"success": True})
except Exception as e:
return jsonify({"success": False, "error": str(e)})
@self.app.route("/api/register", methods=["POST"])
def register_worker():
try:
data = request.json
import urllib.request
import ssl
base_url = config.array_base_url.rstrip("/")
url = f"{base_url}/api/v1/workers/register"
reg_data = {
"name": data.get("name", "DeepSeek-Web"),
"type": data.get("type", "ai"),
"description": data.get("description", ""),
"invite_code": data.get("invite_code") or None,
"capabilities": config.profile.get("capabilities", {}),
"organization": "Array",
"timezone": "Asia/Shanghai",
"tags": ["AI", "DeepSeek", "Agent", "Web"],
"max_concurrent_tasks": 1,
}
req = urllib.request.Request(
url,
data=json.dumps(reg_data).encode("utf-8"),
headers={"Content-Type": "application/json"},
method="POST",
)
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
try:
with urllib.request.urlopen(req, context=ctx, timeout=10) as resp:
result = json.loads(resp.read().decode("utf-8"))
except urllib.error.HTTPError as e:
error_body = e.read().decode("utf-8", errors="replace")
return jsonify({"error": f"HTTP {e.code}: {error_body}"})
worker_id = result.get("worker", {}).get("id", "")
api_key = result.get("api_key", "")
if api_key:
config.api_key = api_key
config.worker_id = worker_id
config.worker_name = data.get("name", config.worker_name)
config.save()
return jsonify({"worker_id": worker_id, "api_key": api_key})
except Exception as e:
return jsonify({"error": str(e)})
@self.app.route("/api/status")
def get_status():
if self.adapter:
status = self.adapter.get_status()
status["worker_name"] = config.worker_name
return jsonify(status)
return jsonify({
"connected": False, "deepseek_ready": False,
"processing": False, "current_task": None,
"worker_id": None, "worker_name": config.worker_name,
})
@self.app.route("/api/browser/start", methods=["POST"])
def start_browser():
if not self.adapter or not self._adapter_loop:
return jsonify({"success": False, "error": "Not ready"})
future = asyncio.run_coroutine_threadsafe(
self.adapter.start_deepseek_session(), self._adapter_loop
)
try:
result = future.result(timeout=30)
return jsonify({"success": result})
except Exception as e:
return jsonify({"success": False, "error": str(e)})
@self.app.route("/api/browser/stop", methods=["POST"])
def stop_browser():
if not self.adapter or not self._adapter_loop:
return jsonify({"success": False, "error": "Not ready"})
future = asyncio.run_coroutine_threadsafe(
self.adapter.stop_deepseek_session(), self._adapter_loop
)
try:
future.result(timeout=10)
return jsonify({"success": True})
except Exception as e:
return jsonify({"success": False, "error": str(e)})
@self.app.route("/api/browser/reinit", methods=["POST"])
def reinit_prompt():
if not self.adapter or not self.adapter.session or not self._adapter_loop:
return jsonify({"success": False, "error": "DeepSeek session not started"})
from .deepseek_agent_adapter import ADAPTER_SYSTEM_PROMPT
async def _reinit():
await self.adapter.session.controller.send(
f"[系统重新初始化]\n\n{ADAPTER_SYSTEM_PROMPT}"
)
return True
future = asyncio.run_coroutine_threadsafe(_reinit(), self._adapter_loop)
try:
future.result(timeout=15)
return jsonify({"success": True})
except Exception as e:
return jsonify({"success": False, "error": str(e)})
@self.app.route("/api/logs/deepseek")
def get_deepseek_logs():
if self.adapter and self.adapter.session:
return jsonify({"logs": self.adapter.session.get_logs(100)})
return jsonify({"logs": None})
@self.app.route("/api/logs/deepseek", methods=["DELETE"])
def clear_deepseek_logs():
if self.adapter and self.adapter.session:
self.adapter.session._log_buffer.clear()
return jsonify({"success": True})
@self.app.route("/api/logs/system")
def get_system_logs():
log_file = Path("adapter_debug.log")
if log_file.exists():
try:
content = log_file.read_text(encoding="utf-8", errors="replace")
lines = content.split("\n")
return jsonify({"logs": "\n".join(lines[-200:])})
except:
pass
return jsonify({"logs": None})
def set_adapter(self, adapter, loop):
self.adapter = adapter
self._adapter_loop = loop
def run(self, host="0.0.0.0", port=None):
if port is None:
port = config.admin_port
logger.info(f"控制面板启动: http://{host}:{port}")
self.socketio.run(self.app, host=host, port=port, debug=False, allow_unsafe_werkzeug=True)