You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
3.1 KiB
144 lines
3.1 KiB
package handler
|
|
|
|
import (
|
|
"embed"
|
|
"encoding/json"
|
|
"io/fs"
|
|
"net/http"
|
|
|
|
"teraclone/internal/service"
|
|
|
|
"github.com/gorilla/websocket"
|
|
)
|
|
|
|
//go:embed web/*
|
|
var webFiles embed.FS
|
|
|
|
type AppHandler struct {
|
|
appService *service.AppService
|
|
fileServer http.Handler
|
|
}
|
|
|
|
type socketRequest struct {
|
|
Type string `json:"type"`
|
|
Data string `json:"data"`
|
|
}
|
|
|
|
type socketResponse struct {
|
|
Type string `json:"type"`
|
|
Data string `json:"data,omitempty"`
|
|
Prompt string `json:"prompt,omitempty"`
|
|
WorkingDir string `json:"workingDir,omitempty"`
|
|
}
|
|
|
|
var upgrader = websocket.Upgrader{
|
|
CheckOrigin: func(r *http.Request) bool {
|
|
return true
|
|
},
|
|
}
|
|
|
|
func NewAppHandler(appService *service.AppService) *AppHandler {
|
|
webRoot, err := fs.Sub(webFiles, "web")
|
|
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return &AppHandler{
|
|
appService: appService,
|
|
fileServer: http.FileServer(http.FS(webRoot)),
|
|
}
|
|
}
|
|
|
|
func (h *AppHandler) RegisterRoutes(mux *http.ServeMux) {
|
|
mux.HandleFunc("/", h.handleRoot)
|
|
mux.HandleFunc("/health", h.handleHealth)
|
|
mux.HandleFunc("/ws", h.handleWebSocket)
|
|
mux.Handle("/assets/", http.StripPrefix("/assets/", h.fileServer))
|
|
}
|
|
|
|
func (h *AppHandler) RegisterLegacyRoutes(mux *http.ServeMux) {
|
|
mux.HandleFunc("/legacy", h.handleLegacyRoot)
|
|
mux.HandleFunc("/legacy/", h.handleLegacyRoot)
|
|
mux.HandleFunc("/legacy/ws", h.handleLegacyWebSocket)
|
|
mux.Handle("/legacy/assets/", http.StripPrefix("/legacy/assets/", h.fileServer))
|
|
}
|
|
|
|
func (h *AppHandler) HandleHealth(w http.ResponseWriter, r *http.Request) {
|
|
h.handleHealth(w, r)
|
|
}
|
|
|
|
func (h *AppHandler) handleRoot(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/" {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
|
|
http.ServeFileFS(w, r, webFiles, "web/index.html")
|
|
}
|
|
|
|
func (h *AppHandler) handleHealth(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
response := map[string]string{
|
|
"status": h.appService.HealthStatus(),
|
|
}
|
|
|
|
_ = json.NewEncoder(w).Encode(response)
|
|
}
|
|
|
|
func (h *AppHandler) handleLegacyRoot(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/legacy" && r.URL.Path != "/legacy/" {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
|
|
http.ServeFileFS(w, r, webFiles, "web/index.html")
|
|
}
|
|
|
|
func (h *AppHandler) handleLegacyWebSocket(w http.ResponseWriter, r *http.Request) {
|
|
h.handleWebSocket(w, r)
|
|
}
|
|
|
|
func (h *AppHandler) handleWebSocket(w http.ResponseWriter, r *http.Request) {
|
|
conn, err := upgrader.Upgrade(w, r, nil)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
|
|
session, err := h.appService.NewTerminalSession()
|
|
if err != nil {
|
|
_ = conn.WriteJSON(socketResponse{
|
|
Type: "error",
|
|
Data: err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
_ = conn.WriteJSON(socketResponse{
|
|
Type: "welcome",
|
|
Data: session.WelcomeMessage(),
|
|
Prompt: session.Prompt(),
|
|
WorkingDir: session.Snapshot().WorkingDir,
|
|
})
|
|
|
|
for {
|
|
var request socketRequest
|
|
if err := conn.ReadJSON(&request); err != nil {
|
|
return
|
|
}
|
|
|
|
if request.Type != "input" {
|
|
continue
|
|
}
|
|
|
|
result := session.Execute(request.Data)
|
|
_ = conn.WriteJSON(socketResponse{
|
|
Type: "output",
|
|
Data: result.Output,
|
|
Prompt: result.Prompt,
|
|
WorkingDir: result.WorkingDir,
|
|
})
|
|
}
|
|
}
|
|
|