package api import ( "encoding/json" "net/http" "github.com/go-chi/chi/v5" "github.com/kindredsystems/silo/internal/auth" "github.com/kindredsystems/silo/internal/db" ) // HandleRegisterWorkstation registers or re-registers a workstation for the current user. func (s *Server) HandleRegisterWorkstation(w http.ResponseWriter, r *http.Request) { ctx := r.Context() user := auth.UserFromContext(ctx) if user == nil { writeError(w, http.StatusUnauthorized, "unauthorized", "Authentication required") return } var req struct { Name string `json:"name"` Hostname string `json:"hostname"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid_json", err.Error()) return } if req.Name == "" { writeError(w, http.StatusBadRequest, "validation_error", "name is required") return } ws := &db.Workstation{ Name: req.Name, UserID: user.ID, Hostname: req.Hostname, } if err := s.workstations.Upsert(ctx, ws); err != nil { s.logger.Error().Err(err).Str("name", req.Name).Msg("failed to register workstation") writeError(w, http.StatusInternalServerError, "internal_error", "Failed to register workstation") return } s.broker.Publish("workstation.registered", mustMarshal(map[string]any{ "id": ws.ID, "name": ws.Name, "user_id": ws.UserID, "hostname": ws.Hostname, })) writeJSON(w, http.StatusOK, map[string]any{ "id": ws.ID, "name": ws.Name, "hostname": ws.Hostname, "last_seen": ws.LastSeen.UTC().Format("2006-01-02T15:04:05Z"), "created_at": ws.CreatedAt.UTC().Format("2006-01-02T15:04:05Z"), }) } // HandleListWorkstations returns all workstations for the current user. func (s *Server) HandleListWorkstations(w http.ResponseWriter, r *http.Request) { ctx := r.Context() user := auth.UserFromContext(ctx) if user == nil { writeError(w, http.StatusUnauthorized, "unauthorized", "Authentication required") return } workstations, err := s.workstations.ListByUser(ctx, user.ID) if err != nil { s.logger.Error().Err(err).Msg("failed to list workstations") writeError(w, http.StatusInternalServerError, "internal_error", "Failed to list workstations") return } type wsResponse struct { ID string `json:"id"` Name string `json:"name"` Hostname string `json:"hostname"` LastSeen string `json:"last_seen"` CreatedAt string `json:"created_at"` } out := make([]wsResponse, len(workstations)) for i, ws := range workstations { out[i] = wsResponse{ ID: ws.ID, Name: ws.Name, Hostname: ws.Hostname, LastSeen: ws.LastSeen.UTC().Format("2006-01-02T15:04:05Z"), CreatedAt: ws.CreatedAt.UTC().Format("2006-01-02T15:04:05Z"), } } writeJSON(w, http.StatusOK, out) } // HandleDeleteWorkstation removes a workstation owned by the current user (or any, for admins). func (s *Server) HandleDeleteWorkstation(w http.ResponseWriter, r *http.Request) { ctx := r.Context() user := auth.UserFromContext(ctx) if user == nil { writeError(w, http.StatusUnauthorized, "unauthorized", "Authentication required") return } id := chi.URLParam(r, "id") ws, err := s.workstations.GetByID(ctx, id) if err != nil { s.logger.Error().Err(err).Str("id", id).Msg("failed to get workstation") writeError(w, http.StatusInternalServerError, "internal_error", "Failed to get workstation") return } if ws == nil { writeError(w, http.StatusNotFound, "not_found", "Workstation not found") return } if ws.UserID != user.ID && user.Role != auth.RoleAdmin { writeError(w, http.StatusForbidden, "forbidden", "You can only delete your own workstations") return } if err := s.workstations.Delete(ctx, id); err != nil { s.logger.Error().Err(err).Str("id", id).Msg("failed to delete workstation") writeError(w, http.StatusInternalServerError, "internal_error", "Failed to delete workstation") return } s.broker.Publish("workstation.removed", mustMarshal(map[string]any{ "id": ws.ID, "name": ws.Name, "user_id": ws.UserID, })) w.WriteHeader(http.StatusNoContent) }