package api import ( "context" "encoding/json" "log/slog" "net/http" "time" "github.com/go-chi/chi/v5" "github.com/go-chi/render" "github.com/google/uuid" ) func ChannelCtx(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { slog.Debug("channel: entering ChannelCtx middleware") var channel *Channel var err error if channelID := chi.URLParam(r, "channelID"); channelID != "" { slog.Debug("channel: fetching channel", "channelID", channelID) channel, err = dbGetChannel(channelID) } else { slog.Error("channel: channelID not found in URL parameters") render.Render(w, r, ErrNotFound) return } if err != nil { slog.Error("channel: failed to fetch channel", "channelID", chi.URLParam(r, "channelID"), "error", err) render.Render(w, r, ErrNotFound) return } slog.Debug("channel: successfully fetched channel", "channelID", channel.ID) ctx := context.WithValue(r.Context(), channelKey{}, channel) next.ServeHTTP(w, r.WithContext(ctx)) }) } func GetChannel(w http.ResponseWriter, r *http.Request) { slog.Debug("channel: entering GetChannel handler") channel, ok := r.Context().Value(channelKey{}).(*Channel) if !ok || channel == nil { slog.Error("channel: channel not found in context") render.Render(w, r, ErrNotFound) return } slog.Debug("channel: rendering channel", "channelID", channel.ID) if err := render.Render(w, r, NewChannelResponse(channel)); err != nil { slog.Error("channel: failed to render channel response", "channelID", channel.ID, "error", err) render.Render(w, r, ErrRender(err)) return } } func ListChannels(w http.ResponseWriter, r *http.Request) { slog.Debug("channel: entering ListChannels handler") dbChannels, err := dbGetAllChannels() if err != nil { slog.Error("channel: failed to fetch channels", "error", err) render.Render(w, r, ErrRender(err)) return } slog.Debug("channel: successfully fetched channels", "count", len(dbChannels)) if err := render.RenderList(w, r, NewChannelListResponse(dbChannels)); err != nil { slog.Error("channel: failed to render channel list response", "error", err) render.Render(w, r, ErrRender(err)) return } } func EditChannel(w http.ResponseWriter, r *http.Request) { slog.Debug("channel: entering EditChannel handler") channel, ok := r.Context().Value(channelKey{}).(*Channel) if !ok || channel == nil { slog.Error("channel: channel not found in context") render.Render(w, r, ErrNotFound) return } err := r.ParseMultipartForm(64 << 10) if err != nil { slog.Error("channel: failed to parse multipart form", "error", err) http.Error(w, "Unable to parse form", http.StatusBadRequest) return } channelName := r.FormValue("name") channeltype := r.FormValue("type") if channelName == "" || channeltype == "" { slog.Error("channel: channel name or type is empty") http.Error(w, "Channel name or type cannot be empty", http.StatusBadRequest) return } slog.Debug("channel: updating channel", "channelID", channel.ID) channel.Name = channelName channel.Type = channeltype err = dbUpdateChannel(channel) if err != nil { slog.Error("channel: failed to update channel", "channelID", channel.ID, "error", err) render.Render(w, r, ErrRender(err)) return } slog.Debug("channel: successfully updated channel", "channelID", channel.ID) if err := render.Render(w, r, NewChannelResponse(channel)); err != nil { slog.Error("channel: failed to render updated channel response", "channelID", channel.ID, "error", err) render.Render(w, r, ErrRender(err)) return } } func DeleteChannel(w http.ResponseWriter, r *http.Request) { slog.Debug("channel: entering DeleteChannel handler") channel, ok := r.Context().Value(channelKey{}).(*Channel) if !ok || channel == nil { slog.Error("channel: channel not found in context") render.Render(w, r, ErrNotFound) return } slog.Debug("channel: deleting channel", "channelID", channel.ID) err := dbDeleteChannel(channel.ID.String()) if err != nil { slog.Error("channel: failed to delete channel", "channelID", channel.ID, "error", err) render.Render(w, r, ErrRender(err)) return } slog.Debug("channel: successfully deleted channel", "channelID", channel.ID) if err := render.Render(w, r, NewChannelResponse(channel)); err != nil { slog.Error("channel: failed to render deleted channel response", "channelID", channel.ID, "error", err) render.Render(w, r, ErrRender(err)) return } } func newChannelID() uuid.UUID { return uuid.New() } func NewChannel(w http.ResponseWriter, r *http.Request) { slog.Debug("channel: entering NewChannel handler") err := r.ParseMultipartForm(64 << 10) if err != nil { slog.Error("channel: failed to parse multipart form", "error", err) http.Error(w, "Unable to parse form", http.StatusBadRequest) return } channelName := r.FormValue("name") channeltype := r.FormValue("type") if channelName == "" || channeltype == "" { slog.Error("channel: channel name or type is empty") http.Error(w, "Channel name or type cannot be empty", http.StatusBadRequest) return } channel := Channel{ ID: newChannelID(), Name: channelName, Type: channeltype, Created: time.Now(), } slog.Debug("channel: creating new channel", "channelID", channel.ID) err = dbAddChannel(&channel) if err != nil { slog.Error("channel: failed to add new channel", "channelID", channel.ID, "error", err) render.Render(w, r, ErrRender(err)) return } slog.Debug("channel: successfulyl created new channel", "channelID", channel.ID) render.Render(w, r, NewChannelResponse(&channel)) } type channelKey struct{} type Channel struct { ID uuid.UUID `json:"id"` Name string `json:"name"` Type string `json:"type"` Created time.Time `json:"created"` } type ChannelRequest struct { *Channel } type ChannelResponse struct { *Channel } func (c ChannelResponse) MarshalJson() ([]byte, error) { type OrderedChannelResponse struct { ID uuid.UUID `json:"id"` Name string `json:"name"` Type string `json:"type"` Created string `json:"created"` } ordered := OrderedChannelResponse{ ID: c.Channel.ID, Name: c.Channel.Name, Type: c.Channel.Type, Created: c.Channel.Created.Format(time.RFC3339), } return json.Marshal(ordered) }