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
@@ -1,3 +1,29 @@
-- name: ListDesktopAccountsByClient :many
SELECT
desktop_id,
tenant_id,
workspace_id,
user_id,
client_id,
platform_id,
platform_uid,
account_fingerprint,
display_name,
avatar_url,
health,
verified_at,
tags,
sync_version,
deleted_at,
delete_requested_at,
created_at,
updated_at
FROM platform_accounts
WHERE workspace_id = sqlc.arg(workspace_id)
AND client_id = sqlc.arg(client_id)
AND deleted_at IS NULL
ORDER BY platform_id, display_name, created_at DESC;
-- name: ListDesktopAccountsByUser :many
SELECT
desktop_id,
@@ -71,6 +97,7 @@ SELECT
updated_at
FROM platform_accounts
WHERE workspace_id = sqlc.arg(workspace_id)
AND client_id = sqlc.arg(client_id)
AND platform_id = sqlc.arg(platform_id)
AND platform_uid = sqlc.arg(platform_uid)
AND deleted_at IS NULL
@@ -117,7 +144,7 @@ VALUES (
sqlc.narg(tags),
1
)
ON CONFLICT (workspace_id, platform_id, platform_uid) WHERE deleted_at IS NULL
ON CONFLICT (workspace_id, client_id, platform_id, platform_uid) WHERE deleted_at IS NULL
DO UPDATE SET
client_id = EXCLUDED.client_id,
user_id = EXCLUDED.user_id,
@@ -135,6 +162,7 @@ DO UPDATE SET
delete_requested_at = NULL,
updated_at = now()
WHERE platform_accounts.workspace_id = EXCLUDED.workspace_id
AND platform_accounts.client_id = EXCLUDED.client_id
AND (
sqlc.narg(if_sync_version)::bigint IS NULL
OR platform_accounts.sync_version = sqlc.narg(if_sync_version)::bigint
@@ -172,6 +200,7 @@ SET display_name = COALESCE(sqlc.narg(display_name)::text, display_name),
updated_at = now()
WHERE desktop_id = sqlc.arg(desktop_id)
AND workspace_id = sqlc.arg(workspace_id)
AND client_id = sqlc.arg(client_id)
AND sync_version = sqlc.arg(if_sync_version)
AND deleted_at IS NULL
RETURNING
@@ -201,6 +230,7 @@ SET deleted_at = now(),
updated_at = now()
WHERE desktop_id = sqlc.arg(desktop_id)
AND workspace_id = sqlc.arg(workspace_id)
AND client_id = sqlc.arg(client_id)
AND sync_version = sqlc.arg(if_sync_version)
AND deleted_at IS NULL
RETURNING