Delete and rename conversations
This commit is contained in:
@@ -6,6 +6,10 @@ import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"code.chimeric.al/chimerical/odidere/internal/db"
|
||||
)
|
||||
|
||||
func (svc *Service) listConversations(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -125,6 +129,103 @@ func (svc *Service) getConversationHandler(
|
||||
}
|
||||
}
|
||||
|
||||
// maxTitleLength is the most characters, counted as Unicode code
|
||||
// points, that a conversation title may hold. It keeps a title
|
||||
// short enough for the picker and small in storage.
|
||||
const maxTitleLength = 256
|
||||
|
||||
// updateConversation renames one conversation. The handler trims
|
||||
// the title, then rejects an empty title or a title longer than
|
||||
// maxTitleLength with 400. A conversation that does not exist, or
|
||||
// belongs to another user, returns 404. The rename is allowed while
|
||||
// inference runs: a title is not a message, and a concurrent
|
||||
// inference does not read or write it.
|
||||
func (svc *Service) updateConversation(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
log = ctx.Value(logKey).(*slog.Logger)
|
||||
id = r.PathValue("id")
|
||||
req struct {
|
||||
Title string `json:"title"`
|
||||
}
|
||||
)
|
||||
|
||||
r.Body = http.MaxBytesReader(w, r.Body, 32<<20)
|
||||
if err := json.UnmarshalRead(r.Body, &req); err != nil {
|
||||
log.ErrorContext(
|
||||
ctx, "failed to decode request",
|
||||
slog.Any("error", err),
|
||||
)
|
||||
http.Error(w, "invalid request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
req.Title = strings.TrimSpace(req.Title)
|
||||
if req.Title == "" {
|
||||
http.Error(w, "invalid request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if utf8.RuneCountInString(req.Title) > maxTitleLength {
|
||||
http.Error(w, "invalid request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Load the conversation first, so a rename of a conversation
|
||||
// that does not belong to the user reports 404, not a silent
|
||||
// no-op.
|
||||
if _, err := svc.dbr.SelectConversationIdUserId(
|
||||
ctx, id, "default",
|
||||
); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
log.ErrorContext(
|
||||
ctx, "failed to load conversation",
|
||||
slog.Any("error", err),
|
||||
)
|
||||
http.Error(
|
||||
w,
|
||||
http.StatusText(http.StatusInternalServerError),
|
||||
http.StatusInternalServerError,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
saved, err := svc.dbw.UpdateConversationTitle(
|
||||
ctx,
|
||||
db.UpdateConversationTitleParams{
|
||||
Id: id,
|
||||
Title: req.Title,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
// The conversation was deleted after the load above.
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
log.ErrorContext(
|
||||
ctx, "failed to rename conversation",
|
||||
slog.Any("error", err),
|
||||
)
|
||||
http.Error(
|
||||
w,
|
||||
http.StatusText(http.StatusInternalServerError),
|
||||
http.StatusInternalServerError,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.MarshalWrite(w, saved); err != nil {
|
||||
log.ErrorContext(
|
||||
ctx, "failed to encode conversation response",
|
||||
slog.Any("error", err),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *Service) deleteConversation(w http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
|
||||
Reference in New Issue
Block a user