293 lines
7.4 KiB
Go
293 lines
7.4 KiB
Go
package web
|
|
|
|
import (
|
|
"embed"
|
|
"encoding/json"
|
|
"io/fs"
|
|
"log"
|
|
"net/http"
|
|
|
|
"github.com/duffy/usb-server/internal/config"
|
|
"github.com/duffy/usb-server/internal/token"
|
|
)
|
|
|
|
//go:embed static
|
|
var staticFiles embed.FS
|
|
|
|
// Handler provides the web UI and API
|
|
type Handler struct {
|
|
cfg *config.Config
|
|
cfgPath string
|
|
mux *http.ServeMux
|
|
|
|
// Callbacks for device operations
|
|
GetDevices func() interface{}
|
|
AttachDevice func(clientID, busID string) error
|
|
DetachDevice func(clientID, busID string) error
|
|
InstallService func() error
|
|
UninstallService func() error
|
|
GetStatus func() map[string]interface{}
|
|
}
|
|
|
|
// NewHandler creates a new web handler
|
|
func NewHandler(cfg *config.Config, cfgPath string) *Handler {
|
|
h := &Handler{
|
|
cfg: cfg,
|
|
cfgPath: cfgPath,
|
|
mux: http.NewServeMux(),
|
|
}
|
|
h.setupRoutes()
|
|
return h
|
|
}
|
|
|
|
func (h *Handler) setupRoutes() {
|
|
// Static files
|
|
staticFS, _ := fs.Sub(staticFiles, "static")
|
|
h.mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticFS))))
|
|
h.mux.HandleFunc("/", h.handleIndex)
|
|
|
|
// API endpoints
|
|
h.mux.HandleFunc("/api/status", h.handleStatus)
|
|
h.mux.HandleFunc("/api/devices", h.handleDevices)
|
|
h.mux.HandleFunc("/api/attach", h.handleAttach)
|
|
h.mux.HandleFunc("/api/detach", h.handleDetach)
|
|
h.mux.HandleFunc("/api/config", h.handleConfig)
|
|
h.mux.HandleFunc("/api/generate-token", h.handleGenerateToken)
|
|
h.mux.HandleFunc("/api/apply-tokens", h.handleApplyTokens)
|
|
h.mux.HandleFunc("/api/service/install", h.handleServiceInstall)
|
|
h.mux.HandleFunc("/api/service/uninstall", h.handleServiceUninstall)
|
|
}
|
|
|
|
// ServeHTTP implements http.Handler
|
|
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
h.mux.ServeHTTP(w, r)
|
|
}
|
|
|
|
func (h *Handler) handleIndex(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/" {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
data, err := staticFiles.ReadFile("static/index.html")
|
|
if err != nil {
|
|
http.Error(w, "Internal error", 500)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
w.Write(data)
|
|
}
|
|
|
|
func (h *Handler) handleStatus(w http.ResponseWriter, r *http.Request) {
|
|
status := map[string]interface{}{
|
|
"connected": false,
|
|
"mode": h.cfg.Mode,
|
|
"name": h.cfg.Name,
|
|
}
|
|
if h.GetStatus != nil {
|
|
status = h.GetStatus()
|
|
}
|
|
writeJSON(w, status)
|
|
}
|
|
|
|
func (h *Handler) handleDevices(w http.ResponseWriter, r *http.Request) {
|
|
if h.GetDevices == nil {
|
|
writeJSON(w, map[string]interface{}{"mode": h.cfg.Mode})
|
|
return
|
|
}
|
|
writeJSON(w, h.GetDevices())
|
|
}
|
|
|
|
func (h *Handler) handleAttach(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != "POST" {
|
|
http.Error(w, "Method not allowed", 405)
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
ClientID string `json:"client_id"`
|
|
BusID string `json:"bus_id"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeJSON(w, map[string]interface{}{"ok": false, "error": "invalid request"})
|
|
return
|
|
}
|
|
|
|
if h.AttachDevice == nil {
|
|
writeJSON(w, map[string]interface{}{"ok": false, "error": "not in use mode"})
|
|
return
|
|
}
|
|
|
|
if err := h.AttachDevice(req.ClientID, req.BusID); err != nil {
|
|
writeJSON(w, map[string]interface{}{"ok": false, "error": err.Error()})
|
|
return
|
|
}
|
|
|
|
writeJSON(w, map[string]interface{}{"ok": true})
|
|
}
|
|
|
|
func (h *Handler) handleDetach(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != "POST" {
|
|
http.Error(w, "Method not allowed", 405)
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
ClientID string `json:"client_id"`
|
|
BusID string `json:"bus_id"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeJSON(w, map[string]interface{}{"ok": false, "error": "invalid request"})
|
|
return
|
|
}
|
|
|
|
if h.DetachDevice == nil {
|
|
writeJSON(w, map[string]interface{}{"ok": false, "error": "not in use mode"})
|
|
return
|
|
}
|
|
|
|
if err := h.DetachDevice(req.ClientID, req.BusID); err != nil {
|
|
writeJSON(w, map[string]interface{}{"ok": false, "error": err.Error()})
|
|
return
|
|
}
|
|
|
|
writeJSON(w, map[string]interface{}{"ok": true})
|
|
}
|
|
|
|
func (h *Handler) handleConfig(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method == "GET" {
|
|
writeJSON(w, h.cfg)
|
|
return
|
|
}
|
|
|
|
if r.Method == "POST" {
|
|
var updates struct {
|
|
RelayAddr string `json:"relay_addr"`
|
|
Mode string `json:"mode"`
|
|
Name string `json:"name"`
|
|
WebPort int `json:"web_port"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&updates); err != nil {
|
|
writeJSON(w, map[string]interface{}{"ok": false, "error": "invalid request"})
|
|
return
|
|
}
|
|
|
|
if updates.RelayAddr != "" {
|
|
h.cfg.RelayAddr = updates.RelayAddr
|
|
}
|
|
if updates.Mode == "share" || updates.Mode == "use" {
|
|
h.cfg.Mode = updates.Mode
|
|
}
|
|
if updates.Name != "" {
|
|
h.cfg.Name = updates.Name
|
|
}
|
|
if updates.WebPort > 0 {
|
|
h.cfg.WebPort = updates.WebPort
|
|
}
|
|
|
|
if err := h.cfg.Save(h.cfgPath); err != nil {
|
|
writeJSON(w, map[string]interface{}{"ok": false, "error": err.Error()})
|
|
return
|
|
}
|
|
|
|
writeJSON(w, map[string]interface{}{"ok": true})
|
|
return
|
|
}
|
|
|
|
http.Error(w, "Method not allowed", 405)
|
|
}
|
|
|
|
func (h *Handler) handleGenerateToken(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != "POST" {
|
|
http.Error(w, "Method not allowed", 405)
|
|
return
|
|
}
|
|
|
|
tokens, err := token.Generate()
|
|
if err != nil {
|
|
writeJSON(w, map[string]interface{}{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
hash := tokens.Hash()
|
|
|
|
// Save tokens to config
|
|
h.cfg.Token1 = tokens.Token1
|
|
h.cfg.Token2 = tokens.Token2
|
|
h.cfg.Token3 = tokens.Token3
|
|
h.cfg.Hash = hash
|
|
h.cfg.Save(h.cfgPath)
|
|
|
|
writeJSON(w, map[string]interface{}{
|
|
"token1": tokens.Token1,
|
|
"token2": tokens.Token2,
|
|
"token3": tokens.Token3,
|
|
"hash": hash,
|
|
})
|
|
}
|
|
|
|
func (h *Handler) handleApplyTokens(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != "POST" {
|
|
http.Error(w, "Method not allowed", 405)
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Token1 string `json:"token1"`
|
|
Token2 string `json:"token2"`
|
|
Token3 string `json:"token3"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
writeJSON(w, map[string]interface{}{"ok": false, "error": "invalid request"})
|
|
return
|
|
}
|
|
|
|
hash := token.HashFromTokens(req.Token1, req.Token2, req.Token3)
|
|
|
|
h.cfg.Token1 = req.Token1
|
|
h.cfg.Token2 = req.Token2
|
|
h.cfg.Token3 = req.Token3
|
|
h.cfg.Hash = hash
|
|
h.cfg.Save(h.cfgPath)
|
|
|
|
writeJSON(w, map[string]interface{}{"ok": true, "hash": hash})
|
|
}
|
|
|
|
func (h *Handler) handleServiceInstall(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != "POST" {
|
|
http.Error(w, "Method not allowed", 405)
|
|
return
|
|
}
|
|
if h.InstallService == nil {
|
|
writeJSON(w, map[string]interface{}{"error": "service management not available"})
|
|
return
|
|
}
|
|
if err := h.InstallService(); err != nil {
|
|
writeJSON(w, map[string]interface{}{"error": err.Error()})
|
|
return
|
|
}
|
|
writeJSON(w, map[string]interface{}{"message": "Service installiert und gestartet"})
|
|
}
|
|
|
|
func (h *Handler) handleServiceUninstall(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != "POST" {
|
|
http.Error(w, "Method not allowed", 405)
|
|
return
|
|
}
|
|
if h.UninstallService == nil {
|
|
writeJSON(w, map[string]interface{}{"error": "service management not available"})
|
|
return
|
|
}
|
|
if err := h.UninstallService(); err != nil {
|
|
writeJSON(w, map[string]interface{}{"error": err.Error()})
|
|
return
|
|
}
|
|
writeJSON(w, map[string]interface{}{"message": "Service deinstalliert"})
|
|
}
|
|
|
|
func writeJSON(w http.ResponseWriter, v interface{}) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
if err := json.NewEncoder(w).Encode(v); err != nil {
|
|
log.Printf("[web] JSON encode error: %v", err)
|
|
}
|
|
}
|