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.

145 lines
3.1 KiB

4 weeks ago
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,
})
}
}