2beb23b360
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>
41 lines
1.4 KiB
TypeScript
41 lines
1.4 KiB
TypeScript
import { db, shoppingLists, shoppingListMembers, eq, and } from "@epicure/db";
|
|
|
|
export type ShoppingListRole = "owner" | "editor" | "viewer";
|
|
|
|
export type ShoppingListAccess = {
|
|
list: typeof shoppingLists.$inferSelect;
|
|
role: ShoppingListRole;
|
|
};
|
|
|
|
/**
|
|
* Resolves access to a shopping list — owner, member with their assigned role,
|
|
* or (when `userId` is null, i.e. no session) an anonymous visitor via the
|
|
* public link: "editor" if the owner enabled public editing, "viewer" if the
|
|
* list is merely public, else no access. The link's id is the only credential
|
|
* an anonymous visitor has, so this must never fall through to a real role
|
|
* for a non-public list.
|
|
*/
|
|
export async function getShoppingListAccess(
|
|
listId: string,
|
|
userId: string | null
|
|
): Promise<ShoppingListAccess | null> {
|
|
const list = await db.query.shoppingLists.findFirst({ where: eq(shoppingLists.id, listId) });
|
|
if (!list) return null;
|
|
|
|
if (userId) {
|
|
if (list.userId === userId) return { list, role: "owner" };
|
|
|
|
const member = await db.query.shoppingListMembers.findFirst({
|
|
where: and(eq(shoppingListMembers.listId, listId), eq(shoppingListMembers.userId, userId)),
|
|
});
|
|
if (member) return { list, role: member.role };
|
|
}
|
|
|
|
if (!list.isPublic) return null;
|
|
return { list, role: list.publicEditable ? "editor" : "viewer" };
|
|
}
|
|
|
|
export function canWriteShoppingList(role: ShoppingListRole): boolean {
|
|
return role === "owner" || role === "editor";
|
|
}
|