| import * as fs from 'fs'; |
| import * as path from 'path'; |
| import { supabase } from './client'; |
| import { config } from '../config'; |
| import { logger } from '../utils/logger'; |
|
|
| |
| |
| |
| |
| export async function uploadInputFile( |
| userId: string, |
| storageKey: string, |
| fileName: string, |
| fileBuffer: Buffer, |
| upsert = false, |
| ): Promise<string> { |
| const ext = path.extname(fileName); |
| const storagePath = `${userId}/${storageKey}/input${ext}`; |
|
|
| const { error } = await supabase.storage |
| .from(config.inputBucket) |
| .upload(storagePath, fileBuffer, { |
| contentType: getMimeType(ext), |
| upsert, |
| }); |
|
|
| if (error) { |
| logger.error('Failed to upload input file', { storagePath, error: error.message }); |
| throw error; |
| } |
|
|
| return storagePath; |
| } |
|
|
| |
| |
| |
| export async function downloadInputFile(storagePath: string, localPath: string): Promise<void> { |
| const { data, error } = await supabase.storage |
| .from(config.inputBucket) |
| .download(storagePath); |
|
|
| if (error) { |
| logger.error('Failed to download input file', { storagePath, error: error.message }); |
| throw error; |
| } |
|
|
| const buffer = Buffer.from(await data.arrayBuffer()); |
| const dir = path.dirname(localPath); |
| if (!fs.existsSync(dir)) { |
| fs.mkdirSync(dir, { recursive: true }); |
| } |
| fs.writeFileSync(localPath, buffer); |
| } |
|
|
| |
| |
| |
| |
| export async function uploadReportPdf( |
| userId: string, |
| jobId: string, |
| localPdfPath: string, |
| ): Promise<{ storagePath: string; expiresAt: string }> { |
| const storagePath = `${userId}/${jobId}/report.pdf`; |
| const fileBuffer = fs.readFileSync(localPdfPath); |
|
|
| const { error } = await supabase.storage |
| .from(config.reportBucket) |
| .upload(storagePath, fileBuffer, { |
| contentType: 'application/pdf', |
| upsert: true, |
| }); |
|
|
| if (error) { |
| logger.error('Failed to upload report PDF', { storagePath, error: error.message }); |
| throw error; |
| } |
|
|
| const expiresAt = new Date( |
| Date.now() + config.reportRetentionHours * 60 * 60 * 1000, |
| ).toISOString(); |
|
|
| return { storagePath, expiresAt }; |
| } |
|
|
| |
| |
| |
| |
| export async function uploadReceiptPdf( |
| userId: string, |
| jobId: string, |
| localPdfPath: string, |
| ): Promise<{ storagePath: string; expiresAt: string }> { |
| const storagePath = `${userId}/${jobId}/receipt.pdf`; |
| const fileBuffer = fs.readFileSync(localPdfPath); |
|
|
| const { error } = await supabase.storage |
| .from(config.reportBucket) |
| .upload(storagePath, fileBuffer, { |
| contentType: 'application/pdf', |
| upsert: true, |
| }); |
|
|
| if (error) { |
| logger.error('Failed to upload Digital Receipt PDF', { |
| storagePath, |
| error: error.message, |
| }); |
| throw error; |
| } |
|
|
| const expiresAt = new Date( |
| Date.now() + config.reportRetentionHours * 60 * 60 * 1000, |
| ).toISOString(); |
|
|
| return { storagePath, expiresAt }; |
| } |
|
|
| |
| |
| |
| export async function deleteReportPdf(storagePath: string): Promise<void> { |
| const { error } = await supabase.storage |
| .from(config.reportBucket) |
| .remove([storagePath]); |
|
|
| if (error) { |
| logger.error('Failed to delete report PDF', { storagePath, error: error.message }); |
| throw error; |
| } |
| } |
|
|
| |
| |
| |
| |
| export async function uploadStorageState( |
| accountId: string, |
| stateJson: string, |
| ): Promise<string> { |
| const storagePath = `${accountId}/state.json`; |
|
|
| const { error } = await supabase.storage |
| .from(config.sessionBucket) |
| .upload(storagePath, Buffer.from(stateJson, 'utf-8'), { |
| contentType: 'application/json', |
| upsert: true, |
| }); |
|
|
| if (error) { |
| logger.error('Failed to upload storage state', { accountId, error: error.message }); |
| throw error; |
| } |
|
|
| return storagePath; |
| } |
|
|
| |
| |
| |
| |
| export async function downloadStorageState(storagePath: string): Promise<string | null> { |
| const { data, error } = await supabase.storage |
| .from(config.sessionBucket) |
| .download(storagePath); |
|
|
| if (error) { |
| |
| if (error.message?.includes('not found') || error.message?.includes('Object not found')) { |
| return null; |
| } |
| logger.error('Failed to download storage state', { storagePath, error: error.message }); |
| throw error; |
| } |
|
|
| return await data.text(); |
| } |
|
|
| |
| |
| |
| export async function createSignedUrl( |
| bucket: string, |
| filePath: string, |
| expiresInSeconds: number, |
| downloadFileName?: string, |
| ): Promise<string> { |
| const { data, error } = await supabase.storage |
| .from(bucket) |
| .createSignedUrl( |
| filePath, |
| expiresInSeconds, |
| downloadFileName ? { download: downloadFileName } : undefined, |
| ); |
|
|
| if (error) { |
| logger.error('Failed to create signed URL', { bucket, filePath, error: error.message }); |
| throw error; |
| } |
|
|
| return data.signedUrl; |
| } |
|
|
| |
| function getMimeType(ext: string): string { |
| const mimeTypes: Record<string, string> = { |
| '.pdf': 'application/pdf', |
| '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', |
| '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', |
| '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation', |
| '.ps': 'application/postscript', |
| '.html': 'text/html', |
| '.txt': 'text/plain', |
| '.rtf': 'application/rtf', |
| '.odt': 'application/vnd.oasis.opendocument.text', |
| '.hwp': 'application/x-hwp', |
| }; |
| return mimeTypes[ext.toLowerCase()] || 'application/octet-stream'; |
| } |
|
|