mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-22 18:08:04 +00:00
device gateway: remotedevice 插件(设备接入网关) + GUI/waiter 受控设备桥 + 设备页/授权开关/托盘/退出进托盘 + 白屏修复(惰性Tray) + deviceinfo 工具
This commit is contained in:
@ -611,6 +611,8 @@ func (h *Handler) RegisterRoutes(mux *http.ServeMux) {
|
||||
mux.HandleFunc("/api/v1/kernel", h.requireAPI(h.handleKernel))
|
||||
mux.HandleFunc("/api/v1/plugins", h.requireAPI(h.handlePlugins))
|
||||
mux.HandleFunc("/api/v1/plugins/", h.requireAPI(h.handlePluginByID))
|
||||
// 设备网关(可配置反代到 remotedevice;默认禁用,未启用时返回 404)
|
||||
mux.HandleFunc("/api/v1/device/", h.requireAPI(h.handleDeviceGatewayProxy))
|
||||
mux.HandleFunc("/v1/chat/completions", h.requireAPI(h.handleOpenAICompletions))
|
||||
mux.HandleFunc("/", h.requireWeb(h.handleStatic))
|
||||
}
|
||||
@ -1663,6 +1665,55 @@ func (h *Handler) handleTracker(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// ======== Remote Device Gateway (proxied to remotedevice, opt-in) ========
|
||||
|
||||
// deviceGatewayEnabled / deviceGatewayAddr 由 webui 插件启动时从设置读取并注入。
|
||||
// 默认禁用:用户显式配置 device_gateway_enabled=true 后,/api/v1/device/* 才会反代到
|
||||
// remotedevice 插件(self-contained),避免与 remotedevice 耦合。
|
||||
var (
|
||||
deviceGatewayEnabled bool
|
||||
deviceGatewayAddr string
|
||||
deviceGatewayToken string
|
||||
)
|
||||
|
||||
// handleDeviceGatewayProxy 将 /api/v1/device/* 反代到 remotedevice 内部 HTTP 服务。
|
||||
// 鉴权:本端走 requireAPI(webui API key),转发时带 remotedevice 的 token(X-API-Key)。
|
||||
func (h *Handler) handleDeviceGatewayProxy(w http.ResponseWriter, r *http.Request) {
|
||||
if !deviceGatewayEnabled {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
addr := deviceGatewayAddr
|
||||
if addr == "" {
|
||||
addr = "127.0.0.1:9890"
|
||||
}
|
||||
path := r.URL.Path // 保留 /api/v1/device/... 全路径
|
||||
url := "http://" + addr + path
|
||||
if r.URL.RawQuery != "" {
|
||||
url += "?" + r.URL.RawQuery
|
||||
}
|
||||
req, err := http.NewRequestWithContext(r.Context(), r.Method, url, r.Body)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
req.Header = r.Header.Clone()
|
||||
if deviceGatewayToken != "" {
|
||||
req.Header.Set("X-API-Key", deviceGatewayToken)
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadGateway, map[string]string{"error": "device gateway unreachable: " + err.Error()})
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
for k, v := range resp.Header {
|
||||
w.Header()[k] = v
|
||||
}
|
||||
w.WriteHeader(resp.StatusCode)
|
||||
io.Copy(w, resp.Body)
|
||||
}
|
||||
|
||||
// ======== Plugin Management (proxied to pluginmgr HTTP API) ========
|
||||
|
||||
func (h *Handler) pluginmgrAddr() string {
|
||||
|
||||
@ -704,7 +704,7 @@ func TestSettingsWithPluginRegistry(t *testing.T) {
|
||||
|
||||
type echoProvider struct{ name string }
|
||||
|
||||
func (p *echoProvider) Name() string { return p.name }
|
||||
func (p *echoProvider) Name() string { return p.name }
|
||||
func (p *echoProvider) MaxContextTokens() int { return 8192 }
|
||||
func (p *echoProvider) Chat(ctx context.Context, req *agentAPI.CompletionRequest) (*agentAPI.CompletionResponse, error) {
|
||||
content := "echo: " + lastUserContent(req.Messages)
|
||||
|
||||
@ -95,7 +95,26 @@ func (p *Plugin) Start(s *sdk.PluginSDK) error {
|
||||
s.Settings().RegisterDef(sdk.ConfigDef{Key: "username", Default: "admin", Type: "string", DisplayName: "登录用户名", Description: "Web 控制台登录用户名", Category: "webui"})
|
||||
s.Settings().RegisterDef(sdk.ConfigDef{Key: "password", Default: "", Type: "password", DisplayName: "Web 控制台登录密码", Description: "Web 控制台登录密码", Category: "webui"})
|
||||
s.Settings().RegisterDef(sdk.ConfigDef{Key: "session_ttl_hours", Default: "24", Type: "int", DisplayName: "会话时长(小时)", Description: "登录 cookie 有效时长", Category: "webui"})
|
||||
s.Settings().RegisterDef(sdk.ConfigDef{Key: "device_gateway_enabled", Default: "false", Type: "bool", DisplayName: "设备网关反代", Description: "启用后 /api/v1/device/* 反代到 remotedevice 插件(默认关闭,避免硬耦合)", Category: "webui"})
|
||||
s.Settings().RegisterDef(sdk.ConfigDef{Key: "device_gateway_addr", Default: "127.0.0.1:9890", Type: "string", DisplayName: "设备网关地址", Description: "remotedevice 插件的内部监听地址", Category: "webui"})
|
||||
s.Settings().RegisterDef(sdk.ConfigDef{Key: "device_gateway_token", Default: "", Type: "password", DisplayName: "设备网关令牌", Description: "访问 remotedevice 的 token(与 remotedevice 的 ws_token 一致)", Category: "webui"})
|
||||
p.ensureAuthBootstrap(s)
|
||||
// 注入设备网关反代配置(默认禁用;仅当用户开启时才挂载路由)
|
||||
if v, _ := s.Settings().Get("device_gateway_enabled"); v != nil {
|
||||
if s2, ok := v.(string); ok && s2 == "true" {
|
||||
deviceGatewayEnabled = true
|
||||
}
|
||||
}
|
||||
if v, _ := s.Settings().Get("device_gateway_addr"); v != nil {
|
||||
if s2, ok := v.(string); ok && s2 != "" {
|
||||
deviceGatewayAddr = s2
|
||||
}
|
||||
}
|
||||
if v, _ := s.Settings().Get("device_gateway_token"); v != nil {
|
||||
if s2, ok := v.(string); ok && s2 != "" {
|
||||
deviceGatewayToken = s2
|
||||
}
|
||||
}
|
||||
|
||||
s.RegisterStage(sdk.StagePreAction, func(ctx *sdk.StageContext) error {
|
||||
s.Publish(&sdk.Event{Type: sdk.EventStage, Payload: map[string]interface{}{"phase": "pre_action", "message": "thinking"}})
|
||||
|
||||
Reference in New Issue
Block a user