mirror of
https://github.com/jwetzell/showbridge-go.git
synced 2026-04-26 12:55:29 +00:00
154 lines
4.4 KiB
Go
154 lines
4.4 KiB
Go
package showbridge
|
|
|
|
import (
|
|
"context"
|
|
"embed"
|
|
_ "embed"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/jwetzell/showbridge-go/internal/config"
|
|
"github.com/jwetzell/showbridge-go/internal/module"
|
|
"github.com/jwetzell/showbridge-go/internal/route"
|
|
)
|
|
|
|
func (r *Router) startAPIServer(config config.ApiConfig) {
|
|
if !config.Enabled {
|
|
r.logger.Warn("API not enabled")
|
|
return
|
|
}
|
|
r.logger.Debug("starting API server", "port", config.Port)
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/ws", r.handleWebsocket)
|
|
mux.HandleFunc("/health", r.handleHealthHTTP)
|
|
mux.HandleFunc("/schema/{schema}", r.handleSchemaHTTP)
|
|
mux.HandleFunc("/api/v1/config", r.handleConfigHTTP)
|
|
|
|
r.apiServerMu.Lock()
|
|
defer r.apiServerMu.Unlock()
|
|
r.apiServer = &http.Server{
|
|
Addr: fmt.Sprintf(":%d", config.Port),
|
|
Handler: mux,
|
|
}
|
|
|
|
go func() {
|
|
r.apiServer.ListenAndServe()
|
|
r.apiServerShutdown()
|
|
}()
|
|
}
|
|
|
|
func (r *Router) stopAPIServer() {
|
|
if r.apiServer == nil {
|
|
return
|
|
}
|
|
r.logger.Debug("stopping API server")
|
|
r.apiServerMu.Lock()
|
|
defer r.apiServerMu.Unlock()
|
|
if r.apiServer != nil {
|
|
apiShutdownCtx, apiShutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
r.apiServerShutdown = apiShutdownCancel
|
|
r.apiServer.Shutdown(apiShutdownCtx)
|
|
<-apiShutdownCtx.Done()
|
|
r.apiServer = nil
|
|
}
|
|
}
|
|
|
|
func (r *Router) handleHealthHTTP(w http.ResponseWriter, req *http.Request) {
|
|
switch req.Method {
|
|
case http.MethodGet:
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
w.WriteHeader(http.StatusOK)
|
|
case http.MethodOptions:
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
|
|
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
|
w.WriteHeader(http.StatusOK)
|
|
default:
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
func (r *Router) handleConfigHTTP(w http.ResponseWriter, req *http.Request) {
|
|
|
|
switch req.Method {
|
|
case http.MethodGet:
|
|
configJSON, err := json.Marshal(r.runningConfig)
|
|
if err != nil {
|
|
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Write(configJSON)
|
|
case http.MethodPut:
|
|
var newConfig config.Config
|
|
err := json.NewDecoder(req.Body).Decode(&newConfig)
|
|
if err != nil {
|
|
http.Error(w, "Bad request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
moduleErrors, routeErrors := r.UpdateConfig(newConfig)
|
|
if len(moduleErrors) > 0 || len(routeErrors) > 0 {
|
|
errorResponse := struct {
|
|
ModuleErrors []module.ModuleError `json:"moduleErrors,omitempty"`
|
|
RouteErrors []route.RouteError `json:"routeErrors,omitempty"`
|
|
}{
|
|
ModuleErrors: moduleErrors,
|
|
RouteErrors: routeErrors,
|
|
}
|
|
errorResponseJSON, err := json.Marshal(errorResponse)
|
|
if err != nil {
|
|
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
w.Write(errorResponseJSON)
|
|
return
|
|
}
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
w.WriteHeader(http.StatusOK)
|
|
r.ConfigChange <- newConfig
|
|
case http.MethodOptions:
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
w.Header().Set("Access-Control-Allow-Methods", "GET, PUT, OPTIONS")
|
|
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
|
w.WriteHeader(http.StatusOK)
|
|
default:
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
}
|
|
|
|
}
|
|
|
|
//go:embed schema
|
|
var schema embed.FS
|
|
|
|
func (r *Router) handleSchemaHTTP(w http.ResponseWriter, req *http.Request) {
|
|
switch req.Method {
|
|
case http.MethodGet:
|
|
schemaName := req.PathValue("schema")
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
w.Header().Set("Content-Type", "application/json")
|
|
configSchema, err := schema.ReadFile(fmt.Sprintf("schema/%s.schema.json", schemaName))
|
|
if err != nil {
|
|
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.Write(configSchema)
|
|
case http.MethodOptions:
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
|
|
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
|
w.WriteHeader(http.StatusOK)
|
|
default:
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
}
|
|
}
|