diff --git a/backend/src/utils/gdrive.ts b/backend/src/utils/gdrive.ts new file mode 100644 index 0000000..a1d1249 --- /dev/null +++ b/backend/src/utils/gdrive.ts @@ -0,0 +1,69 @@ +import { google } from 'googleapis'; +import type { Readable } from 'node:stream'; +import { env } from '../config/env.js'; + +const auth = new google.auth.OAuth2(env.gdriveClientId, env.gdriveClientSecret); +auth.setCredentials({ refresh_token: env.gdriveRefreshToken }); + +const drive = google.drive({ version: 'v3', auth }); + +/** + * Upload a stream directly to Google Drive. + * Uses the Drive API's resumable upload — no rclone process, no disk writes. + * Returns the new file's Drive ID. + */ +export async function uploadStream( + stream: Readable, + filename: string, + mimeType: string, +): Promise { + const res = await drive.files.create({ + requestBody: { + name: filename, + parents: [env.gdriveFolderId], + }, + media: { + mimeType: mimeType || 'application/octet-stream', + body: stream, + }, + fields: 'id', + }); + + if (!res.data.id) throw new Error('GDrive upload returned no file ID'); + return res.data.id; +} + +/** + * Stream file content from Drive for inline preview. + * Returns a readable stream — no disk writes on the server. + */ +export async function getStream(fileId: string): Promise { + const res = await drive.files.get( + { fileId, alt: 'media' }, + { responseType: 'stream' }, + ); + return res.data as unknown as Readable; +} + +/** Permanently delete a file from Drive. Silently ignores 404. */ +export async function deleteFile(fileId: string): Promise { + await drive.files.delete({ fileId }).catch((err: { code?: number }) => { + if (err.code !== 404) throw err; + }); +} + +/** Direct download URL — bypasses Drive's antivirus interstitial for large files. */ +export function downloadUrl(fileId: string): string { + return `https://drive.usercontent.google.com/download?id=${fileId}&export=download&confirm=t`; +} + +/** Lightweight connectivity check: lists 1 file in our folder. Returns false on any error. */ +export async function checkGdriveHealth(): Promise { + if (!env.gdriveClientId) return false; + try { + await drive.files.list({ q: `'${env.gdriveFolderId}' in parents`, pageSize: 1, fields: 'files(id)' }); + return true; + } catch { + return false; + } +}