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:
@@ -155,6 +155,9 @@ func validateBufferedDesktopAccountHealthReport(report bufferedDesktopAccountHea
|
||||
if _, err := uuid.Parse(strings.TrimSpace(report.AccountID)); err != nil {
|
||||
return fmt.Errorf("invalid account_id: %w", err)
|
||||
}
|
||||
if _, err := uuid.Parse(strings.TrimSpace(report.ClientID)); err != nil {
|
||||
return fmt.Errorf("invalid client_id: %w", err)
|
||||
}
|
||||
if report.WorkspaceID == 0 {
|
||||
return fmt.Errorf("workspace_id is required")
|
||||
}
|
||||
@@ -188,9 +191,10 @@ SET health = $1,
|
||||
updated_at = now()
|
||||
WHERE desktop_id = $5::uuid
|
||||
AND workspace_id = $6
|
||||
AND client_id = $7::uuid
|
||||
AND deleted_at IS NULL
|
||||
AND (last_check_at IS NULL OR last_check_at <= $4::timestamptz)
|
||||
`, report.Health, desktopAccountLegacyStatusFromHealth(report.Health), verifiedAt, checkedAt, report.AccountID, report.WorkspaceID)
|
||||
`, report.Health, desktopAccountLegacyStatusFromHealth(report.Health), verifiedAt, checkedAt, report.AccountID, report.WorkspaceID, report.ClientID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("sink desktop account health snapshot: %w", err)
|
||||
}
|
||||
|
||||
@@ -133,12 +133,12 @@ type bufferedDesktopAccountHealthReport struct {
|
||||
ReceivedAt time.Time `json:"received_at"`
|
||||
}
|
||||
|
||||
func (s *DesktopAccountService) ListByUser(ctx context.Context, client *repository.DesktopClient) ([]DesktopAccountView, error) {
|
||||
func (s *DesktopAccountService) ListByClient(ctx context.Context, client *repository.DesktopClient) ([]DesktopAccountView, error) {
|
||||
if client == nil {
|
||||
return nil, response.ErrUnauthorized(40108, "desktop_client_missing", "desktop client context is required")
|
||||
}
|
||||
|
||||
rows, err := s.repo.ListByUser(ctx, client.WorkspaceID, client.UserID)
|
||||
rows, err := s.repo.ListByClient(ctx, client.WorkspaceID, client.ID)
|
||||
if err != nil {
|
||||
return nil, response.ErrInternal(50081, "desktop_accounts_query_failed", "failed to list desktop accounts")
|
||||
}
|
||||
@@ -177,7 +177,7 @@ func (s *DesktopAccountService) Upsert(ctx context.Context, client *repository.D
|
||||
}
|
||||
|
||||
if req.IfSyncVersion != nil {
|
||||
existing, err := s.repo.GetByIdentity(ctx, client.WorkspaceID, req.Platform, req.PlatformUID)
|
||||
existing, err := s.repo.GetByIdentity(ctx, client.WorkspaceID, client.ID, req.Platform, req.PlatformUID)
|
||||
if err != nil && !errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, response.ErrInternal(50082, "desktop_account_lookup_failed", "failed to inspect desktop account before upsert")
|
||||
}
|
||||
@@ -346,6 +346,7 @@ func (s *DesktopAccountService) Patch(ctx context.Context, client *repository.De
|
||||
VerifiedAt: req.VerifiedAt,
|
||||
Tags: req.Tags,
|
||||
IfSyncVersion: req.IfSyncVersion,
|
||||
ClientID: client.ID,
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
@@ -363,7 +364,7 @@ func (s *DesktopAccountService) Tombstone(ctx context.Context, client *repositor
|
||||
return nil, response.ErrUnauthorized(40108, "desktop_client_missing", "desktop client context is required")
|
||||
}
|
||||
|
||||
account, err := s.repo.Tombstone(ctx, desktopID, client.WorkspaceID, ifSyncVersion)
|
||||
account, err := s.repo.Tombstone(ctx, desktopID, client.WorkspaceID, client.ID, ifSyncVersion)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, response.ErrConflict(40982, "desktop_account_sync_conflict", "desktop account sync version conflict")
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -192,16 +192,18 @@ SELECT
|
||||
updated_at
|
||||
FROM platform_accounts
|
||||
WHERE workspace_id = $1
|
||||
AND platform_id = $2
|
||||
AND platform_uid = $3
|
||||
AND client_id = $2
|
||||
AND platform_id = $3
|
||||
AND platform_uid = $4
|
||||
AND deleted_at IS NULL
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
type GetDesktopAccountByIdentityParams struct {
|
||||
WorkspaceID int64 `json:"workspace_id"`
|
||||
PlatformID string `json:"platform_id"`
|
||||
PlatformUid string `json:"platform_uid"`
|
||||
WorkspaceID int64 `json:"workspace_id"`
|
||||
ClientID pgtype.UUID `json:"client_id"`
|
||||
PlatformID string `json:"platform_id"`
|
||||
PlatformUid string `json:"platform_uid"`
|
||||
}
|
||||
|
||||
type GetDesktopAccountByIdentityRow struct {
|
||||
@@ -226,7 +228,12 @@ type GetDesktopAccountByIdentityRow struct {
|
||||
}
|
||||
|
||||
func (q *Queries) GetDesktopAccountByIdentity(ctx context.Context, arg GetDesktopAccountByIdentityParams) (GetDesktopAccountByIdentityRow, error) {
|
||||
row := q.db.QueryRow(ctx, getDesktopAccountByIdentity, arg.WorkspaceID, arg.PlatformID, arg.PlatformUid)
|
||||
row := q.db.QueryRow(ctx, getDesktopAccountByIdentity,
|
||||
arg.WorkspaceID,
|
||||
arg.ClientID,
|
||||
arg.PlatformID,
|
||||
arg.PlatformUid,
|
||||
)
|
||||
var i GetDesktopAccountByIdentityRow
|
||||
err := row.Scan(
|
||||
&i.DesktopID,
|
||||
@@ -251,6 +258,98 @@ func (q *Queries) GetDesktopAccountByIdentity(ctx context.Context, arg GetDeskto
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listDesktopAccountsByClient = `-- 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 = $1
|
||||
AND client_id = $2
|
||||
AND deleted_at IS NULL
|
||||
ORDER BY platform_id, display_name, created_at DESC
|
||||
`
|
||||
|
||||
type ListDesktopAccountsByClientParams struct {
|
||||
WorkspaceID int64 `json:"workspace_id"`
|
||||
ClientID pgtype.UUID `json:"client_id"`
|
||||
}
|
||||
|
||||
type ListDesktopAccountsByClientRow struct {
|
||||
DesktopID pgtype.UUID `json:"desktop_id"`
|
||||
TenantID int64 `json:"tenant_id"`
|
||||
WorkspaceID int64 `json:"workspace_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
ClientID pgtype.UUID `json:"client_id"`
|
||||
PlatformID string `json:"platform_id"`
|
||||
PlatformUid string `json:"platform_uid"`
|
||||
AccountFingerprint pgtype.Text `json:"account_fingerprint"`
|
||||
DisplayName string `json:"display_name"`
|
||||
AvatarUrl pgtype.Text `json:"avatar_url"`
|
||||
Health string `json:"health"`
|
||||
VerifiedAt pgtype.Timestamptz `json:"verified_at"`
|
||||
Tags []byte `json:"tags"`
|
||||
SyncVersion int64 `json:"sync_version"`
|
||||
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
|
||||
DeleteRequestedAt pgtype.Timestamptz `json:"delete_requested_at"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListDesktopAccountsByClient(ctx context.Context, arg ListDesktopAccountsByClientParams) ([]ListDesktopAccountsByClientRow, error) {
|
||||
rows, err := q.db.Query(ctx, listDesktopAccountsByClient, arg.WorkspaceID, arg.ClientID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []ListDesktopAccountsByClientRow
|
||||
for rows.Next() {
|
||||
var i ListDesktopAccountsByClientRow
|
||||
if err := rows.Scan(
|
||||
&i.DesktopID,
|
||||
&i.TenantID,
|
||||
&i.WorkspaceID,
|
||||
&i.UserID,
|
||||
&i.ClientID,
|
||||
&i.PlatformID,
|
||||
&i.PlatformUid,
|
||||
&i.AccountFingerprint,
|
||||
&i.DisplayName,
|
||||
&i.AvatarUrl,
|
||||
&i.Health,
|
||||
&i.VerifiedAt,
|
||||
&i.Tags,
|
||||
&i.SyncVersion,
|
||||
&i.DeletedAt,
|
||||
&i.DeleteRequestedAt,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listDesktopAccountsByUser = `-- name: ListDesktopAccountsByUser :many
|
||||
SELECT
|
||||
desktop_id,
|
||||
@@ -437,7 +536,8 @@ SET display_name = COALESCE($1::text, display_name),
|
||||
updated_at = now()
|
||||
WHERE desktop_id = $6
|
||||
AND workspace_id = $7
|
||||
AND sync_version = $8
|
||||
AND client_id = $8
|
||||
AND sync_version = $9
|
||||
AND deleted_at IS NULL
|
||||
RETURNING
|
||||
desktop_id,
|
||||
@@ -468,6 +568,7 @@ type PatchDesktopAccountParams struct {
|
||||
Tags []byte `json:"tags"`
|
||||
DesktopID pgtype.UUID `json:"desktop_id"`
|
||||
WorkspaceID int64 `json:"workspace_id"`
|
||||
ClientID pgtype.UUID `json:"client_id"`
|
||||
IfSyncVersion int64 `json:"if_sync_version"`
|
||||
}
|
||||
|
||||
@@ -501,6 +602,7 @@ func (q *Queries) PatchDesktopAccount(ctx context.Context, arg PatchDesktopAccou
|
||||
arg.Tags,
|
||||
arg.DesktopID,
|
||||
arg.WorkspaceID,
|
||||
arg.ClientID,
|
||||
arg.IfSyncVersion,
|
||||
)
|
||||
var i PatchDesktopAccountRow
|
||||
@@ -534,7 +636,8 @@ SET deleted_at = now(),
|
||||
updated_at = now()
|
||||
WHERE desktop_id = $1
|
||||
AND workspace_id = $2
|
||||
AND sync_version = $3
|
||||
AND client_id = $3
|
||||
AND sync_version = $4
|
||||
AND deleted_at IS NULL
|
||||
RETURNING
|
||||
desktop_id,
|
||||
@@ -560,6 +663,7 @@ RETURNING
|
||||
type TombstoneDesktopAccountParams struct {
|
||||
DesktopID pgtype.UUID `json:"desktop_id"`
|
||||
WorkspaceID int64 `json:"workspace_id"`
|
||||
ClientID pgtype.UUID `json:"client_id"`
|
||||
IfSyncVersion int64 `json:"if_sync_version"`
|
||||
}
|
||||
|
||||
@@ -585,7 +689,12 @@ type TombstoneDesktopAccountRow struct {
|
||||
}
|
||||
|
||||
func (q *Queries) TombstoneDesktopAccount(ctx context.Context, arg TombstoneDesktopAccountParams) (TombstoneDesktopAccountRow, error) {
|
||||
row := q.db.QueryRow(ctx, tombstoneDesktopAccount, arg.DesktopID, arg.WorkspaceID, arg.IfSyncVersion)
|
||||
row := q.db.QueryRow(ctx, tombstoneDesktopAccount,
|
||||
arg.DesktopID,
|
||||
arg.WorkspaceID,
|
||||
arg.ClientID,
|
||||
arg.IfSyncVersion,
|
||||
)
|
||||
var i TombstoneDesktopAccountRow
|
||||
err := row.Scan(
|
||||
&i.DesktopID,
|
||||
@@ -651,7 +760,7 @@ VALUES (
|
||||
$15,
|
||||
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,
|
||||
@@ -669,6 +778,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 (
|
||||
$16::bigint IS NULL
|
||||
OR platform_accounts.sync_version = $16::bigint
|
||||
|
||||
@@ -115,6 +115,7 @@ type Querier interface {
|
||||
ListArticles(ctx context.Context, arg ListArticlesParams) ([]ListArticlesRow, error)
|
||||
ListBrands(ctx context.Context, tenantID int64) ([]ListBrandsRow, error)
|
||||
ListCompetitors(ctx context.Context, arg ListCompetitorsParams) ([]ListCompetitorsRow, error)
|
||||
ListDesktopAccountsByClient(ctx context.Context, arg ListDesktopAccountsByClientParams) ([]ListDesktopAccountsByClientRow, error)
|
||||
ListDesktopAccountsByUser(ctx context.Context, arg ListDesktopAccountsByUserParams) ([]ListDesktopAccountsByUserRow, error)
|
||||
ListDesktopClientsByWorkspace(ctx context.Context, workspaceID int64) ([]DesktopClient, error)
|
||||
ListImageAssets(ctx context.Context, arg ListImageAssetsParams) ([]ListImageAssetsRow, error)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -34,7 +34,7 @@ func (h *DesktopAccountHandler) List(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := h.svc.ListByUser(c.Request.Context(), MustDesktopClient(c.Request.Context()))
|
||||
data, err := h.svc.ListByClient(c.Request.Context(), MustDesktopClient(c.Request.Context()))
|
||||
if err != nil {
|
||||
response.Error(c, err)
|
||||
return
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
DROP INDEX IF EXISTS uniq_platform_accounts_workspace_identity_active;
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uniq_platform_accounts_workspace_identity_active
|
||||
ON platform_accounts (workspace_id, platform_id, platform_uid)
|
||||
WHERE deleted_at IS NULL;
|
||||
@@ -0,0 +1,5 @@
|
||||
DROP INDEX IF EXISTS uniq_platform_accounts_workspace_identity_active;
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uniq_platform_accounts_workspace_identity_active
|
||||
ON platform_accounts (workspace_id, client_id, platform_id, platform_uid)
|
||||
WHERE deleted_at IS NULL;
|
||||
Reference in New Issue
Block a user