@accounter/server
Version:
Accounter GraphQL server
99 lines (81 loc) • 3.27 kB
text/typescript
import { Inject, Injectable, Scope } from 'graphql-modules';
import { ENVIRONMENT } from '../../../shared/tokens.js';
import type { Environment } from '../../../shared/types/index.js';
import {
folderContentSchema,
type DriveFileContent,
type DriveFolderContent,
} from './types/folder-content.js';
({
scope: Scope.Singleton,
global: true,
})
export class GoogleDriveProvider {
private apiKey: string;
constructor((ENVIRONMENT) private env: Environment) {
this.apiKey = this.env.googleDrive?.driveApiKey ?? '';
}
private async fetchFolderContent(folderUrl: string): Promise<DriveFolderContent> {
const mainUrl = folderUrl.split('?')[0];
const folderId = mainUrl.split('folders/')[1];
if (!folderId) {
throw new Error('Invalid Google Drive URL');
}
const url = new URL(
`https://www.googleapis.com/drive/v3/files?q='${folderId}'+in+parents&key=${this.apiKey}`,
);
const res = await fetch(url).catch(err => {
const message = `Failed fetching data from Google Drive for URL="${folderUrl}"`;
console.error(`${message}: ${err}`);
throw new Error(message);
});
const jsonResponse = await res.json().catch(err => {
const message = `Failed parsing response from Google Drive`;
console.error(`${message}: ${err}`);
throw new Error(message);
});
const parsedResponse = folderContentSchema.safeParse(jsonResponse);
if (!parsedResponse.success) {
const message = `Failed to parse response from Google Drive`;
console.error(message, parsedResponse.error);
throw new Error(message);
}
return parsedResponse.data;
}
private async fetchFile(fileInfo: DriveFileContent): Promise<File> {
const fileId = fileInfo.id;
const url = new URL(`https://drive.google.com/uc?export=download&id=${fileId}`);
// fetch file
const response = await fetch(url).catch(err => {
const message = `Failed fetching file from Google Drive for file="${fileInfo.name}"`;
console.error(`${message}: ${err}`);
throw new Error(message);
});
const buffer = await response.arrayBuffer().catch(err => {
const message = `Failed parsing file from Google Drive for file="${fileInfo.name}"`;
console.error(`${message}: ${err}`);
throw new Error(message);
});
const file = new File([buffer], fileInfo.name, { type: fileInfo.mimeType });
return file;
}
private isRelevantFileType(originalMimeType: string): boolean {
const mimeType = originalMimeType.toLowerCase();
return mimeType === 'application/pdf' || mimeType.startsWith('image/');
}
public async fetchFilesFromSharedFolder(folderUrl: string): Promise<File[]> {
try {
const folderData = await this.fetchFolderContent(folderUrl);
const relevantFiles = folderData.files.filter(file => this.isRelevantFileType(file.mimeType));
if (!relevantFiles.length) {
return [];
}
const files = await Promise.all(relevantFiles.map(fileInfo => this.fetchFile(fileInfo)));
return files;
} catch (e) {
const message = `Failed fetching files from Google Drive`;
console.error(`${message}: ${e}`);
throw new Error(message, { cause: e });
}
}
}