fix: export tx more logs

This commit is contained in:
Vasily Zubarev
2025-05-19 14:38:46 +02:00
parent b7475ca57d
commit d2ef3a088a
4 changed files with 52 additions and 58 deletions

View File

@@ -38,6 +38,9 @@ export async function GET(request: Request) {
// Process transactions in chunks to avoid memory issues // Process transactions in chunks to avoid memory issues
for (let i = 0; i < transactions.length; i += TRANSACTIONS_CHUNK_SIZE) { for (let i = 0; i < transactions.length; i += TRANSACTIONS_CHUNK_SIZE) {
const chunk = transactions.slice(i, i + TRANSACTIONS_CHUNK_SIZE) const chunk = transactions.slice(i, i + TRANSACTIONS_CHUNK_SIZE)
console.log(
`Processing transactions ${i + 1}-${Math.min(i + TRANSACTIONS_CHUNK_SIZE, transactions.length)} of ${transactions.length}`
)
for (const transaction of chunk) { for (const transaction of chunk) {
const row: Record<string, unknown> = {} const row: Record<string, unknown> = {}
@@ -90,8 +93,22 @@ export async function GET(request: Request) {
throw new Error("Failed to create zip folder") throw new Error("Failed to create zip folder")
} }
let totalFilesProcessed = 0
let totalFilesToProcess = 0
// First count total files to process
for (const transaction of transactions) {
const transactionFiles = await getFilesByTransactionId(transaction.id, user.id)
totalFilesToProcess += transactionFiles.length
}
console.log(`Starting to process ${totalFilesToProcess} files in total`)
for (let i = 0; i < transactions.length; i += FILES_CHUNK_SIZE) { for (let i = 0; i < transactions.length; i += FILES_CHUNK_SIZE) {
const chunk = transactions.slice(i, i + FILES_CHUNK_SIZE) const chunk = transactions.slice(i, i + FILES_CHUNK_SIZE)
console.log(
`Processing files for transactions ${i + 1}-${Math.min(i + FILES_CHUNK_SIZE, transactions.length)} of ${transactions.length}`
)
for (const transaction of chunk) { for (const transaction of chunk) {
const transactionFiles = await getFilesByTransactionId(transaction.id, user.id) const transactionFiles = await getFilesByTransactionId(transaction.id, user.id)
@@ -108,6 +125,9 @@ export async function GET(request: Request) {
for (const file of transactionFiles) { for (const file of transactionFiles) {
const fullFilePath = fullPathForFile(user, file) const fullFilePath = fullPathForFile(user, file)
if (await fileExists(fullFilePath)) { if (await fileExists(fullFilePath)) {
console.log(
`Processing file ${++totalFilesProcessed}/${totalFilesToProcess}: ${file.filename} for transaction ${transaction.id}`
)
const fileData = await fs.readFile(fullFilePath) const fileData = await fs.readFile(fullFilePath)
const fileExtension = path.extname(fullFilePath) const fileExtension = path.extname(fullFilePath)
transactionFolder.file( transactionFolder.file(
@@ -116,11 +136,15 @@ export async function GET(request: Request) {
}${fileExtension}`, }${fileExtension}`,
fileData fileData
) )
} else {
console.log(`Skipping missing file: ${file.filename} for transaction ${transaction.id}`)
} }
} }
} }
} }
console.log(`Finished processing all ${totalFilesProcessed} files`)
// Generate zip with progress tracking // Generate zip with progress tracking
const zipContent = await zip.generateAsync({ const zipContent = await zip.generateAsync({
type: "uint8array", type: "uint8array",

View File

@@ -36,14 +36,14 @@ export default async function FieldsSettingsPage() {
{ key: "llm_prompt", label: "LLM Prompt", editable: true }, { key: "llm_prompt", label: "LLM Prompt", editable: true },
{ {
key: "isVisibleInList", key: "isVisibleInList",
label: "Show in transactions table", label: "Always show in transactions table",
type: "checkbox", type: "checkbox",
defaultValue: false, defaultValue: false,
editable: true, editable: true,
}, },
{ {
key: "isVisibleInAnalysis", key: "isVisibleInAnalysis",
label: "Show in analysis form", label: "Always show in analysis form",
type: "checkbox", type: "checkbox",
defaultValue: false, defaultValue: false,
editable: true, editable: true,

View File

@@ -17,7 +17,6 @@ import { Separator } from "@/components/ui/separator"
import { useTransactionFilters } from "@/hooks/use-transaction-filters" import { useTransactionFilters } from "@/hooks/use-transaction-filters"
import { Category, Field, Project } from "@/prisma/client" import { Category, Field, Project } from "@/prisma/client"
import { formatDate } from "date-fns" import { formatDate } from "date-fns"
import { Download, Loader2 } from "lucide-react"
import { useState } from "react" import { useState } from "react"
const deselectedFields = ["files", "text"] const deselectedFields = ["files", "text"]
@@ -42,11 +41,9 @@ export function ExportTransactionsDialog({
) )
const [includeAttachments, setIncludeAttachments] = useState(true) const [includeAttachments, setIncludeAttachments] = useState(true)
const handleSubmit = async () => { const handleSubmit = () => {
setIsLoading(true) setIsLoading(true)
const exportWindow = window.open(
try {
const response = await fetch(
`/export/transactions?${new URLSearchParams({ `/export/transactions?${new URLSearchParams({
search: exportFilters?.search || "", search: exportFilters?.search || "",
dateFrom: exportFilters?.dateFrom || "", dateFrom: exportFilters?.dateFrom || "",
@@ -59,32 +56,19 @@ export function ExportTransactionsDialog({
}).toString()}` }).toString()}`
) )
if (!response.ok) { // Check if window was opened successfully
throw new Error("Export failed") if (!exportWindow) {
setIsLoading(false)
return
} }
// Get the filename from the Content-Disposition header // Monitor the export window
const contentDisposition = response.headers.get("Content-Disposition") const checkWindow = setInterval(() => {
const filename = contentDisposition?.split("filename=")[1]?.replace(/"/g, "") || "transactions.zip" if (exportWindow.closed) {
clearInterval(checkWindow)
// Create a blob from the response
const blob = await response.blob()
// Create a download link and trigger it
const url = window.URL.createObjectURL(blob)
const a = document.createElement("a")
a.href = url
a.download = filename
document.body.appendChild(a)
a.click()
window.URL.revokeObjectURL(url)
document.body.removeChild(a)
} catch (error) {
console.error("Export failed:", error)
// You might want to show an error message to the user here
} finally {
setIsLoading(false) setIsLoading(false)
} }
}, 1000)
} }
return ( return (
@@ -92,9 +76,7 @@ export function ExportTransactionsDialog({
<DialogTrigger asChild>{children}</DialogTrigger> <DialogTrigger asChild>{children}</DialogTrigger>
<DialogContent className="max-w-xl"> <DialogContent className="max-w-xl">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl font-bold"> <DialogTitle className="text-2xl font-bold">Export {total} Transactions</DialogTitle>
Export {total} Transaction{total !== 1 ? "s" : ""}
</DialogTitle>
<DialogDescription>Export selected transactions and files as a CSV file or a ZIP archive</DialogDescription> <DialogDescription>Export selected transactions and files as a CSV file or a ZIP archive</DialogDescription>
</DialogHeader> </DialogHeader>
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
@@ -190,17 +172,7 @@ export function ExportTransactionsDialog({
</div> </div>
<DialogFooter className="sm:justify-end"> <DialogFooter className="sm:justify-end">
<Button type="button" onClick={handleSubmit} disabled={isLoading}> <Button type="button" onClick={handleSubmit} disabled={isLoading}>
{isLoading ? ( {isLoading ? "Exporting..." : "Export Transactions"}
<div className="flex items-center gap-2">
<Loader2 className="h-4 w-4 animate-spin" />
<span>Exporting...</span>
</div>
) : (
<div className="flex items-center gap-2">
<Download className="h-4 w-4" />
<span>Export Transactions</span>
</div>
)}
</Button> </Button>
</DialogFooter> </DialogFooter>
</DialogContent> </DialogContent>

View File

@@ -28,8 +28,6 @@ services:
volumes: volumes:
- ./pgdata:/var/lib/postgresql/data - ./pgdata:/var/lib/postgresql/data
restart: unless-stopped restart: unless-stopped
ports:
- "5432:5432"
logging: logging:
driver: "local" driver: "local"
options: options: