feat(desktop): isolate platform accounts per desktop client
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:
@@ -52,6 +52,7 @@ type UpsertDesktopAccountParams struct {
|
||||
type PatchDesktopAccountParams struct {
|
||||
DesktopID uuid.UUID
|
||||
WorkspaceID int64
|
||||
ClientID uuid.UUID
|
||||
DisplayName *string
|
||||
Health *string
|
||||
VerifiedAt *time.Time
|
||||
@@ -60,12 +61,13 @@ type PatchDesktopAccountParams struct {
|
||||
}
|
||||
|
||||
type DesktopAccountRepository interface {
|
||||
ListByClient(ctx context.Context, workspaceID int64, clientID uuid.UUID) ([]DesktopAccount, error)
|
||||
ListByUser(ctx context.Context, workspaceID, userID int64) ([]DesktopAccount, error)
|
||||
GetByDesktopID(ctx context.Context, desktopID uuid.UUID, workspaceID int64) (*DesktopAccount, error)
|
||||
GetByIdentity(ctx context.Context, workspaceID int64, platform, platformUID string) (*DesktopAccount, error)
|
||||
GetByIdentity(ctx context.Context, workspaceID int64, clientID uuid.UUID, platform, platformUID string) (*DesktopAccount, error)
|
||||
Upsert(ctx context.Context, params UpsertDesktopAccountParams) (*DesktopAccount, error)
|
||||
Patch(ctx context.Context, params PatchDesktopAccountParams) (*DesktopAccount, error)
|
||||
Tombstone(ctx context.Context, desktopID uuid.UUID, workspaceID, ifSyncVersion int64) (*DesktopAccount, error)
|
||||
Tombstone(ctx context.Context, desktopID uuid.UUID, workspaceID int64, clientID uuid.UUID, ifSyncVersion int64) (*DesktopAccount, error)
|
||||
MarkDeleteRequested(ctx context.Context, desktopID uuid.UUID, workspaceID int64) (*DesktopAccount, error)
|
||||
ClearDeleteRequested(ctx context.Context, desktopID uuid.UUID, workspaceID int64) (*DesktopAccount, error)
|
||||
}
|
||||
@@ -78,6 +80,26 @@ func NewDesktopAccountRepository(db generated.DBTX) DesktopAccountRepository {
|
||||
return &desktopAccountRepository{q: newQuerier(db)}
|
||||
}
|
||||
|
||||
func (r *desktopAccountRepository) ListByClient(ctx context.Context, workspaceID int64, clientID uuid.UUID) ([]DesktopAccount, error) {
|
||||
rows, err := r.q.ListDesktopAccountsByClient(ctx, generated.ListDesktopAccountsByClientParams{
|
||||
WorkspaceID: workspaceID,
|
||||
ClientID: pgUUID(clientID),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := make([]DesktopAccount, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
item, err := desktopAccountFromClientListRow(row)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, *item)
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (r *desktopAccountRepository) ListByUser(ctx context.Context, workspaceID, userID int64) ([]DesktopAccount, error) {
|
||||
rows, err := r.q.ListDesktopAccountsByUser(ctx, generated.ListDesktopAccountsByUserParams{
|
||||
WorkspaceID: workspaceID,
|
||||
@@ -109,9 +131,10 @@ func (r *desktopAccountRepository) GetByDesktopID(ctx context.Context, desktopID
|
||||
return desktopAccountFromGetRow(row)
|
||||
}
|
||||
|
||||
func (r *desktopAccountRepository) GetByIdentity(ctx context.Context, workspaceID int64, platform, platformUID string) (*DesktopAccount, error) {
|
||||
func (r *desktopAccountRepository) GetByIdentity(ctx context.Context, workspaceID int64, clientID uuid.UUID, platform, platformUID string) (*DesktopAccount, error) {
|
||||
row, err := r.q.GetDesktopAccountByIdentity(ctx, generated.GetDesktopAccountByIdentityParams{
|
||||
WorkspaceID: workspaceID,
|
||||
ClientID: pgUUID(clientID),
|
||||
PlatformID: platform,
|
||||
PlatformUid: platformUID,
|
||||
})
|
||||
@@ -177,6 +200,7 @@ func (r *desktopAccountRepository) Patch(ctx context.Context, params PatchDeskto
|
||||
Tags: tagsJSON,
|
||||
DesktopID: pgUUID(params.DesktopID),
|
||||
WorkspaceID: params.WorkspaceID,
|
||||
ClientID: pgUUID(params.ClientID),
|
||||
IfSyncVersion: params.IfSyncVersion,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -185,10 +209,11 @@ func (r *desktopAccountRepository) Patch(ctx context.Context, params PatchDeskto
|
||||
return desktopAccountFromPatchRow(row)
|
||||
}
|
||||
|
||||
func (r *desktopAccountRepository) Tombstone(ctx context.Context, desktopID uuid.UUID, workspaceID, ifSyncVersion int64) (*DesktopAccount, error) {
|
||||
func (r *desktopAccountRepository) Tombstone(ctx context.Context, desktopID uuid.UUID, workspaceID int64, clientID uuid.UUID, ifSyncVersion int64) (*DesktopAccount, error) {
|
||||
row, err := r.q.TombstoneDesktopAccount(ctx, generated.TombstoneDesktopAccountParams{
|
||||
DesktopID: pgUUID(desktopID),
|
||||
WorkspaceID: workspaceID,
|
||||
ClientID: pgUUID(clientID),
|
||||
IfSyncVersion: ifSyncVersion,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -219,6 +244,29 @@ func (r *desktopAccountRepository) ClearDeleteRequested(ctx context.Context, des
|
||||
return desktopAccountFromClearDeleteRow(row)
|
||||
}
|
||||
|
||||
func desktopAccountFromClientListRow(row generated.ListDesktopAccountsByClientRow) (*DesktopAccount, error) {
|
||||
return buildDesktopAccount(
|
||||
row.DesktopID,
|
||||
row.TenantID,
|
||||
row.WorkspaceID,
|
||||
row.UserID,
|
||||
row.ClientID,
|
||||
row.PlatformID,
|
||||
row.PlatformUid,
|
||||
row.AccountFingerprint,
|
||||
row.DisplayName,
|
||||
row.AvatarUrl,
|
||||
row.Health,
|
||||
row.VerifiedAt,
|
||||
row.Tags,
|
||||
row.SyncVersion,
|
||||
row.DeletedAt,
|
||||
row.DeleteRequestedAt,
|
||||
row.CreatedAt,
|
||||
row.UpdatedAt,
|
||||
)
|
||||
}
|
||||
|
||||
func desktopAccountFromListRow(row generated.ListDesktopAccountsByUserRow) (*DesktopAccount, error) {
|
||||
return buildDesktopAccount(
|
||||
row.DesktopID,
|
||||
|
||||
Reference in New Issue
Block a user