@opendatalabs/vana-sdk
Version:
A TypeScript library for interacting with Vana Network smart contracts.
1 lines • 27.5 kB
Source Map (JSON)
{"version":3,"sources":["../../../src/storage/providers/google-drive.ts"],"sourcesContent":["/**\n * Google Drive Storage Provider for Vana SDK\n *\n * Implements storage interface for Google Drive using OAuth2 authentication.\n * Based on patterns from dlp-ui-template with NextAuth integration.\n */\n\nimport {\n StorageError,\n type StorageProvider,\n type StorageUploadResult,\n type StorageFile,\n type StorageListOptions,\n type StorageProviderConfig,\n} from \"../index\";\n\nexport interface GoogleDriveConfig {\n /** OAuth2 access token */\n accessToken: string;\n /** Optional refresh token for token renewal */\n refreshToken?: string;\n /** OAuth2 client ID */\n clientId?: string;\n /** OAuth2 client secret */\n clientSecret?: string;\n /** Parent folder ID to upload files to */\n folderId?: string;\n}\n\ninterface GoogleDriveFile {\n id: string;\n name: string;\n webViewLink: string;\n size: string;\n mimeType: string;\n createdTime: string;\n}\n\ninterface GoogleDriveUploadResponse {\n id: string;\n name: string;\n}\n\ninterface GoogleDriveListResponse {\n files: GoogleDriveFile[];\n}\n\ninterface GoogleDriveTokenResponse {\n access_token: string;\n}\n\n/**\n * Google Drive Storage Provider with folder management capabilities\n *\n * @remarks\n * Implements storage interface for Google Drive using OAuth2 authentication.\n * Provides file upload/download operations and advanced folder management\n * including search, creation, and nested folder structures. Requires the\n * `https://www.googleapis.com/auth/drive.file` OAuth scope for full functionality.\n *\n * @category Storage\n *\n * @example\n * ```typescript\n * const googleDriveStorage = new GoogleDriveStorage({\n * accessToken: \"your-oauth-access-token\",\n * refreshToken: \"your-oauth-refresh-token\",\n * clientId: \"your-oauth-client-id\",\n * clientSecret: \"your-oauth-client-secret\",\n * });\n *\n * // Create folder structure and upload file\n * const folderId = await googleDriveStorage.findOrCreateFolder(\"screenshots\");\n * const result = await googleDriveStorage.upload(fileBlob, \"image.png\");\n * ```\n */\nexport class GoogleDriveStorage implements StorageProvider {\n private readonly baseUrl = \"https://www.googleapis.com/drive/v3\";\n private readonly uploadUrl = \"https://www.googleapis.com/upload/drive/v3\";\n\n constructor(private config: GoogleDriveConfig) {\n if (!config.accessToken) {\n throw new StorageError(\n \"Google Drive access token is required\",\n \"MISSING_ACCESS_TOKEN\",\n \"google-drive\",\n );\n }\n }\n\n async upload(file: Blob, filename?: string): Promise<StorageUploadResult> {\n try {\n const fileName = filename ?? `vana-file-${Date.now()}.dat`;\n\n // Create file metadata\n const metadata = {\n name: fileName,\n parents: this.config.folderId ? [this.config.folderId] : undefined,\n };\n\n // Create multipart upload request\n const delimiter = \"-------314159265358979323846\";\n const closeDelim = `\\r\\n--${delimiter}--`;\n\n const metadataBlob = new Blob([JSON.stringify(metadata)], {\n type: \"application/json\",\n });\n\n const multipartRequestBody = [\n `--${delimiter}`,\n \"Content-Type: application/json\",\n \"\",\n await metadataBlob.text(),\n `--${delimiter}`,\n `Content-Type: ${file.type || \"application/octet-stream\"}`,\n \"\",\n \"\",\n ].join(\"\\r\\n\");\n\n const requestBody = new Blob([multipartRequestBody, file, closeDelim]);\n\n const response = await fetch(\n `${this.uploadUrl}/files?uploadType=multipart`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.config.accessToken}`,\n \"Content-Type\": `multipart/related; boundary=\"${delimiter}\"`,\n },\n body: requestBody,\n },\n );\n\n if (!response.ok) {\n const error = await response.text();\n throw new StorageError(\n `Failed to upload to Google Drive: ${error}`,\n \"UPLOAD_FAILED\",\n \"google-drive\",\n );\n }\n\n const result = (await response.json()) as GoogleDriveUploadResponse;\n\n // Make file publicly readable\n await this.makeFilePublic(result.id);\n\n return {\n url: `https://drive.google.com/uc?id=${result.id}&export=download`,\n size: file.size,\n contentType: file.type || \"application/octet-stream\",\n metadata: {\n id: result.id,\n name: result.name,\n driveUrl: `https://drive.google.com/file/d/${result.id}/view`,\n },\n };\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Google Drive upload error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"UPLOAD_ERROR\",\n \"google-drive\",\n );\n }\n }\n\n async download(url: string): Promise<Blob> {\n try {\n // Extract file ID from Google Drive URL\n const fileId = this.extractFileId(url);\n if (!fileId) {\n throw new StorageError(\n \"Invalid Google Drive URL format\",\n \"INVALID_URL\",\n \"google-drive\",\n );\n }\n\n const response = await fetch(\n `${this.baseUrl}/files/${fileId}?alt=media`,\n {\n headers: {\n Authorization: `Bearer ${this.config.accessToken}`,\n },\n },\n );\n\n if (!response.ok) {\n const error = await response.text();\n throw new StorageError(\n `Failed to download from Google Drive: ${error}`,\n \"DOWNLOAD_FAILED\",\n \"google-drive\",\n );\n }\n\n const blob = await response.blob();\n\n // Check if we got HTML content instead of the actual file (authentication issue)\n if (blob.type === \"text/html\") {\n throw new StorageError(\n \"Received HTML content instead of file data. This suggests an authentication or URL formatting issue with Google Drive.\",\n \"AUTHENTICATION_ERROR\",\n \"google-drive\",\n );\n }\n\n return blob;\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Google Drive download error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"DOWNLOAD_ERROR\",\n \"google-drive\",\n );\n }\n }\n\n async list(options?: StorageListOptions): Promise<StorageFile[]> {\n try {\n let query = \"trashed = false\";\n\n // Add parent folder filter if configured\n if (this.config.folderId) {\n query += ` and '${this.config.folderId}' in parents`;\n }\n\n // Add name pattern filter\n if (options?.namePattern) {\n query += ` and name contains '${options.namePattern}'`;\n }\n\n const params = new URLSearchParams({\n q: query,\n fields: \"files(id,name,size,mimeType,createdTime,webViewLink)\",\n pageSize: (options?.limit ?? 100).toString(),\n });\n\n if (options?.offset && typeof options.offset === \"string\") {\n params.set(\"pageToken\", options.offset);\n }\n\n const response = await fetch(`${this.baseUrl}/files?${params}`, {\n headers: {\n Authorization: `Bearer ${this.config.accessToken}`,\n },\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new StorageError(\n `Failed to list Google Drive files: ${error}`,\n \"LIST_FAILED\",\n \"google-drive\",\n );\n }\n\n const result = (await response.json()) as GoogleDriveListResponse;\n\n return result.files.map((file: GoogleDriveFile) => ({\n id: file.id,\n name: file.name,\n url: `https://drive.google.com/uc?id=${file.id}&export=download`,\n size: parseInt(file.size) || 0,\n contentType: file.mimeType,\n createdAt: new Date(file.createdTime),\n metadata: {\n id: file.id,\n driveUrl: file.webViewLink,\n },\n }));\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Google Drive list error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"LIST_ERROR\",\n \"google-drive\",\n );\n }\n }\n\n async delete(url: string): Promise<boolean> {\n try {\n // Extract file ID from Google Drive URL\n const fileId = this.extractFileId(url);\n if (!fileId) {\n throw new StorageError(\n \"Invalid Google Drive URL format\",\n \"INVALID_URL\",\n \"google-drive\",\n );\n }\n\n const response = await fetch(`${this.baseUrl}/files/${fileId}`, {\n method: \"DELETE\",\n headers: {\n Authorization: `Bearer ${this.config.accessToken}`,\n },\n });\n\n if (!response.ok && response.status !== 404) {\n const error = await response.text();\n throw new StorageError(\n `Failed to delete from Google Drive: ${error}`,\n \"DELETE_FAILED\",\n \"google-drive\",\n );\n }\n\n return true;\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Google Drive delete error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"DELETE_ERROR\",\n \"google-drive\",\n );\n }\n }\n\n getConfig(): StorageProviderConfig {\n return {\n name: \"Google Drive\",\n type: \"google-drive\",\n requiresAuth: true,\n features: {\n upload: true,\n download: true,\n list: true,\n delete: true,\n },\n };\n }\n\n /**\n * Make a Google Drive file publicly readable\n *\n * @param fileId - Google Drive file ID\n */\n private async makeFilePublic(fileId: string): Promise<void> {\n try {\n await fetch(`${this.baseUrl}/files/${fileId}/permissions`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.config.accessToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n role: \"reader\",\n type: \"anyone\",\n }),\n });\n } catch (error) {\n // Non-critical error - file upload succeeded but sharing failed\n console.warn(\"Failed to make Google Drive file public:\", error);\n }\n }\n\n /**\n * Extract file ID from various Google Drive URL formats\n *\n * @param url - Google Drive URL\n * @returns File ID or null if not found\n */\n private extractFileId(url: string): string | null {\n // Handle various Google Drive URL formats\n const patterns = [\n /\\/file\\/d\\/([a-zA-Z0-9-_]+)/, // https://drive.google.com/file/d/FILE_ID/view\n /id=([a-zA-Z0-9-_]+)/, // https://drive.google.com/uc?id=FILE_ID\n /^([a-zA-Z0-9-_]+)$/, // Just the file ID\n ];\n\n for (const pattern of patterns) {\n const match = url.match(pattern);\n if (match) {\n return match[1];\n }\n }\n\n return null;\n }\n\n /**\n * Searches for an existing folder by name within a specified parent folder\n *\n * @remarks\n * This method queries the Google Drive API to find a folder with the exact name\n * within the specified parent directory. Only searches for folders (not files)\n * and excludes trashed items.\n *\n * @param name - The exact name of the folder to search for\n * @param parentId - The ID of the parent folder to search within (defaults to 'root')\n * @returns Promise that resolves to the folder ID if found, or `null` if not found\n * @throws {StorageError} When the Google Drive API request fails\n *\n * @example\n * ```typescript\n * // Search for a folder in the root directory\n * const folderId = await googleDriveStorage.findFolder(\"screenshots\");\n * if (folderId) {\n * console.log(\"Found folder:\", folderId);\n * } else {\n * console.log(\"Folder not found\");\n * }\n *\n * // Search for a subfolder within another folder\n * const subFolderId = await googleDriveStorage.findFolder(\"roasts\", parentFolderId);\n * ```\n */\n async findFolder(\n name: string,\n parentId: string = \"root\",\n ): Promise<string | null> {\n try {\n const query = `name='${name}' and '${parentId}' in parents and mimeType='application/vnd.google-apps.folder' and trashed = false`;\n\n const params = new URLSearchParams({\n q: query,\n fields: \"files(id,name,mimeType)\",\n });\n\n const response = await fetch(`${this.baseUrl}/files?${params}`, {\n headers: {\n Authorization: `Bearer ${this.config.accessToken}`,\n },\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new StorageError(\n `Failed to search Google Drive folders: ${error}`,\n \"FIND_FOLDER_FAILED\",\n \"google-drive\",\n );\n }\n\n const result = (await response.json()) as GoogleDriveListResponse;\n\n if (result.files && result.files.length > 0) {\n return result.files[0].id;\n }\n\n return null;\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Google Drive findFolder error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"FIND_FOLDER_ERROR\",\n \"google-drive\",\n );\n }\n }\n\n /**\n * Creates a new folder within a specified parent folder\n *\n * @remarks\n * This method creates a new folder using the Google Drive API. The folder will be\n * created with the specified name as a child of the parent folder. Requires the\n * `https://www.googleapis.com/auth/drive.file` OAuth scope.\n *\n * @param name - The name for the new folder\n * @param parentId - The ID of the parent folder where the new folder will be created (defaults to 'root')\n * @returns Promise that resolves to the ID of the newly created folder\n * @throws {StorageError} When the Google Drive API request fails or folder creation is denied\n *\n * @example\n * ```typescript\n * // Create a folder in the root directory\n * const folderId = await googleDriveStorage.createFolder(\"my-documents\");\n * console.log(\"Created folder:\", folderId);\n *\n * // Create a subfolder within another folder\n * const subFolderId = await googleDriveStorage.createFolder(\"reports\", parentFolderId);\n * ```\n */\n async createFolder(name: string, parentId: string = \"root\"): Promise<string> {\n try {\n const metadata = {\n name,\n mimeType: \"application/vnd.google-apps.folder\",\n parents: [parentId],\n };\n\n const response = await fetch(`${this.baseUrl}/files`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.config.accessToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(metadata),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new StorageError(\n `Failed to create Google Drive folder: ${error}`,\n \"CREATE_FOLDER_FAILED\",\n \"google-drive\",\n );\n }\n\n const result = (await response.json()) as GoogleDriveUploadResponse;\n return result.id;\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Google Drive createFolder error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"CREATE_FOLDER_ERROR\",\n \"google-drive\",\n );\n }\n }\n\n /**\n * Finds an existing folder by name, or creates it if it doesn't exist\n *\n * @remarks\n * This is a convenience method that combines `findFolder` and `createFolder`.\n * It first searches for an existing folder with the specified name. If found,\n * it returns the existing folder's ID. If not found, it creates a new folder\n * and returns the new folder's ID.\n *\n * @param name - The name of the folder to find or create\n * @param parentId - The ID of the parent folder to search within or create the folder in (defaults to 'root')\n * @returns Promise that resolves to the folder ID (either existing or newly created)\n * @throws {StorageError} When the Google Drive API request fails\n *\n * @example\n * ```typescript\n * // Ensure a folder exists, creating it if necessary\n * const folderId = await googleDriveStorage.findOrCreateFolder(\"screenshots\");\n * console.log(\"Folder ID:\", folderId); // Will be same ID if folder already existed\n *\n * // Create nested folder structure\n * const parentId = await googleDriveStorage.findOrCreateFolder(\"projects\");\n * const childId = await googleDriveStorage.findOrCreateFolder(\"vana-app\", parentId);\n * ```\n */\n async findOrCreateFolder(\n name: string,\n parentId: string = \"root\",\n ): Promise<string> {\n try {\n const existingFolderId = await this.findFolder(name, parentId);\n\n if (existingFolderId) {\n return existingFolderId;\n }\n\n return await this.createFolder(name, parentId);\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Google Drive findOrCreateFolder error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"FIND_OR_CREATE_FOLDER_ERROR\",\n \"google-drive\",\n );\n }\n }\n\n /**\n * Refresh the access token using refresh token\n *\n * @returns Promise with new access token\n */\n async refreshAccessToken(): Promise<string> {\n if (\n !this.config.refreshToken ||\n !this.config.clientId ||\n !this.config.clientSecret\n ) {\n throw new StorageError(\n \"Refresh token, client ID, and client secret are required for token refresh\",\n \"MISSING_REFRESH_CONFIG\",\n \"google-drive\",\n );\n }\n\n try {\n const response = await fetch(\"https://oauth2.googleapis.com/token\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n body: new URLSearchParams({\n client_id: this.config.clientId,\n client_secret: this.config.clientSecret,\n refresh_token: this.config.refreshToken,\n grant_type: \"refresh_token\",\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new StorageError(\n `Failed to refresh Google Drive token: ${error}`,\n \"TOKEN_REFRESH_FAILED\",\n \"google-drive\",\n );\n }\n\n const result = (await response.json()) as GoogleDriveTokenResponse;\n this.config.accessToken = result.access_token;\n\n return result.access_token;\n } catch (error) {\n if (error instanceof StorageError) {\n throw error;\n }\n throw new StorageError(\n `Google Drive token refresh error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"TOKEN_REFRESH_ERROR\",\n \"google-drive\",\n );\n }\n }\n}\n"],"mappings":"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","names":[]}