package server import ( "context" "encoding/json" "fmt" "io" "log/slog" "net/http" "level3.git.onstackit.cloud/LEVEL3/gabriel/src/api" "level3.git.onstackit.cloud/LEVEL3/gabriel/src/config" "level3.git.onstackit.cloud/LEVEL3/gabriel/src/search" ) type PagedbService interface { Create(ctx context.Context, instanceId, owner string) (api.Instance, error) Get(ctx context.Context, instanceId, owner string) (api.Instance, error) List(ctx context.Context, owner string) ([]api.Instance, error) Delete(ctx context.Context, instanceId, owner string) error ValidateInstanceOwner(ctx context.Context, instanceId, owner string) error } type SearchService interface { Index(ctx context.Context, instanceId, owner, text string, index *string) (search.IndexedDocument, error) Search(ctx context.Context, instanceId, owner string, params api.SearchDocsParams) ([]api.Document, error) Delete(ctx context.Context, instanceId, owner, docId string, params api.DeleteDocParams) error ListIndices(ctx context.Context, instanceId, owner string) ([]string, error) } type LogsStore interface { Add(instanceId string, operation api.LogEntryOperation, status api.LogEntryStatus, message string, username string) List(instanceId string) []api.LogEntry } type Server struct { pagedb PagedbService search SearchService logs LogsStore inviteCode string } func NewServer(pagedbSvc PagedbService, searchSvc SearchService, logsStore LogsStore, inviteCode string) *Server { return &Server{ pagedb: pagedbSvc, search: searchSvc, logs: logsStore, inviteCode: inviteCode, } } func (s *Server) usernameFromCtx(r *http.Request) (string, bool) { u, ok := r.Context().Value(config.UsernameKey).(string) if !ok { slog.Error("Username not found in context") return "", false } return u, true } func writeJSONResponse(w http.ResponseWriter, statusCode int, data any) { slog.Debug("Writing JSON response", slog.Int("statusCode", statusCode)) w.Header().Set("Content-Type", "application/json") w.WriteHeader(statusCode) if err := json.NewEncoder(w).Encode(data); err != nil { slog.Error("failed to write JSON response", slog.String("error", err.Error())) } } func reqBody[T any](r io.Reader) (*T, error) { var t T if err := json.NewDecoder(r).Decode(&t); err != nil { return nil, fmt.Errorf("failed to parse %T: %w", t, err) } return &t, nil } func writeServiceError(w http.ResponseWriter, err error) { if se, ok := err.(*search.ServiceError); ok { writeJSONResponse(w, se.Code, map[string]string{"error": se.Msg}) return } slog.Error("internal server error", "error", err) writeJSONResponse(w, http.StatusInternalServerError, map[string]string{"error": "internal server error"}) }