diff --git a/internal/handlers/api.go b/internal/handlers/api.go index 7182105..a55cbc1 100644 --- a/internal/handlers/api.go +++ b/internal/handlers/api.go @@ -303,46 +303,61 @@ func (s *Handlers) handlePrivmsg(w http.ResponseWriter, r *http.Request, uid, ni content := strings.Join(lines, "\n") if strings.HasPrefix(to, "#") { - // Channel message. - var chID string + s.sendChannelMessage(w, r, uid, nick, to, content) - //nolint:gosec // G701: parameterized query, not injection - err := s.params.Database.GetDB().QueryRowContext(r.Context(), - "SELECT id FROM channels WHERE name = ?", to).Scan(&chID) - if err != nil { - s.respondJSON(w, r, map[string]string{"error": "channel not found"}, http.StatusNotFound) - - return - } - - msgID, err := s.params.Database.SendMessage(r.Context(), chID, uid, nick, content) - if err != nil { - s.log.Error("send message failed", "error", err) - s.respondJSON(w, r, map[string]string{"error": "internal error"}, http.StatusInternalServerError) - - return - } - - s.respondJSON(w, r, map[string]any{"id": msgID, "status": "sent"}, http.StatusCreated) - } else { - // DM. - targetUser, err := s.params.Database.GetUserByNick(r.Context(), to) - if err != nil { - s.respondJSON(w, r, map[string]string{"error": "user not found"}, http.StatusNotFound) - - return - } - - msgID, err := s.params.Database.SendDM(r.Context(), uid, nick, targetUser.ID, content) - if err != nil { - s.log.Error("send dm failed", "error", err) - s.respondJSON(w, r, map[string]string{"error": "internal error"}, http.StatusInternalServerError) - - return - } - - s.respondJSON(w, r, map[string]any{"id": msgID, "status": "sent"}, http.StatusCreated) + return } + + // DM. + s.sendDirectMessage(w, r, uid, nick, to, content) +} + +func (s *Handlers) sendChannelMessage( + w http.ResponseWriter, r *http.Request, + uid, nick, channel, content string, +) { + var chID string + + //nolint:gosec // G701: parameterized query, not injection + err := s.params.Database.GetDB().QueryRowContext(r.Context(), + "SELECT id FROM channels WHERE name = ?", channel).Scan(&chID) + if err != nil { + s.respondJSON(w, r, map[string]string{"error": "channel not found"}, http.StatusNotFound) + + return + } + + msgID, err := s.params.Database.SendMessage(r.Context(), chID, uid, nick, content) + if err != nil { + s.log.Error("send message failed", "error", err) + s.respondJSON(w, r, map[string]string{"error": "internal error"}, http.StatusInternalServerError) + + return + } + + s.respondJSON(w, r, map[string]any{"id": msgID, "status": "sent"}, http.StatusCreated) +} + +func (s *Handlers) sendDirectMessage( + w http.ResponseWriter, r *http.Request, + uid, nick, to, content string, +) { + targetUser, err := s.params.Database.GetUserByNick(r.Context(), to) + if err != nil { + s.respondJSON(w, r, map[string]string{"error": "user not found"}, http.StatusNotFound) + + return + } + + msgID, err := s.params.Database.SendDM(r.Context(), uid, nick, targetUser.ID, content) + if err != nil { + s.log.Error("send dm failed", "error", err) + s.respondJSON(w, r, map[string]string{"error": "internal error"}, http.StatusInternalServerError) + + return + } + + s.respondJSON(w, r, map[string]any{"id": msgID, "status": "sent"}, http.StatusCreated) } func (s *Handlers) handleJoin(w http.ResponseWriter, r *http.Request, uid, to string) { @@ -495,49 +510,63 @@ func (s *Handlers) HandleGetHistory() http.HandlerFunc { } if strings.HasPrefix(target, "#") { - // Channel history — look up channel by name to get its ID for target matching. - var chID string + s.getChannelHistory(w, r, target, beforeTS, limit) - //nolint:gosec // G701: parameterized query, not injection - err := s.params.Database.GetDB().QueryRowContext(r.Context(), - "SELECT id FROM channels WHERE name = ?", target).Scan(&chID) - if err != nil { - s.respondJSON(w, r, map[string]string{"error": "channel not found"}, http.StatusNotFound) - - return - } - - msgs, err := s.params.Database.GetMessagesBefore(r.Context(), chID, beforeTS, limit) - if err != nil { - s.log.Error("get history failed", "error", err) - s.respondJSON(w, r, map[string]string{"error": "internal error"}, http.StatusInternalServerError) - - return - } - - s.respondJSON(w, r, msgs, http.StatusOK) - } else { - // DM history. - targetUser, err := s.params.Database.GetUserByNick(r.Context(), target) - if err != nil { - s.respondJSON(w, r, map[string]string{"error": "user not found"}, http.StatusNotFound) - - return - } - - msgs, err := s.params.Database.GetDMsBefore(r.Context(), uid, targetUser.ID, beforeTS, limit) - if err != nil { - s.log.Error("get dm history failed", "error", err) - s.respondJSON(w, r, map[string]string{"error": "internal error"}, http.StatusInternalServerError) - - return - } - - s.respondJSON(w, r, msgs, http.StatusOK) + return } + + s.getDMHistory(w, r, uid, target, beforeTS, limit) } } +func (s *Handlers) getChannelHistory( + w http.ResponseWriter, r *http.Request, + channel, beforeTS string, limit int, +) { + var chID string + + //nolint:gosec // G701: parameterized query, not injection + err := s.params.Database.GetDB().QueryRowContext(r.Context(), + "SELECT id FROM channels WHERE name = ?", channel).Scan(&chID) + if err != nil { + s.respondJSON(w, r, map[string]string{"error": "channel not found"}, http.StatusNotFound) + + return + } + + msgs, err := s.params.Database.GetMessagesBefore(r.Context(), chID, beforeTS, limit) + if err != nil { + s.log.Error("get history failed", "error", err) + s.respondJSON(w, r, map[string]string{"error": "internal error"}, http.StatusInternalServerError) + + return + } + + s.respondJSON(w, r, msgs, http.StatusOK) +} + +func (s *Handlers) getDMHistory( + w http.ResponseWriter, r *http.Request, + uid, target, beforeTS string, limit int, +) { + targetUser, err := s.params.Database.GetUserByNick(r.Context(), target) + if err != nil { + s.respondJSON(w, r, map[string]string{"error": "user not found"}, http.StatusNotFound) + + return + } + + msgs, err := s.params.Database.GetDMsBefore(r.Context(), uid, targetUser.ID, beforeTS, limit) + if err != nil { + s.log.Error("get dm history failed", "error", err) + s.respondJSON(w, r, map[string]string{"error": "internal error"}, http.StatusInternalServerError) + + return + } + + s.respondJSON(w, r, msgs, http.StatusOK) +} + // HandleServerInfo returns server metadata (MOTD, name). func (s *Handlers) HandleServerInfo() http.HandlerFunc { type response struct {