Log state changes
This commit is contained in:
@ -175,6 +175,12 @@ export const exportJob = async (jobData: ExportJobData) => {
|
||||
userId
|
||||
)
|
||||
|
||||
await saveExport(userId, {
|
||||
id: exportId,
|
||||
state: TaskState.Running,
|
||||
totalItems: itemCount,
|
||||
})
|
||||
|
||||
logger.info(`exporting ${itemCount} items...`, {
|
||||
userId,
|
||||
})
|
||||
@ -226,6 +232,11 @@ export const exportJob = async (jobData: ExportJobData) => {
|
||||
// fetch data from the database
|
||||
const batchSize = 20
|
||||
for (cursor = 0; cursor < itemCount; cursor += batchSize) {
|
||||
logger.info(`export extracting ${cursor} of ${itemCount}`, {
|
||||
userId,
|
||||
exportId,
|
||||
})
|
||||
|
||||
const items = await searchLibraryItems(
|
||||
{
|
||||
from: cursor,
|
||||
@ -242,6 +253,10 @@ export const exportJob = async (jobData: ExportJobData) => {
|
||||
// write data to the csv file
|
||||
if (size > 0) {
|
||||
await uploadToBucket(userId, items, cursor, size, archive)
|
||||
await saveExport(userId, {
|
||||
id: exportId,
|
||||
processedItems: cursor,
|
||||
})
|
||||
} else {
|
||||
break
|
||||
}
|
||||
@ -264,7 +279,7 @@ export const exportJob = async (jobData: ExportJobData) => {
|
||||
// generate a temporary signed url for the zip file
|
||||
const [signedUrl] = await file.getSignedUrl({
|
||||
action: 'read',
|
||||
expires: Date.now() + 48 * 60 * 60 * 1000, // 48 hours
|
||||
expires: Date.now() + 168 * 60 * 60 * 1000, // one week
|
||||
})
|
||||
|
||||
logger.info('signed url for export:', {
|
||||
@ -275,6 +290,8 @@ export const exportJob = async (jobData: ExportJobData) => {
|
||||
await saveExport(userId, {
|
||||
id: exportId,
|
||||
state: TaskState.Succeeded,
|
||||
signedUrl,
|
||||
processedItems: itemCount,
|
||||
})
|
||||
|
||||
const job = await sendExportJobEmail(userId, 'completed', signedUrl)
|
||||
|
||||
@ -1,11 +1,38 @@
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { apiFetcher } from './networkHelpers'
|
||||
import { TaskState } from './mutations/exportToIntegrationMutation'
|
||||
|
||||
type Export = {
|
||||
id: string
|
||||
state: TaskState
|
||||
createdAt: string
|
||||
signedUrl: string
|
||||
}
|
||||
|
||||
type ExportsResponse = {
|
||||
exports: Export[]
|
||||
}
|
||||
|
||||
export const createExport = async (): Promise<boolean> => {
|
||||
try {
|
||||
const response = await apiFetcher(`/api/export/`)
|
||||
console.log('RESPONSE: ', response)
|
||||
if ('error' in (response as any)) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
console.log('error scheduling export. ')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export function useGetExports() {
|
||||
return useQuery({
|
||||
queryKey: ['exports'],
|
||||
queryFn: async () => {
|
||||
const response = (await apiFetcher(`/api/export/list`)) as ExportsResponse
|
||||
return response.exports
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Button } from '../../components/elements/Button'
|
||||
import {
|
||||
Box,
|
||||
HStack,
|
||||
SpanBox,
|
||||
VStack,
|
||||
} from '../../components/elements/LayoutPrimitives'
|
||||
@ -27,7 +28,13 @@ import { useGetViewerQuery } from '../../lib/networking/queries/useGetViewerQuer
|
||||
import { useValidateUsernameQuery } from '../../lib/networking/queries/useValidateUsernameQuery'
|
||||
import { applyStoredTheme } from '../../lib/themeUpdater'
|
||||
import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers'
|
||||
import { createExport } from '../../lib/networking/useCreateExport'
|
||||
import {
|
||||
createExport,
|
||||
useGetExports,
|
||||
} from '../../lib/networking/useCreateExport'
|
||||
import { TaskState } from '../../lib/networking/mutations/exportToIntegrationMutation'
|
||||
import { timeAgo } from '../../lib/textFormatting'
|
||||
import { Download, DownloadSimple } from '@phosphor-icons/react'
|
||||
|
||||
const ACCOUNT_LIMIT = 50_000
|
||||
|
||||
@ -475,6 +482,8 @@ export default function Account(): JSX.Element {
|
||||
}
|
||||
|
||||
const ExportSection = (): JSX.Element => {
|
||||
const { data: recentExports } = useGetExports()
|
||||
console.log('recentExports: ', recentExports)
|
||||
const doExport = useCallback(async () => {
|
||||
const result = await createExport()
|
||||
if (result) {
|
||||
@ -502,6 +511,12 @@ const ExportSection = (): JSX.Element => {
|
||||
you should receive an email with a link to your data within an hour. The
|
||||
download link will be available for 24 hours.
|
||||
</StyledText>
|
||||
<StyledText style="footnote" css={{ mt: '10px', mb: '20px' }}>
|
||||
If you do not receive your completed export within 24hrs please contact{' '}
|
||||
<a href="mailto:feedback@omnivore.app">
|
||||
Contact us via email
|
||||
</a>
|
||||
</StyledText>
|
||||
<Button
|
||||
style="ctaDarkYellow"
|
||||
onClick={(event) => {
|
||||
@ -512,6 +527,33 @@ const ExportSection = (): JSX.Element => {
|
||||
>
|
||||
Export Data
|
||||
</Button>
|
||||
|
||||
{recentExports && (
|
||||
<VStack css={{ width: '100% ', mt: '20px' }}>
|
||||
<StyledLabel>Recent exports</StyledLabel>
|
||||
{recentExports.map((item) => {
|
||||
return (
|
||||
<HStack
|
||||
key={item.id}
|
||||
css={{ width: '100% ' }}
|
||||
distribution="start"
|
||||
>
|
||||
<SpanBox css={{ width: '180px' }} title={item.createdAt}>
|
||||
{timeAgo(item.createdAt)}
|
||||
</SpanBox>
|
||||
<SpanBox>{item.state}</SpanBox>
|
||||
{item.signedUrl && (
|
||||
<SpanBox css={{ marginLeft: 'auto' }}>
|
||||
<a href={item.signedUrl} target="_blank" rel="noreferrer">
|
||||
Download
|
||||
</a>
|
||||
</SpanBox>
|
||||
)}
|
||||
</HStack>
|
||||
)
|
||||
})}
|
||||
</VStack>
|
||||
)}
|
||||
</VStack>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user