Address review: redact plant owner ids, cache-control, 400, DRY reset
Build image / build-and-push (push) Successful in 15s

- Security (2-model, security+correctness): the public payload zeroed
  Garden.OwnerID but referenced custom plants still carried Plant.OwnerID,
  leaking the owner's user id to anonymous viewers. Redact plant owner ids
  too, and assert it in the service test.
- Set Cache-Control: no-store on the public read so a capability-URL response
  isn't held in a shared proxy cache and always reflects the live garden.
- createShareLink now 400s on a present-but-malformed JSON body instead of
  silently enabling (an empty body still means enable-without-rotate).
- Extract the transient-editor-state reset into a shared resetTransient store
  action (3-model finding) and use it from PublicGardenPage.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01JdQpdYYsTgtkJBxbcpAszi
This commit is contained in:
2026-07-19 02:17:15 -04:00
co-authored by Claude Opus 4.8
parent f3b0ca572d
commit 0842618ad1
5 changed files with 43 additions and 11 deletions
+17
View File
@@ -49,6 +49,12 @@ interface EditorState {
// pan gesture stands down (both listen on the same svg).
objectDragging: boolean
setObjectDragging: (v: boolean) => void
// Clear all transient (non-persisted) editor state at once — selection, focus,
// armed plant/kind, and any in-flight live geometry — when entering a garden
// view fresh (e.g. the public read-only page on mount). The viewport is left
// alone; the canvas re-fits it from the loaded garden.
resetTransient: () => void
}
export const useEditorStore = create<EditorState>((set) => ({
@@ -78,4 +84,15 @@ export const useEditorStore = create<EditorState>((set) => ({
objectDragging: false,
setObjectDragging: (v) => set({ objectDragging: v }),
resetTransient: () =>
set({
selectedId: null,
selectedPlantingId: null,
focusedObjectId: null,
armedPlant: null,
armedKind: null,
liveObject: null,
livePlanting: null,
}),
}))
+1 -8
View File
@@ -25,14 +25,7 @@ export function PublicGardenPage() {
// Start from a clean canvas: if a logged-in user reaches here from their own
// editor, stale focus/selection state would otherwise dim or highlight things.
useEffect(() => {
const s = useEditorStore.getState()
s.setFocusedObject(null)
s.select(null)
s.selectPlanting(null)
s.setArmedPlant(null)
s.setArmedKind(null)
s.setLiveObject(null)
s.setLivePlanting(null)
useEditorStore.getState().resetTransient()
}, [token])
const objects = useMemo(() => full.data?.objects.map(toEditorObject) ?? [], [full.data?.objects])