import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; const isDev = process.env.NODE_ENV === "development"; function getR2Client() { return new S3Client({ region: "auto", endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`, credentials: { accessKeyId: process.env.R2_ACCESS_KEY_ID!, secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!, }, }); } const DEV_BASE_URL = process.env.NEXTAUTH_URL ?? "http://localhost:3000"; export async function generateUploadUrl( key: string, contentType: string, expiresIn = 300 ): Promise { if (isDev) { return `${DEV_BASE_URL}/api/files/dev/${key}`; } const command = new PutObjectCommand({ Bucket: process.env.R2_BUCKET_NAME!, Key: key, ContentType: contentType, }); return getSignedUrl(getR2Client(), command, { expiresIn }); } export async function generateDownloadUrl( key: string, expiresIn = 3600 ): Promise { if (isDev) { return `${DEV_BASE_URL}/api/files/dev/${key}`; } const command = new GetObjectCommand({ Bucket: process.env.R2_BUCKET_NAME!, Key: key }); return getSignedUrl(getR2Client(), command, { expiresIn }); } export function buildStorageKey( type: "po-document" | "receipt", poId: string, fileName: string ): string { const timestamp = Date.now(); const safe = fileName.replace(/[^a-zA-Z0-9._-]/g, "_"); return `${type}/${poId}/${timestamp}-${safe}`; } export function buildSignatureKey(userId: string, ext: string): string { return `signatures/${userId}.${ext}`; } /** * Upload a file buffer directly to storage (server-side). * In dev: writes to .dev-uploads/. In prod: PUTs to R2. */ export async function uploadBuffer( key: string, buffer: Buffer, contentType: string ): Promise { if (isDev) { const fs = await import("fs/promises"); const path = await import("path"); const dir = path.join(process.cwd(), ".dev-uploads", ...key.split("/").slice(0, -1)); const filePath = path.join(process.cwd(), ".dev-uploads", ...key.split("/")); await fs.mkdir(dir, { recursive: true }); await fs.writeFile(filePath, buffer); } else { const { S3Client, PutObjectCommand } = await import("@aws-sdk/client-s3"); const s3 = new S3Client({ region: "auto", endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`, credentials: { accessKeyId: process.env.R2_ACCESS_KEY_ID!, secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!, }, }); await s3.send(new PutObjectCommand({ Bucket: process.env.R2_BUCKET_NAME!, Key: key, Body: buffer, ContentType: contentType, })); } } /** * Fetch a stored file as a Buffer (server-side). */ export async function downloadBuffer(key: string): Promise { try { if (isDev) { const fs = await import("fs/promises"); const path = await import("path"); const filePath = path.join(process.cwd(), ".dev-uploads", ...key.split("/")); return await fs.readFile(filePath) as Buffer; } else { const url = await generateDownloadUrl(key, 60); const res = await fetch(url); if (!res.ok) return null; return Buffer.from(await res.arrayBuffer()); } } catch { return null; } }