// src/drive/drive.service.ts import { BadRequestException, Injectable, InternalServerErrorException, } from '@nestjs/common'; import { google } from 'googleapis'; import * as fs from 'fs'; @Injectable() export class DriveService { private SCOPES = ['https://www.googleapis.com/auth/drive']; private loadCredentials() { try { const oAuth2Client = new google.auth.OAuth2( process.env.CLIENT_ID, process.env.CLIENT_SECRET, ); oAuth2Client.setCredentials({ refresh_token: process.env.REFRESH_TOKEN, }); return oAuth2Client; } catch (err) { throw new InternalServerErrorException( 'Error al cargar credenciales de Google Drive', ); } } async list(pageToken = '') { const authClient = this.loadCredentials(); const drive = google.drive({ version: 'v3', auth: authClient }); try { const res = await drive.files.list({ corpora: 'user', supportsAllDrives: true, includeItemsFromAllDrives: true, spaces: 'drive', pageSize: 1000, pageToken, orderBy: 'name', }); return res.data; } catch (err) { throw new InternalServerErrorException( 'Error al listar archivos: ' + err.message, ); } } async deleteFile(fileId: string) { if (fileId === process.env.CARPETA) { throw new BadRequestException( 'No se puede eliminar este archivo/carpeta.', ); } console.log('Eliminando archivo de Drive, ID:', fileId); const authClient = this.loadCredentials(); const drive = google.drive({ version: 'v3', auth: authClient }); try { return await drive.files.delete({ fileId }); } catch (err) { console.error('Error al eliminar archivo de Drive:', err.message); return 0; } } async uploadFile( filePath: string, name: string, mimeType: string, parent: string, ) { const authClient = this.loadCredentials(); const drive = google.drive({ version: 'v3', auth: authClient }); const requestBody = { name, parents: [parent], }; const media = { mimeType, body: fs.createReadStream(filePath), }; try { const res = await drive.files.create({ requestBody, media, fields: 'id', supportsAllDrives: true, }); fs.unlinkSync(filePath); console.log('Archivo subido a Drive, ID:', res.data.id); return res.data.id!; } catch (err) { throw new InternalServerErrorException( 'Error al subir archivo: ' + err.message, ); } } async mkDir(name: string): Promise { if (!name) { throw new BadRequestException('Nombre de carpeta inválido'); } if (!process.env.CARPETA) { throw new InternalServerErrorException('CARPETA no configurada'); } const authClient = this.loadCredentials(); const drive = google.drive({ version: 'v3', auth: authClient }); const requestBody = { name, mimeType: 'application/vnd.google-apps.folder', parents: [process.env.CARPETA], }; try { const res = await drive.files.create({ requestBody, fields: 'id', supportsAllDrives: true, }); if (!res.data.id) { throw new BadRequestException('No se pudo crear la carpeta'); } return res.data.id; } catch (err) { throw new InternalServerErrorException( 'Error al crear carpeta: ' + err.message, ); } } async update(fileId: string, addParents: string, removeParents: string) { const authClient = this.loadCredentials(); const drive = google.drive({ version: 'v3', auth: authClient }); try { const res = await drive.files.update({ fileId, addParents, removeParents, supportsAllDrives: true, }); return res.data; } catch (err) { throw new InternalServerErrorException( 'Error al actualizar archivo: ' + err.message, ); } } async folder(numeroCuenta: string): Promise { if (!process.env.CARPETA) { throw new InternalServerErrorException('CARPETA no configurada'); } const authClient = this.loadCredentials(); const drive = google.drive({ version: 'v3', auth: authClient }); const res = await drive.files.list({ q: ` mimeType='application/vnd.google-apps.folder' and name='${numeroCuenta}' and '${process.env.CARPETA}' in parents and trashed=false `, fields: 'files(id, name)', supportsAllDrives: true, includeItemsFromAllDrives: true, }); if (res.data.files?.length) { return res.data.files[0].id!; } return this.mkDir(numeroCuenta); } }