import { Storage } from "@google-cloud/storage"; import path from 'path'; import logger from "@utils/logger"; /** Optional layout for CPC/CSD objects (mirrors local `uploads/cpc-csd-files/...`). */ export type CpcGcsUploadOptions = { bucket?: string; /** Directory prefix inside the bucket, no leading slash, e.g. `cpc-csd/csd/BOOK-1/documents` */ objectDir?: string; /** Final filename segment only (no path separators) */ objectBaseName?: string; }; class CpcGcsService { private storage: Storage; private bucketName: string; constructor() { this.storage = new Storage({ projectId: process.env.GCP_PROJECT_ID, keyFilename: process.env.GCP_KEY_FILE }); this.bucketName = process.env.GCP_BUCKET_NAME || ''; } parseGsUrl(gsUrl: string) { if (!gsUrl || !gsUrl.startsWith("gs://")) throw new Error("INVALID_DOCUMENT_URL"); const s = gsUrl.slice(5); const [bucket, ...rest] = s.split("/"); const objectPath = rest.join("/"); if (!bucket || !objectPath) throw new Error("INVALID_DOCUMENT_URL"); return { bucket, objectPath }; } async downloadFromGcs(gsUrl: string): Promise { const { bucket, objectPath } = this.parseGsUrl(gsUrl); const [buf] = await this.storage.bucket(bucket).file(objectPath).download(); return buf; } /** * Third argument can be a legacy custom bucket string, or structured options for path layout. */ async uploadToGcs( fileBuffer: Buffer, originalName: string, legacyBucketOrOpts?: string | CpcGcsUploadOptions ): Promise { const opts: CpcGcsUploadOptions = typeof legacyBucketOrOpts === 'string' ? { bucket: legacyBucketOrOpts } : legacyBucketOrOpts || {}; const targetBucket = opts.bucket || this.bucketName; const base = opts.objectBaseName && !opts.objectBaseName.includes('/') && !opts.objectBaseName.includes('..') ? opts.objectBaseName : `${Date.now()}-${path.basename(originalName)}`; const dir = opts.objectDir && !opts.objectDir.includes('..') ? opts.objectDir.replace(/^\/+|\/+$/g, '') : 'cpc-csd/uploads'; const fileName = `${dir}/${base}`.replace(/\\/g, '/'); const bucket = this.storage.bucket(targetBucket); const file = bucket.file(fileName); await file.save(fileBuffer); logger.info(`[CpcGcsService] File uploaded to gs://${targetBucket}/${fileName}`); return `gs://${targetBucket}/${fileName}`; } } export const cpcGcsService = new CpcGcsService();