mcp-memory-libsql
by spences10
Verified
import { google } from 'googleapis';
import { BaseGoogleService } from '../../services/base/BaseGoogleService.js';
import { DriveOperationResult, FileDownloadOptions, FileListOptions, FileSearchOptions, FileUploadOptions, PermissionOptions } from './types.js';
import { Readable } from 'stream';
import { DRIVE_SCOPES } from './scopes.js';
import { workspaceManager } from '../../utils/workspace.js';
import fs from 'fs/promises';
import { GaxiosResponse } from 'gaxios';
export class DriveService extends BaseGoogleService<ReturnType<typeof google.drive>> {
private initialized = false;
constructor() {
super({
serviceName: 'Google Drive',
version: 'v3'
});
}
/**
* Initialize the Drive service and all dependencies
*/
public async initialize(): Promise<void> {
try {
await super.initialize();
this.initialized = true;
} catch (error) {
throw this.handleError(error, 'Failed to initialize Drive service');
}
}
/**
* Ensure the Drive service is initialized
*/
public async ensureInitialized(): Promise<void> {
if (!this.initialized) {
await this.initialize();
}
}
/**
* Check if the service is initialized
*/
private checkInitialized(): void {
if (!this.initialized) {
throw this.handleError(
new Error('Drive service not initialized'),
'Please ensure the service is initialized before use'
);
}
}
async listFiles(email: string, options: FileListOptions = {}): Promise<DriveOperationResult> {
try {
await this.ensureInitialized();
this.checkInitialized();
await this.validateScopes(email, [DRIVE_SCOPES.FILE]);
const client = await this.getAuthenticatedClient(
email,
(auth) => google.drive({ version: 'v3', auth })
);
const query = [];
if (options.folderId) {
query.push(`'${options.folderId}' in parents`);
}
if (options.query) {
query.push(options.query);
}
const response = await client.files.list({
q: query.join(' and ') || undefined,
pageSize: options.pageSize,
orderBy: options.orderBy?.join(','),
fields: options.fields?.join(',') || 'files(id, name, mimeType, modifiedTime, size)',
});
return {
success: true,
data: response.data,
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error occurred',
};
}
}
async uploadFile(email: string, options: FileUploadOptions): Promise<DriveOperationResult> {
try {
await this.ensureInitialized();
await this.validateScopes(email, [DRIVE_SCOPES.FILE]);
const client = await this.getAuthenticatedClient(
email,
(auth) => google.drive({ version: 'v3', auth })
);
// Save content to workspace first
const uploadPath = await workspaceManager.getUploadPath(email, options.name);
await fs.writeFile(uploadPath, options.content);
const fileContent = await fs.readFile(uploadPath);
const media = {
mimeType: options.mimeType || 'application/octet-stream',
body: Readable.from([fileContent]),
};
const response = await client.files.create({
requestBody: {
name: options.name,
mimeType: options.mimeType,
parents: options.parents,
},
media,
fields: 'id, name, mimeType, webViewLink',
});
return {
success: true,
data: response.data,
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error occurred',
};
}
}
async downloadFile(email: string, options: FileDownloadOptions): Promise<DriveOperationResult> {
try {
await this.ensureInitialized();
await this.validateScopes(email, [DRIVE_SCOPES.FILE]);
const client = await this.getAuthenticatedClient(
email,
(auth) => google.drive({ version: 'v3', auth })
);
// First get file metadata to check mime type and name
const file = await client.files.get({
fileId: options.fileId,
fields: 'name,mimeType',
});
const fileName = file.data.name || options.fileId;
// Handle Google Workspace files differently
if (file.data.mimeType?.startsWith('application/vnd.google-apps')) {
let exportMimeType = options.mimeType || 'text/plain';
// Default export formats if not specified
if (!options.mimeType) {
switch (file.data.mimeType) {
case 'application/vnd.google-apps.document':
exportMimeType = 'text/markdown';
break;
case 'application/vnd.google-apps.spreadsheet':
exportMimeType = 'text/csv';
break;
case 'application/vnd.google-apps.presentation':
exportMimeType = 'text/plain';
break;
case 'application/vnd.google-apps.drawing':
exportMimeType = 'image/png';
break;
}
}
const response = await client.files.export({
fileId: options.fileId,
mimeType: exportMimeType
}, {
responseType: 'arraybuffer'
}) as unknown as GaxiosResponse<Uint8Array>;
const downloadPath = await workspaceManager.getDownloadPath(email, fileName);
await fs.writeFile(downloadPath, Buffer.from(response.data));
return {
success: true,
data: response.data,
mimeType: exportMimeType,
filePath: downloadPath
};
}
// For regular files
const response = await client.files.get({
fileId: options.fileId,
alt: 'media'
}, {
responseType: 'arraybuffer'
}) as unknown as GaxiosResponse<Uint8Array>;
const downloadPath = await workspaceManager.getDownloadPath(email, fileName);
await fs.writeFile(downloadPath, Buffer.from(response.data));
return {
success: true,
data: response.data,
filePath: downloadPath
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error occurred',
};
}
}
async createFolder(email: string, name: string, parentId?: string): Promise<DriveOperationResult> {
try {
await this.ensureInitialized();
await this.validateScopes(email, [DRIVE_SCOPES.FILE]);
const client = await this.getAuthenticatedClient(
email,
(auth) => google.drive({ version: 'v3', auth })
);
const response = await client.files.create({
requestBody: {
name,
mimeType: 'application/vnd.google-apps.folder',
parents: parentId ? [parentId] : undefined,
},
fields: 'id, name, mimeType, webViewLink',
});
return {
success: true,
data: response.data,
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error occurred',
};
}
}
async searchFiles(email: string, options: FileSearchOptions): Promise<DriveOperationResult> {
try {
await this.ensureInitialized();
await this.validateScopes(email, [DRIVE_SCOPES.FILE]);
const client = await this.getAuthenticatedClient(
email,
(auth) => google.drive({ version: 'v3', auth })
);
const query = [];
if (options.fullText) {
const escapedQuery = options.fullText.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
query.push(`fullText contains '${escapedQuery}'`);
}
if (options.mimeType) {
query.push(`mimeType = '${options.mimeType}'`);
}
if (options.folderId) {
query.push(`'${options.folderId}' in parents`);
}
if (options.trashed !== undefined) {
query.push(`trashed = ${options.trashed}`);
}
if (options.query) {
query.push(options.query);
}
const response = await client.files.list({
q: query.join(' and ') || undefined,
pageSize: options.pageSize,
orderBy: options.orderBy?.join(','),
fields: options.fields?.join(',') || 'files(id, name, mimeType, modifiedTime, size)',
});
return {
success: true,
data: response.data,
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error occurred',
};
}
}
async updatePermissions(email: string, options: PermissionOptions): Promise<DriveOperationResult> {
try {
await this.ensureInitialized();
await this.validateScopes(email, [DRIVE_SCOPES.FILE]);
const client = await this.getAuthenticatedClient(
email,
(auth) => google.drive({ version: 'v3', auth })
);
const response = await client.permissions.create({
fileId: options.fileId,
requestBody: {
role: options.role,
type: options.type,
emailAddress: options.emailAddress,
domain: options.domain,
allowFileDiscovery: options.allowFileDiscovery,
},
});
return {
success: true,
data: response.data,
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error occurred',
};
}
}
async deleteFile(email: string, fileId: string): Promise<DriveOperationResult> {
try {
await this.ensureInitialized();
await this.validateScopes(email, [DRIVE_SCOPES.FILE]);
const client = await this.getAuthenticatedClient(
email,
(auth) => google.drive({ version: 'v3', auth })
);
await client.files.delete({
fileId,
});
return {
success: true,
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error occurred',
};
}
}
}