Address code review findings across API, web and deployment
release gate / check (push) Failing after 6m43s
release gate / check (push) Failing after 6m43s
High severity: - wire TRUSTED_PROXY through Compose/deploy/docs and warn at web startup - bucket public rate limits on effective client IP, add per-bookmark budget, and forward the signed browser identity from the share page - disable first-run bootstrap in server deployments and document seeding - serialize last-admin demotion in one advisory-locked transaction Medium/low: - cap and batch share recipient inserts - add a shared outbound budget for preview/CardDAV/IMAP/signal work - pin CardDAV dials to 443 and bound IMAP logout - fix rightward kanban column drags and normalize cards on column delete - classify backend outages in auth guards, add request timeouts and an error boundary, remove the dead login route, surface throttling - enforce pinned/unpinned page reorder grouping server-side - avoid preview work for stale bookmark versions; drop duplicate dialog fetch - sequence-guard collection/tag/calendar/quick-find loads - validate property config and fix MULTI_SELECT/DATE filter inputs - document index maintenance; apply same-origin check to sign-out - trim signup names, unique shared dates and kanban keys (migration 010), reset reference cache on identity changes, hide OIDC issuer, return 413 for oversized direct bodies, escape Markdown link destinations - remove dead template files and duplicate test helper Verified with go build/vet, full PostgreSQL race suite, db convergence, tsc, both ESLint configs and a Next production build.
This commit is contained in:
@@ -230,16 +230,32 @@ func HandleKanbanColumnsDelete(w http.ResponseWriter, r *http.Request) {
|
||||
return bad("A board needs at least one column")
|
||||
}
|
||||
dest = str(remaining[0], "id")
|
||||
// The destination column's legacy key, when it has one, becomes the
|
||||
// moved cards' kanbanStatus so the two representations do not point
|
||||
// at the deleted column (review M-5/L-12). Custom columns have no
|
||||
// key; their cards keep the legacy status as a documented fallback.
|
||||
destKey := ""
|
||||
for _, c := range remaining {
|
||||
if str(c, "id") == dest {
|
||||
if k := str(c, "key"); validStatus(k) {
|
||||
destKey = k
|
||||
}
|
||||
}
|
||||
}
|
||||
// Only the three columns ResolveColumnID needs, and one set-based
|
||||
// UPDATE for the whole move instead of a round trip per bookmark.
|
||||
bms, e := db.Many(r.Context(), tx, `SELECT jsonb_build_object('id',b.id,'kanbanColumnId',b."kanbanColumnId",'kanbanStatus',b."kanbanStatus") FROM "Bookmark" b WHERE "ownerId"=$1`, user(r))
|
||||
// Positions are normalized below too (review M-5), so reload order is
|
||||
// deterministic instead of interleaving with the destination's cards.
|
||||
bms, e := db.Many(r.Context(), tx, `SELECT jsonb_build_object('id',b.id,'kanbanColumnId',b."kanbanColumnId",'kanbanStatus',b."kanbanStatus") FROM "Bookmark" b WHERE "ownerId"=$1 ORDER BY b."kanbanPosition",b."updatedAt" DESC,b.id DESC`, user(r))
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
moveIDs := make([]string, 0, len(bms))
|
||||
moving := map[string]bool{}
|
||||
for _, b := range bms {
|
||||
if kanban.ResolveColumnID(b, cols) == id {
|
||||
moveIDs = append(moveIDs, str(b, "id"))
|
||||
moving[str(b, "id")] = true
|
||||
}
|
||||
}
|
||||
if len(moveIDs) > 0 {
|
||||
@@ -248,7 +264,20 @@ func HandleKanbanColumnsDelete(w http.ResponseWriter, r *http.Request) {
|
||||
// snapshot that still shows the old column into a 409 on its
|
||||
// next save instead of letting it silently move the bookmark
|
||||
// back.
|
||||
if _, e := tx.ExecContext(r.Context(), `UPDATE "Bookmark" SET "kanbanColumnId"=$1,version=version+1,"updatedAt"=now() WHERE id=ANY($2::text[])`, dest, moveIDs); e != nil {
|
||||
if _, e := tx.ExecContext(r.Context(), `UPDATE "Bookmark" SET "kanbanColumnId"=$1,"kanbanStatus"=CASE WHEN $4='' THEN "kanbanStatus" ELSE $4::"KanbanStatus" END,version=version+1,"updatedAt"=now() WHERE id=ANY($2::text[]) AND "ownerId"=$3`, dest, moveIDs, user(r), destKey); e != nil {
|
||||
return e
|
||||
}
|
||||
// Append the moved cards after the destination's existing order
|
||||
// and renumber contiguously, exactly like a normal card move.
|
||||
order := make([]string, 0, len(bms))
|
||||
for _, b := range bms {
|
||||
bid := str(b, "id")
|
||||
if !moving[bid] && kanban.ResolveColumnID(b, remaining) == dest {
|
||||
order = append(order, bid)
|
||||
}
|
||||
}
|
||||
order = append(order, moveIDs...)
|
||||
if _, e := tx.ExecContext(r.Context(), `UPDATE "Bookmark" AS b SET "kanbanPosition"=t.ord-1 FROM unnest($1::text[]) WITH ORDINALITY AS t(bid,ord) WHERE b.id=t.bid AND b."ownerId"=$2`, order, user(r)); e != nil {
|
||||
return e
|
||||
}
|
||||
moved = len(moveIDs)
|
||||
|
||||
Reference in New Issue
Block a user