72 lines
2.7 KiB
TypeScript
72 lines
2.7 KiB
TypeScript
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<Buffer> {
|
|
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<string> {
|
|
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();
|