feat: public shopping list links can allow editing

Owner opts in per-list via a new "Allow editing" toggle next to the
existing public-link switch. Anonymous writes are scoped to that one
list only — the link id is the sole credential, enforced in
getShoppingListAccess and the item routes (no session required there
now), with an IP rate limit on genuinely anonymous requests. Turning
off the public link also revokes editing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Arnaud
2026-07-13 12:53:52 +02:00
parent b1f4bba6dd
commit 2beb23b360
21 changed files with 5174 additions and 76 deletions
@@ -0,0 +1 @@
ALTER TABLE "shopping_lists" ADD COLUMN "public_editable" boolean DEFAULT false NOT NULL;
File diff suppressed because it is too large Load Diff
@@ -253,6 +253,13 @@
"when": 1783887815564,
"tag": "0035_gifted_plazm",
"breakpoints": true
},
{
"idx": 36,
"version": "7",
"when": 1783939302554,
"tag": "0036_mixed_wither",
"breakpoints": true
}
]
}
+5
View File
@@ -61,6 +61,11 @@ export const shoppingLists = pgTable("shopping_lists", {
userId: text("user_id").references(() => users.id, { onDelete: "cascade" }),
name: text("name").notNull(),
isPublic: boolean("is_public").notNull().default(false),
// Only meaningful when isPublic is true — lets anyone with the link edit
// (check/add/reorder) items with no account, like a Google-Docs-style
// "anyone with the link can edit". The link itself (an unguessable id) is
// the credential; there's no separate per-visitor identity.
publicEditable: boolean("public_editable").notNull().default(false),
generatedAt: timestamp("generated_at"),
createdAt: timestamp("created_at").notNull().defaultNow(),
});