package server import ( "bytes" "html/template" "log" "net/http" "github.com/x-grk/longpolling-remote-access/internal/proto" ) // A panel page is either a fragment - its content alone, for the shell to put // in place without reloading - or the shell with that content already in it. func isFrag(r *http.Request) bool { return r.URL.Query().Get("frag") == "1" } // pageView is the shell's view a page is shown in; the machine pages (Порты, // Статус, Изменить) open as a dialog over the machines view. var pageView = map[string]string{"dashboard": "dashboard", "settings": "settings"} func (s *Server) renderPage(w http.ResponseWriter, r *http.Request, name string, data map[string]any) { w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("Cache-Control", "no-store") if data == nil { data = map[string]any{} } data["IsAdmin"] = s.currentAdmin(r).IsAdmin() if !isFrag(r) { if _, ok := pageView[name]; !ok { // A machine page asked for whole: the machines view with it in // a dialog. s.shell(w, r, map[string]any{"View": "dashboard", "Dialog": r.URL.Path}) return } } var body bytes.Buffer if err := s.tmpl.ExecuteTemplate(&body, name+"-body", data); err != nil { log.Printf("template %s: %v", name, err) http.Error(w, "internal error", http.StatusInternalServerError) return } if isFrag(r) { _, _ = w.Write(body.Bytes()) return } data["View"] = pageView[name] data["Body"] = template.HTML(body.String()) s.shell(w, r, data) } // shell writes the shell page with data (View, Body, Open, Dialog). func (s *Server) shell(w http.ResponseWriter, r *http.Request, data map[string]any) { data["IsAdmin"] = s.currentAdmin(r).IsAdmin() data["EditLimit"] = proto.FSReadCap if a := s.currentAdmin(r); a != nil { data["Prefs"] = s.prefs(a.ID) } if data["View"] == "dashboard" && data["Body"] == nil { // The machines list under a dialog: draw it too. var body bytes.Buffer if err := s.tmpl.ExecuteTemplate(&body, "dashboard-body", s.dashboardData(r)); err == nil { data["Body"] = template.HTML(body.String()) } } w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("Cache-Control", "no-store") if err := s.tmpl.ExecuteTemplate(w, "app.html", data); err != nil { log.Printf("template app.html: %v", err) } } // handleShell serves the shell alone: the terminal view, or with a pane of // a machine opened at once (/agents/{id}/terminal, /agents/{id}/files). func (s *Server) handleShell(w http.ResponseWriter, r *http.Request) { data := map[string]any{"View": "terminal"} if id := r.PathValue("id"); id != "" { a, _ := s.db.GetAgent(id) if a == nil { http.NotFound(w, r) return } kind := "term" switch r.PathValue("kind") { case "", "terminal": case "files": kind = "files" default: http.NotFound(w, r) return } data["Open"] = map[string]any{"kind": kind, "agent": map[string]string{"id": a.ID, "name": a.Name, "host": a.Hostname, "user": a.TerminalUser()}} } s.shell(w, r, data) }