feat(desktop): isolate platform accounts per desktop client
Frontend CI / Frontend (push) Successful in 3m4s
Backend CI / Backend (push) Successful in 14m24s

Scope desktop media accounts by the authenticated desktop client_id so
the same user logging in from Mac and Windows no longer sees a shared
account list. The list, identity lookup, upsert, patch, tombstone and
async health sink paths now all require matching client_id, and the
active uniqueness index moves from (workspace, platform, uid) to
(workspace, client, platform, uid).

Also strengthen the desktop client_id seed: when the OS machine id is
unavailable, fall back to a hashed fingerprint of stable hardware MAC
addresses before the random installation id, so the same hardware
keeps the same client across reinstalls.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-06 13:13:11 +08:00
parent f6aed87e44
commit 1ba29b9a09
14 changed files with 378 additions and 24 deletions
+17
View File
@@ -790,3 +790,20 @@
- `cd server && go test ./...`
- `git diff --check`
- `ruby -e 'require "yaml"; YAML.load_file("deploy/monitoring/article-generation-alerts.yml"); puts "ok"'`
## 2026-05-06T13:05:00+08:00 - Desktop client account isolation
- Fixed the desktop media account cross-device leak reported from Mac/Windows login:
- `/api/desktop/accounts` now lists accounts by the authenticated desktop `client_id`, not `workspace_id + user_id`.
- Desktop account identity lookup and upsert now use `(workspace_id, client_id, platform_id, platform_uid)`, preventing one machine from overwriting another machine's row for the same third-party UID.
- Desktop-side patch/delete and async health sink updates now also require matching `client_id`.
- Added migration `20260506120000_scope_platform_accounts_to_desktop_client` to update the active uniqueness index.
- Added MAC-address fingerprint fallback for desktop client ID generation while keeping OS machine ID as the preferred stable source.
- Verification passed:
- `cd server && make sqlc-generate`
- `cd server && go test ./internal/tenant/app ./internal/tenant/repository ./internal/tenant/transport -count=1`
- `pnpm --filter @geo/desktop-client typecheck`
- `pnpm --filter @geo/desktop-client exec vitest run src/main/device-id.test.ts`
- `git diff --check`
- Caveat:
- `pnpm --filter @geo/desktop-client test -- src/main/device-id.test.ts` ran unrelated existing tests and failed in `deepseek.test.ts` due to a pre-existing missing `electron/main` mock; the new device-id test passes when run directly with vitest.