From 438c946badba9881f62c2f7f1c7b93c5530bdfb2 Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Wed, 4 Mar 2026 18:30:21 +0000 Subject: [PATCH] Start creating struct-based JSON encoding for API endpoints --- api/handler.go | 64 +++++++++++++++++++++++++++++++++++++++++++++++++ api/signal.go | 15 ++++++++++++ auth/content.go | 45 ++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 api/handler.go create mode 100644 api/signal.go create mode 100644 auth/content.go diff --git a/api/handler.go b/api/handler.go new file mode 100644 index 00000000..6b4151ad --- /dev/null +++ b/api/handler.go @@ -0,0 +1,64 @@ +package api + +import ( + "context" + "encoding/json" + "net/http" + + "github.com/Gleipnir-Technology/nidus-sync/auth" + "github.com/Gleipnir-Technology/nidus-sync/db" + "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/html" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" + "github.com/Gleipnir-Technology/nidus-sync/platform" + "github.com/rs/zerolog/log" +) + +type handlerFunctionGet[T any] func(context.Context, *http.Request, *models.Organization, *models.User) (*T, *nhttp.ErrorWithStatus) +type wrappedHandler func(http.ResponseWriter, *http.Request) +type contentAuthenticated[T any] struct { + C T + Config html.ContentConfig + User platform.User +} + +type ErrorAPI struct { + Message string `json:"message"` +} + +// w http.ResponseWriter, r *http.Request, u *models.User) { +func authenticatedHandlerJSON[T any](f handlerFunctionGet[T]) http.Handler { + return auth.NewEnsureAuth(func(w http.ResponseWriter, r *http.Request, u *models.User) { + ctx := r.Context() + org, err := u.Organization().One(ctx, db.PGInstance.BobDB) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + if org == nil { + http.Error(w, "nil org", http.StatusInternalServerError) + return + } + var body []byte + resp, e := f(ctx, r, org, u) + w.Header().Set("Content-Type", "application/json") + //log.Info().Str("template", template).Err(e).Msg("handler done") + if e != nil { + log.Warn().Int("status", e.Status).Err(e).Str("user message", e.Message).Msg("Responding with an error from sync pages") + body, err = json.Marshal(ErrorAPI{Message: e.Error()}) + if err != nil { + log.Error().Err(err).Msg("failed to marshal error") + http.Error(w, "{\"message\": \"boom. I can't even tell you what went wrong\"}", http.StatusInternalServerError) + return + } + http.Error(w, string(body), e.Status) + return + } + body, err = json.Marshal(resp) + if err != nil { + http.Error(w, "{\"message\": \"failed to marshal json\"}", http.StatusInternalServerError) + return + } + w.Write(body) + }) +} diff --git a/api/signal.go b/api/signal.go new file mode 100644 index 00000000..256419b6 --- /dev/null +++ b/api/signal.go @@ -0,0 +1,15 @@ +package api + +import ( + "context" + "net/http" + + "github.com/Gleipnir-Technology/nidus-sync/db/models" + nhttp "github.com/Gleipnir-Technology/nidus-sync/http" +) + +type contentListSignal struct{} + +func listSignal(ctx context.Context, r *http.Request, org *models.Organization, user *models.User) (*contentListSignal, *nhttp.ErrorWithStatus) { + return nil, nil +} diff --git a/auth/content.go b/auth/content.go new file mode 100644 index 00000000..ca17caf8 --- /dev/null +++ b/auth/content.go @@ -0,0 +1,45 @@ +package auth + +import ( + "context" + "strings" + + "github.com/Gleipnir-Technology/nidus-sync/db/models" + "github.com/Gleipnir-Technology/nidus-sync/notification" + "github.com/Gleipnir-Technology/nidus-sync/platform" +) + +func ContentForUser(ctx context.Context, user *models.User) (platform.User, error) { + notifications, err := notification.ForUser(ctx, user) + if err != nil { + return platform.User{}, err + } + org := user.R.Organization + var organization platform.Organization + if org != nil { + organization.ID = int(org.ID) + organization.Name = org.Name + } + return platform.User{ + DisplayName: user.DisplayName, + Initials: extractInitials(user.DisplayName), + Notifications: notifications, + Organization: organization, + Role: user.Role.String(), + Username: user.Username, + }, nil + +} + +func extractInitials(name string) string { + parts := strings.Fields(name) + var initials strings.Builder + + for _, part := range parts { + if len(part) > 0 { + initials.WriteString(strings.ToUpper(string(part[0]))) + } + } + + return initials.String() +}