From 04769dec7821467572c2460df31f826f897e54e4 Mon Sep 17 00:00:00 2001 From: liangxu Date: Fri, 26 Jun 2026 21:04:38 +0800 Subject: [PATCH] fix publish record deletion --- .../src/views/PublishManagementView.vue | 34 ++++ apps/desktop-client/src/main/bootstrap.ts | 4 + .../src/main/transport/api-client.ts | 9 ++ apps/desktop-client/src/preload/bridge.ts | 4 + apps/desktop-client/src/renderer/env.d.ts | 1 + .../renderer/views/PublishManagementView.vue | 49 ++++++ .../tenant/app/desktop_task_service.go | 29 ++++ server/internal/tenant/app/media_service.go | 149 ++++++++++++------ .../tenant/app/publish_record_support_test.go | 17 +- .../tenant/transport/media_handler.go | 14 ++ server/internal/tenant/transport/router.go | 1 + .../internal/tenant/transport/router_test.go | 1 + 12 files changed, 259 insertions(+), 53 deletions(-) diff --git a/apps/admin-web/src/views/PublishManagementView.vue b/apps/admin-web/src/views/PublishManagementView.vue index d416a89..1b727b7 100644 --- a/apps/admin-web/src/views/PublishManagementView.vue +++ b/apps/admin-web/src/views/PublishManagementView.vue @@ -2,6 +2,7 @@ import { ClockCircleOutlined, CopyOutlined, + DeleteOutlined, DesktopOutlined, ExportOutlined, ReloadOutlined, @@ -193,6 +194,7 @@ const appliedTitle = ref('') const copiedErrorTaskId = ref(null) const retryingTaskId = ref(null) const cancelingTaskId = ref(null) +const deletingRecordId = ref(null) const manualRefreshing = ref(false) const recordsQuery = useQuery({ @@ -370,6 +372,19 @@ async function cancelTask(record: PublishRecordItem): Promise { } } +async function deleteRecord(record: PublishRecordItem): Promise { + deletingRecordId.value = record.id + try { + await publishRecordsApi.remove(record.recordId) + message.success('发文记录已删除') + await recordsQuery.refetch() + } catch (error) { + message.error(formatError(error)) + } finally { + deletingRecordId.value = null + } +} + async function copyErrorMessage(record: PublishRecordItem): Promise { if (!record.errorMessage) { return @@ -622,6 +637,25 @@ async function copyErrorMessage(record: PublishRecordItem): Promise { + + + + + + + diff --git a/apps/desktop-client/src/main/bootstrap.ts b/apps/desktop-client/src/main/bootstrap.ts index b8d8a64..950f615 100644 --- a/apps/desktop-client/src/main/bootstrap.ts +++ b/apps/desktop-client/src/main/bootstrap.ts @@ -78,6 +78,7 @@ import { import { cancelDesktopTask, checkDesktopClientRelease, + deleteDesktopPublishRecord, initTransport, listDesktopPublishTasks, resolveDesktopClientReleaseDownloadURL, @@ -1246,6 +1247,9 @@ function registerBridgeHandlers(): void { safeHandle('desktop:cancel-publish-task', async (_event, taskId: string) => { return cancelDesktopTask(taskId, { reason: 'user_cancelled_publish' }) }) + safeHandle('desktop:delete-publish-record', async (_event, recordId: number) => { + return deleteDesktopPublishRecord(recordId) + }) safeHandle('desktop:open-external-url', async (_event, url: string) => { if (!isSafeExternalUrl(url)) { throw new Error('unsupported_external_url') diff --git a/apps/desktop-client/src/main/transport/api-client.ts b/apps/desktop-client/src/main/transport/api-client.ts index 244ce95..c7df3ae 100644 --- a/apps/desktop-client/src/main/transport/api-client.ts +++ b/apps/desktop-client/src/main/transport/api-client.ts @@ -9,6 +9,7 @@ import type { CancelDesktopTaskRequest, CompleteDesktopTaskRequest, CreatePublishJobResponse, + DeletePublishRecordResponse, DesktopAccountInfo, DesktopArticleContent, DesktopClientHeartbeatRequest, @@ -678,6 +679,14 @@ export async function retryDesktopPublishTask(taskId: string): Promise { + return desktopDelete( + `/api/desktop/publish-records/${encodeURIComponent(String(recordId))}`, + ) +} + export async function upsertDesktopAccount( payload: UpsertDesktopAccountRequest, ): Promise { diff --git a/apps/desktop-client/src/preload/bridge.ts b/apps/desktop-client/src/preload/bridge.ts index 545bfdb..24914ce 100644 --- a/apps/desktop-client/src/preload/bridge.ts +++ b/apps/desktop-client/src/preload/bridge.ts @@ -226,6 +226,10 @@ const desktopBridge = { ipcRenderer.invoke('desktop:retry-publish-task', taskId) as Promise, cancelPublishTask: (taskId: string) => ipcRenderer.invoke('desktop:cancel-publish-task', taskId) as Promise, + deletePublishRecord: (recordId: number) => + ipcRenderer.invoke('desktop:delete-publish-record', recordId) as Promise<{ + deleted: boolean + }>, openExternalUrl: (url: string) => ipcRenderer.invoke('desktop:open-external-url', url) as Promise, openSettingsWindow: () => ipcRenderer.invoke('desktop:open-settings-window') as Promise, diff --git a/apps/desktop-client/src/renderer/env.d.ts b/apps/desktop-client/src/renderer/env.d.ts index cfb6aa0..e1f2fff 100644 --- a/apps/desktop-client/src/renderer/env.d.ts +++ b/apps/desktop-client/src/renderer/env.d.ts @@ -133,6 +133,7 @@ declare global { ): Promise retryPublishTask(taskId: string): Promise cancelPublishTask(taskId: string): Promise + deletePublishRecord(recordId: number): Promise<{ deleted: boolean }> openExternalUrl(url: string): Promise openSettingsWindow(): Promise getAppSettings(): Promise diff --git a/apps/desktop-client/src/renderer/views/PublishManagementView.vue b/apps/desktop-client/src/renderer/views/PublishManagementView.vue index 86b40b0..e97386c 100644 --- a/apps/desktop-client/src/renderer/views/PublishManagementView.vue +++ b/apps/desktop-client/src/renderer/views/PublishManagementView.vue @@ -2,6 +2,7 @@ import { ClockCircleOutlined, CopyOutlined, + DeleteOutlined, DesktopOutlined, ExportOutlined, ReloadOutlined, @@ -36,6 +37,7 @@ const publishPlatformIndex: Record(null) const copiedErrorTaskId = ref(null) const retryingTaskId = ref(null) const cancelingTaskId = ref(null) +const deletingTaskId = ref(null) function notifySuccess(message: string) { notification.success({ @@ -629,6 +632,30 @@ async function cancelTask(record: PublishTaskItem) { } } +async function deleteRecord(record: PublishTaskItem) { + if (!record.publishRecordId) { + return + } + + deletingTaskId.value = record.id + try { + await window.desktopBridge.app.deletePublishRecord(record.publishRecordId) + notifySuccess('发文记录已删除') + await refreshTasks(currentPage.value) + } catch (err) { + notifyActionError( + normalizeTaskErrorMessage(unwrapClientErrorMessage(err, '删除发文记录失败')) ?? + '删除发文记录失败', + ) + } finally { + deletingTaskId.value = null + } +} + +function canDeleteRecord(record: PublishTaskItem): boolean { + return record.publishRecordId !== null +} + async function copyTextToClipboard(text: string): Promise { if (navigator.clipboard?.writeText) { await navigator.clipboard.writeText(text) @@ -741,6 +768,7 @@ const publishTasks = computed(() => const articleId = extractNumber(payload.article_id) ?? extractNumber(nestedContentRef?.article_id ?? nestedContentRef?.id) + const publishRecordId = extractNumber(payload.publish_record_id) const pendingTasks = taskPage.value?.items.filter((item) => isPendingStatus(normalizePublishTaskStatus(item.status)), @@ -782,6 +810,7 @@ const publishTasks = computed(() => return { id: task.id, + publishRecordId, title: extractString(payload.title) ?? `发布任务 · ${translatePlatform(task.platform)}`, articleId, platform: task.platform, @@ -1096,6 +1125,26 @@ const tableScroll = { x: 1080 } as const + + + + + + + diff --git a/server/internal/tenant/app/desktop_task_service.go b/server/internal/tenant/app/desktop_task_service.go index 0951ff9..5982aec 100644 --- a/server/internal/tenant/app/desktop_task_service.go +++ b/server/internal/tenant/app/desktop_task_service.go @@ -759,6 +759,20 @@ func (s *DesktopTaskService) leaseNextQueuedPublishTask( AND dt.kind = 'publish' AND dt.status = 'queued' AND dt.attempts < $4 + AND ( + NOT (dt.payload ? 'publish_record_id') + OR EXISTS ( + SELECT 1 + FROM publish_records pr + WHERE pr.tenant_id = dt.tenant_id + AND pr.id = CASE + WHEN (dt.payload->>'publish_record_id') ~ '^[0-9]+$' + THEN (dt.payload->>'publish_record_id')::bigint + ELSE NULL + END + AND pr.deleted_at IS NULL + ) + ) AND EXISTS ( SELECT 1 FROM platform_accounts AS pa @@ -866,6 +880,21 @@ func leaseQueuedDesktopTaskByIDSQL() string { AND dt.workspace_id = $2 AND dt.status = 'queued' AND (dt.kind <> 'publish' OR dt.attempts < $7) + AND ( + dt.kind <> 'publish' + OR NOT (dt.payload ? 'publish_record_id') + OR EXISTS ( + SELECT 1 + FROM publish_records pr + WHERE pr.tenant_id = dt.tenant_id + AND pr.id = CASE + WHEN (dt.payload->>'publish_record_id') ~ '^[0-9]+$' + THEN (dt.payload->>'publish_record_id')::bigint + ELSE NULL + END + AND pr.deleted_at IS NULL + ) + ) AND ( dt.kind <> 'publish' OR EXISTS ( diff --git a/server/internal/tenant/app/media_service.go b/server/internal/tenant/app/media_service.go index c7d3264..0615dcc 100644 --- a/server/internal/tenant/app/media_service.go +++ b/server/internal/tenant/app/media_service.go @@ -13,6 +13,7 @@ import ( "github.com/geo-platform/tenant-api/internal/shared/auth" "github.com/geo-platform/tenant-api/internal/shared/response" + "github.com/geo-platform/tenant-api/internal/tenant/repository" ) type MediaService struct { @@ -82,11 +83,15 @@ type DeletePublishRecordResponse struct { Deleted bool `json:"deleted"` } +type publishRecordDeleteOwner struct { + TenantID int64 + WorkspaceID int64 + UserID int64 + Source string +} + const visiblePublishRecordWhereSQL = ` - AND ( - pr.deleted_at IS NULL - OR pr.status IN ('success', 'published', 'publish_success', 'failed', 'publish_failed', 'cancelled', 'canceled') - ) + AND pr.deleted_at IS NULL ` func (s *MediaService) ListPlatforms(ctx context.Context) ([]MediaPlatformResponse, error) { @@ -327,10 +332,7 @@ func (s *MediaService) countPublishRecordsByStatuses( AND pb.tenant_id = pr.tenant_id AND pb.initiator_user_id = $2 ) - AND ( - pr.deleted_at IS NULL - OR pr.status IN ('success', 'published', 'publish_success', 'failed', 'publish_failed', 'cancelled', 'canceled') - ) +`+visiblePublishRecordWhereSQL+` AND pr.status = ANY($3) AND ( $4 = '' @@ -378,10 +380,7 @@ func (s *MediaService) listArticlePublishRecords(ctx context.Context, tenantID, AND pr.article_id = $2 AND a.brand_id = $3 AND a.deleted_at IS NULL - AND ( - pr.deleted_at IS NULL - OR pr.status IN ('success', 'published', 'publish_success', 'failed', 'publish_failed', 'cancelled', 'canceled') - ) +`+visiblePublishRecordWhereSQL+` ORDER BY pr.created_at DESC, pr.id DESC `, tenantID, articleID, brandID) if err != nil { @@ -433,7 +432,35 @@ func (s *MediaService) DeletePublishRecord(ctx context.Context, recordID int64) return nil, response.ErrBadRequest(40001, "invalid_publish_record_id", "publish record id must be a positive number") } actor := auth.MustActor(ctx) - if actor.TenantID == 0 || actor.UserID == 0 { + workspaceID := auth.CurrentWorkspaceID(ctx) + if actor.TenantID == 0 || actor.UserID == 0 || workspaceID == 0 { + return nil, response.ErrUnauthorized(40132, "workspace_scope_missing", "workspace context is required") + } + return s.deletePublishRecordForOwner(ctx, recordID, publishRecordDeleteOwner{ + TenantID: actor.TenantID, + WorkspaceID: workspaceID, + UserID: actor.UserID, + Source: "tenant_web", + }) +} + +func (s *MediaService) DeleteDesktopPublishRecord(ctx context.Context, client *repository.DesktopClient, recordID int64) (*DeletePublishRecordResponse, error) { + if recordID <= 0 { + return nil, response.ErrBadRequest(40001, "invalid_publish_record_id", "publish record id must be a positive number") + } + if client == nil || client.TenantID == 0 || client.WorkspaceID == 0 || client.UserID == 0 { + return nil, response.ErrUnauthorized(40108, "desktop_client_missing", "desktop client context is required") + } + return s.deletePublishRecordForOwner(ctx, recordID, publishRecordDeleteOwner{ + TenantID: client.TenantID, + WorkspaceID: client.WorkspaceID, + UserID: client.UserID, + Source: "desktop_client", + }) +} + +func (s *MediaService) deletePublishRecordForOwner(ctx context.Context, recordID int64, owner publishRecordDeleteOwner) (*DeletePublishRecordResponse, error) { + if owner.TenantID == 0 || owner.WorkspaceID == 0 || owner.UserID == 0 { return nil, response.ErrUnauthorized(40132, "workspace_scope_missing", "workspace context is required") } @@ -445,17 +472,16 @@ func (s *MediaService) DeletePublishRecord(ctx context.Context, recordID int64) var publishBatchID int64 var articleID int64 - var publishStatus string cancelErrorJSONBytes, err := marshalOptionalJSON(map[string]any{ "reason": "publish_record_deleted", - "source": "tenant_web", + "source": owner.Source, }) if err != nil { return nil, response.ErrInternal(50044, "publish_record_delete_payload_failed", "failed to delete publish record") } cancelErrorJSON := string(cancelErrorJSONBytes) err = tx.QueryRow(ctx, ` - SELECT pr.publish_batch_id, pr.article_id, pr.status + SELECT pr.publish_batch_id, pr.article_id FROM publish_records pr JOIN articles a ON a.id = pr.article_id @@ -466,9 +492,44 @@ func (s *MediaService) DeletePublishRecord(ctx context.Context, recordID int64) WHERE pr.id = $1 AND pr.tenant_id = $2 AND pb.initiator_user_id = $3 + AND ( + ( + COALESCE(pr.target_type, 'platform_account') = 'platform_account' + AND EXISTS ( + SELECT 1 + FROM platform_accounts pa + WHERE pa.id = pr.platform_account_id + AND pa.tenant_id = pr.tenant_id + AND pa.workspace_id = $4 + ) + ) + OR ( + COALESCE(pr.target_type, 'platform_account') = 'enterprise_site' + AND EXISTS ( + SELECT 1 + FROM enterprise_site_connections esc + WHERE esc.id = pr.target_connection_id + AND esc.tenant_id = pr.tenant_id + AND esc.workspace_id = $4 + ) + ) + OR EXISTS ( + SELECT 1 + FROM desktop_tasks dt + JOIN desktop_publish_jobs j + ON j.desktop_id = dt.job_id + AND j.tenant_id = dt.tenant_id + AND j.workspace_id = dt.workspace_id + WHERE dt.tenant_id = pr.tenant_id + AND dt.workspace_id = $4 + AND dt.kind = 'publish' + AND dt.payload->>'publish_record_id' = pr.id::text + AND j.created_by_user_id = $3 + ) + ) AND pr.deleted_at IS NULL FOR UPDATE OF pr - `, recordID, actor.TenantID, actor.UserID).Scan(&publishBatchID, &articleID, &publishStatus) + `, recordID, owner.TenantID, owner.UserID, owner.WorkspaceID).Scan(&publishBatchID, &articleID) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, response.ErrNotFound(40435, "publish_record_not_found", "publish record not found") @@ -476,10 +537,6 @@ func (s *MediaService) DeletePublishRecord(ctx context.Context, recordID int64) return nil, response.ErrInternal(50044, "publish_record_delete_lookup_failed", "failed to delete publish record") } - if !publishRecordMayBeDiscarded(publishStatus) { - return nil, response.ErrConflict(40996, "publish_record_history_locked", "completed publish records are kept for retry and audit") - } - if tag, err := tx.Exec(ctx, ` UPDATE publish_records SET deleted_at = NOW(), @@ -487,30 +544,12 @@ func (s *MediaService) DeletePublishRecord(ctx context.Context, recordID int64) WHERE id = $1 AND tenant_id = $2 AND deleted_at IS NULL - `, recordID, actor.TenantID); err != nil { + `, recordID, owner.TenantID); err != nil { return nil, response.ErrInternal(50044, "publish_record_delete_failed", "failed to delete publish record") } else if tag.RowsAffected() == 0 { return nil, response.ErrNotFound(40435, "publish_record_not_found", "publish record not found") } - if publishRecordMayBeDiscarded(publishStatus) { - if _, err := tx.Exec(ctx, ` - UPDATE desktop_tasks - SET status = 'aborted', - error = $3::jsonb, - active_attempt_id = NULL, - lease_token_hash = NULL, - lease_expires_at = NULL, - updated_at = NOW() - WHERE tenant_id = $1 - AND kind = 'publish' - AND status = 'queued' - AND payload->>'publish_record_id' = $2::text - `, actor.TenantID, recordID, cancelErrorJSON); err != nil { - return nil, response.ErrInternal(50044, "publish_record_delete_cancel_task_failed", "failed to cancel queued publish task before deleting record") - } - } - batchStatus, err := recalculatePublishBatchStatus(ctx, tx, publishBatchID) if err != nil { return nil, err @@ -519,11 +558,11 @@ func (s *MediaService) DeletePublishRecord(ctx context.Context, recordID int64) UPDATE publish_batches SET status = $1, updated_at = NOW() WHERE id = $2 AND tenant_id = $3 - `, batchStatus, publishBatchID, actor.TenantID); err != nil { + `, batchStatus, publishBatchID, owner.TenantID); err != nil { return nil, response.ErrInternal(50054, "publish_batch_update_failed", "failed to update publish batch") } - articleStatus, err := recalculateArticlePublishStatus(ctx, tx, actor.TenantID, articleID) + articleStatus, err := recalculateArticlePublishStatus(ctx, tx, owner.TenantID, articleID) if err != nil { return nil, err } @@ -531,16 +570,38 @@ func (s *MediaService) DeletePublishRecord(ctx context.Context, recordID int64) UPDATE articles SET publish_status = $1, updated_at = NOW() WHERE id = $2 AND tenant_id = $3 AND deleted_at IS NULL - `, articleStatus, articleID, actor.TenantID); err != nil { + `, articleStatus, articleID, owner.TenantID); err != nil { return nil, response.ErrInternal(50055, "article_publish_update_failed", "failed to update article publish status") } if err := tx.Commit(ctx); err != nil { return nil, response.ErrInternal(50044, "publish_record_delete_commit_failed", "failed to delete publish record") } + + s.abortQueuedDesktopTasksForDeletedPublishRecord(ctx, owner, recordID, cancelErrorJSON) return &DeletePublishRecordResponse{Deleted: true}, nil } +func (s *MediaService) abortQueuedDesktopTasksForDeletedPublishRecord(ctx context.Context, owner publishRecordDeleteOwner, recordID int64, errorJSON string) { + if s == nil || s.pool == nil { + return + } + _, _ = s.pool.Exec(ctx, ` + UPDATE desktop_tasks + SET status = 'aborted', + error = $3::jsonb, + active_attempt_id = NULL, + lease_token_hash = NULL, + lease_expires_at = NULL, + updated_at = NOW() + WHERE tenant_id = $1 + AND workspace_id = $4 + AND kind = 'publish' + AND status = 'queued' + AND payload->>'publish_record_id' = $2::text + `, owner.TenantID, recordID, errorJSON, owner.WorkspaceID) +} + func publishBatchRequiresCover(accounts []platformAccountSeed) bool { for _, account := range accounts { if publishPlatformRequiresCover(account.PlatformID) { @@ -561,7 +622,7 @@ func publishPlatformRequiresCover(platformID string) bool { func publishRecordMayBeDiscarded(status string) bool { switch strings.TrimSpace(status) { - case "queued", "pending": + case "queued", "pending", "publishing", "running", "failed", "publish_failed", "success", "published", "publish_success", "cancelled", "canceled": return true default: return false diff --git a/server/internal/tenant/app/publish_record_support_test.go b/server/internal/tenant/app/publish_record_support_test.go index 8c42d26..fb7a46a 100644 --- a/server/internal/tenant/app/publish_record_support_test.go +++ b/server/internal/tenant/app/publish_record_support_test.go @@ -194,30 +194,29 @@ func TestDesktopTaskToPublishStatusMapsAbortToCancelled(t *testing.T) { } } -func TestPublishRecordMayBeDiscardedOnlyForQueuedRecords(t *testing.T) { - for _, status := range []string{"queued", "pending"} { +func TestPublishRecordMayBeDiscardedIncludesHistoryRecords(t *testing.T) { + for _, status := range []string{"queued", "pending", "publishing", "running", "failed", "success", "cancelled"} { if !publishRecordMayBeDiscarded(status) { t.Fatalf("publishRecordMayBeDiscarded(%q) = false, want true", status) } } - for _, status := range []string{"publishing", "running", "failed", "success", "cancelled"} { + for _, status := range []string{"", "archived"} { if publishRecordMayBeDiscarded(status) { t.Fatalf("publishRecordMayBeDiscarded(%q) = true, want false", status) } } } -func TestVisiblePublishRecordWhereIncludesDeletedTerminalFailures(t *testing.T) { +func TestVisiblePublishRecordWhereExcludesDeletedRecords(t *testing.T) { normalized := normalizeSQLWhitespace(visiblePublishRecordWhereSQL) if !strings.Contains(normalized, "pr.deleted_at IS NULL") { t.Fatalf("visible publish record filter must include active records: %s", normalized) } - if !strings.Contains(normalized, "'failed'") || !strings.Contains(normalized, "'publish_failed'") { - t.Fatalf("visible publish record filter must keep terminal failed history: %s", normalized) - } - if strings.Contains(normalized, "'queued'") || strings.Contains(normalized, "'publishing'") { - t.Fatalf("visible publish record filter must not resurrect deleted active records: %s", normalized) + for _, status := range []string{"'queued'", "'publishing'", "'failed'", "'publish_failed'", "'success'", "'cancelled'"} { + if strings.Contains(normalized, status) { + t.Fatalf("visible publish record filter must not resurrect deleted records by status %s: %s", status, normalized) + } } } diff --git a/server/internal/tenant/transport/media_handler.go b/server/internal/tenant/transport/media_handler.go index aa8e700..bb2d61c 100644 --- a/server/internal/tenant/transport/media_handler.go +++ b/server/internal/tenant/transport/media_handler.go @@ -94,3 +94,17 @@ func (h *MediaHandler) DeletePublishRecord(c *gin.Context) { } response.Success(c, data) } + +func (h *MediaHandler) DeleteDesktopPublishRecord(c *gin.Context) { + recordID, err := strconv.ParseInt(c.Param("id"), 10, 64) + if err != nil || recordID <= 0 { + response.Error(c, response.ErrBadRequest(40001, "invalid_publish_record_id", "publish record id must be a positive number")) + return + } + data, err := h.svc.DeleteDesktopPublishRecord(c.Request.Context(), MustDesktopClient(c.Request.Context()), recordID) + if err != nil { + response.Error(c, err) + return + } + response.Success(c, data) +} diff --git a/server/internal/tenant/transport/router.go b/server/internal/tenant/transport/router.go index 8d8adfd..52c8570 100644 --- a/server/internal/tenant/transport/router.go +++ b/server/internal/tenant/transport/router.go @@ -65,6 +65,7 @@ func RegisterRoutes(app *bootstrap.App) { desktopAuth.GET("/content/articles/:id", desktopContentHandler.Article) desktopAuth.GET("/content/assets/:token", publicAssets.ServeDesktop) desktopAuth.GET("/publish-tasks", desktopTaskHandler.ListPublish) + desktopAuth.DELETE("/publish-records/:id", mediaHandler.DeleteDesktopPublishRecord) desktopAuth.POST("/accounts/health-reports", desktopAccountHandler.ReportHealth) desktopAuth.POST("/accounts", desktopAccountHandler.Upsert) desktopAuth.PATCH("/accounts/:id", desktopAccountHandler.Patch) diff --git a/server/internal/tenant/transport/router_test.go b/server/internal/tenant/transport/router_test.go index 2be6d1e..49b9dbb 100644 --- a/server/internal/tenant/transport/router_test.go +++ b/server/internal/tenant/transport/router_test.go @@ -89,6 +89,7 @@ func TestDesktopClientRoutes_ClientTokenAuth(t *testing.T) { {http.MethodGet, "/api/desktop/dispatch"}, {http.MethodGet, "/api/desktop/accounts"}, {http.MethodGet, "/api/desktop/publish-tasks"}, + {http.MethodDelete, "/api/desktop/publish-records/:id"}, {http.MethodGet, "/api/desktop/content/assets/:token"}, {http.MethodPost, "/api/desktop/clients/offline"}, {http.MethodPost, "/api/desktop/tasks/lease"},