Files
supabase/apps/studio/components/interfaces/Support/dashboard-logs.ts
T
CharisandJoshen Lim d8f7cc0d57 feat(support form): attach dashboard logs (#39539)
* o11y: mirror and sanitize breadcrumbs

Mirror Sentry breadcrumbs as the basis for our own support logging. Also
adds more sanitization to breadcrumbs.

* feat(support form): toggle for attaching dashboard  logs

Add a toggle to the support form when the category is "Dashboard bug",
to attach recent dashboard logs. Users can preview the attached logs and
opt out.

* feat(support links): dedicated support link component

Add a new component for support links, which:
- Uses the serializer for support link params to ensure
serialization/deserialization pairs correctly
- Snapshots breadcrumbs so the attached log on the support form will be
cut off at the support link click (otherwise we will get support form
actions cluttering up the log)

* tests(support form): extend timeout on flaky test

* Minor clean up

* fix(support form): allow url to specifically indicate no specified project

* minor nits

* Fix tests

* Fix tests

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2025-10-22 08:57:49 -04:00

78 lines
2.3 KiB
TypeScript

import * as Sentry from '@sentry/nextjs'
import { SupportCategories } from '@supabase/shared-types/out/constants'
import type {
GenerateAttachmentURLsData,
GenerateAttachmentURLsVariables,
} from 'data/support/generate-attachment-urls-mutation'
import { getMirroredBreadcrumbs, getOwnershipOfBreadcrumbSnapshot } from 'lib/breadcrumbs'
import { uuidv4 } from 'lib/helpers'
import { sanitizeArrayOfObjects } from 'lib/sanitize'
import { createSupportStorageClient } from './support-storage-client'
import type { ExtendedSupportCategories } from './Support.constants'
export type DashboardBreadcrumb = Sentry.Breadcrumb
export const DASHBOARD_LOG_BUCKET = 'dashboard-logs'
export const DASHBOARD_LOG_CATEGORIES: ExtendedSupportCategories[] = [
SupportCategories.DASHBOARD_BUG,
]
export const getSanitizedBreadcrumbs = (): unknown[] => {
const breadcrumbs = getOwnershipOfBreadcrumbSnapshot() ?? getMirroredBreadcrumbs()
return sanitizeArrayOfObjects(breadcrumbs)
}
export const uploadDashboardLog = async ({
userId,
uploadDashboardLogFn,
}: {
userId: string | undefined
uploadDashboardLogFn: (
vars: GenerateAttachmentURLsVariables
) => Promise<GenerateAttachmentURLsData>
}): Promise<string[]> => {
if (!userId) {
console.error(
'[SupportForm > uploadDashboardLog] Cannot upload dashboard log: user ID is undefined'
)
return []
}
const sanitized = getSanitizedBreadcrumbs()
if (sanitized.length === 0) return []
try {
const supportStorageClient = createSupportStorageClient()
const objectKey = `${userId}/${uuidv4()}.json`
const body = new Blob([JSON.stringify(sanitized, null, 2)], {
type: 'application/json',
})
const { error: uploadError } = await supportStorageClient.storage
.from(DASHBOARD_LOG_BUCKET)
.upload(objectKey, body, {
cacheControl: '3600',
contentType: 'application/json',
upsert: false,
})
if (uploadError) {
console.error(
'[SupportForm > uploadDashboardLog] Failed to upload dashboard log to support storage bucket',
uploadError
)
return []
}
return uploadDashboardLogFn({
bucket: DASHBOARD_LOG_BUCKET,
filenames: [objectKey],
})
} catch (error) {
console.error('[SupportForm] Unexpected error uploading dashboard log', error)
return []
}
}