mirror of
https://github.com/lobehub/lobehub.git
synced 2026-09-20 04:56:13 +08:00
1eacff997e
* 🔒 fix(acceptance): close the holes the open discussion opened Opening an acceptance's discussion to whoever holds its public link widened three things it should not have. An agent belongs to its workspace, not to its readers. The author-agent gate was scoped by the acceptance's workspace, which answers "is this agent visible there", not "may this caller use it" — and the discussion payload hands a visitor the ids to pick from. It is now scoped to the workspace only for callers who actually belong to it; everyone else falls back to their own agents. Read-only means read-only. A `viewer` of the acceptance's own workspace was swept in by "anyone signed in who can read it" and could write on a private delivery, unlike the same role on topic and document comments. Commenting now needs the public link or a non-viewer seat. An open discussion needs a way to close an unwanted remark. Its author is exactly who will not remove it, so the acceptance's reviewers may now take one down — the same tombstone the author's own deletion leaves — and a visitor gets a per-minute ceiling on how much they can land. Two client reads of `canDelete` as "I wrote this" are replaced by the author id, since that flag now also turns on for a moderator: whether a thread may be closed, and whose approval the reviewer bar is showing. The read-only line no longer flashes while the permission answer is in flight, and the sign-in return URL moves to a shared helper. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * 🔒 fix(acceptance): do not call an outside reader a departed teammate The `former` tag infers "left the workspace" from "is not a member now". That only holds on a delivery nobody outside could write on. With the link public, most people in the discussion never had a seat, so the tag is dropped there rather than guessed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * 🐛 fix(acceptance): charge the visitor ceiling where the row is written Codex review of the open-discussion hardening, three findings. The ceiling was counted before the write and enforced after it, so a burst of parallel requests each read the same under-limit count and all landed — which is the shape a flood actually arrives in. It now travels into the write: the transaction takes an advisory lock on (acceptance, author), inserts, counts, and rolls itself back when it is the one over the line. Everybody else still writes in parallel. The same move fixes a retry being punished for a remark it already made. A replay of the tenth comment's idempotency key inserts nothing, so it is no longer counted and answers with the row it wrote the first time, instead of a refusal for a lost response. `canResolveThread` reads the signed-in identity, which arrives after the first paint, but the overlay memo did not depend on it — a thread could keep the answer computed while nobody was signed in until an unrelated comment changed. It is a `useCallback` on what it reads, and the memo depends on it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * 🐛 fix(workbench): let the SSR electron stub answer the gateway device question Five shared store modules read getElectronStoreState().gatewayDeviceInfo to decide whether a working directory is local. They enter the workbench SSR graph through the acceptance check route, where the stub had no such key, so the stub surface guard failed the build. Give the stub the key with no device, which is the truthful answer on the server. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>