From 4fef5e4c3a3978b138ed1174b60487e044f2e393 Mon Sep 17 00:00:00 2001 From: loadingthedev Date: Sat, 23 Sep 2023 13:02:23 +0530 Subject: [PATCH 1/3] feat: support for shared-drive aka teamdrive --- src/app/api/getData/route.ts | 96 ++++++++------------------------ src/app/api/getPassword/route.ts | 51 ++++------------- src/app/api/getReadme/route.ts | 22 ++------ src/app/api/search/route.ts | 3 +- src/config/gIndex.config.ts | 5 +- src/pages/[...path]/index.tsx | 4 +- src/utils/gdrive.ts | 30 ++++++++++ 7 files changed, 79 insertions(+), 132 deletions(-) create mode 100644 src/utils/gdrive.ts diff --git a/src/app/api/getData/route.ts b/src/app/api/getData/route.ts index f88c82f..f2dab9a 100644 --- a/src/app/api/getData/route.ts +++ b/src/app/api/getData/route.ts @@ -3,90 +3,58 @@ import { drive_v3 } from "googleapis"; import { NextRequest, NextResponse } from "next/server"; import getSearchParams from "utils/apiHelper/getSearchParams"; -import { - decryptData, - encryptData, -} from "utils/encryptionHelper/hash"; +import { decryptData, encryptData } from "utils/encryptionHelper/hash"; import ExtendedError from "utils/extendedError"; +import { gdriveFilesList } from "utils/gdrive"; import gdrive from "utils/gdriveInstance"; import { IGDriveFiles } from "types/api/files"; -import { - APIGetFileResponse, - ErrorResponse, -} from "types/api/response"; +import { APIGetFileResponse, ErrorResponse } from "types/api/response"; function isHiddenFile(name: string) { - return gIndexConfig.apiConfig.hiddenFiles.find((item) => - name.startsWith(item), - ) - ? true - : false; + return gIndexConfig.apiConfig.hiddenFiles.find((item) => name.startsWith(item)) ? true : false; } -function generateFileObject( - data: drive_v3.Schema$File, -): IGDriveFiles { +function generateFileObject(data: drive_v3.Schema$File): IGDriveFiles { return { mimeType: data.mimeType as string, fileExtension: (data.fileExtension as string) ?? null, encryptedId: encryptData(data.id as string), name: data.name as string, trashed: (data.trashed as boolean) ?? false, - modifiedTime: new Date( - data.modifiedTime as string, - ).toLocaleDateString(), - encryptedWebContentLink: encryptData( - data.webContentLink as string, - ), + modifiedTime: new Date(data.modifiedTime as string).toLocaleDateString(), + encryptedWebContentLink: encryptData(data.webContentLink as string), size: Number(data.size ?? 0), thumbnailLink: (data.thumbnailLink as string) ?? null, imageMediaMetadata: data.imageMediaMetadata ? { width: Number(data.imageMediaMetadata.width ?? 0), - height: Number( - data.imageMediaMetadata.height ?? 0, - ), - rotation: Number( - data.imageMediaMetadata.rotation ?? 0, - ), + height: Number(data.imageMediaMetadata.height ?? 0), + rotation: Number(data.imageMediaMetadata.rotation ?? 0), } : null, videoMediaMetadata: data.videoMediaMetadata ? { width: Number(data.videoMediaMetadata.width ?? 0), - height: Number( - data.videoMediaMetadata.height ?? 0, - ), - durationMillis: Number( - data.videoMediaMetadata.durationMillis ?? 0, - ), + height: Number(data.videoMediaMetadata.height ?? 0), + durationMillis: Number(data.videoMediaMetadata.durationMillis ?? 0), } : null, }; } -function generateFolderObject( - data: drive_v3.Schema$File, -): IGDriveFiles { +function generateFolderObject(data: drive_v3.Schema$File): IGDriveFiles { return { mimeType: data.mimeType as string, encryptedId: encryptData(data.id as string), name: data.name as string, trashed: (data.trashed as boolean) ?? false, - modifiedTime: new Date( - data.modifiedTime as string, - ).toLocaleDateString(), + modifiedTime: new Date(data.modifiedTime as string).toLocaleDateString(), }; } export async function GET(request: NextRequest) { const reqStart = Date.now(); try { - const { encryptedId, isFile, pageToken } = - getSearchParams(request.url, [ - "encryptedId", - "isFile", - "pageToken", - ]); + const { encryptedId, isFile, pageToken } = getSearchParams(request.url, ["encryptedId", "isFile", "pageToken"]); let data: { file: IGDriveFiles | null; @@ -105,52 +73,34 @@ export async function GET(request: NextRequest) { fields: gIndexConfig.apiConfig.defaultField, }); if (isHiddenFile(fileContent.data.name as string)) - throw new ExtendedError( - "File not found", - 404, - "File you are looking for is not found", - ); + throw new ExtendedError("File not found", 404, "File you are looking for is not found"); - const payload: IGDriveFiles = generateFileObject( - fileContent.data, - ); + const payload: IGDriveFiles = generateFileObject(fileContent.data); data.file = payload; } else { - const filterName = gIndexConfig.apiConfig.hiddenFiles - .map((item) => `name != '${item}'`) - .join(" and "); + const filterName = gIndexConfig.apiConfig.hiddenFiles.map((item) => `name != '${item}'`).join(" and "); const query: string[] = [ ...gIndexConfig.apiConfig.defaultQuery, - `'${ - encryptedId - ? decryptData(encryptedId) - : gIndexConfig.apiConfig.rootFolder - }' in parents`, + `'${encryptedId ? decryptData(encryptedId) : gIndexConfig.apiConfig.rootFolder}' in parents`, `${filterName}`, ]; - const folderContent = await gdrive.files.list({ + const folderContent = await gdriveFilesList({ q: query.join(" and "), fields: `files(${gIndexConfig.apiConfig.defaultField}), nextPageToken`, orderBy: gIndexConfig.apiConfig.defaultOrder, pageSize: gIndexConfig.apiConfig.itemsPerPage, pageToken: pageToken ?? undefined, + supportsAllDrives: gIndexConfig.apiConfig.isTeamDrive, + includeItemsFromAllDrives: gIndexConfig.apiConfig.isTeamDrive, }); const listFolders: IGDriveFiles[] = folderContent.data.files - ?.filter( - (item) => - item.mimeType === - "application/vnd.google-apps.folder", - ) + ?.filter((item) => item.mimeType === "application/vnd.google-apps.folder") .map((item) => generateFolderObject(item)) ?? []; const listFiles: IGDriveFiles[] = folderContent.data.files - ?.filter( - (item) => - item.mimeType !== - "application/vnd.google-apps.folder", - ) + ?.filter((item) => item.mimeType !== "application/vnd.google-apps.folder") .map((item) => generateFileObject(item)) ?? []; data = { diff --git a/src/app/api/getPassword/route.ts b/src/app/api/getPassword/route.ts index 91930e1..432e023 100644 --- a/src/app/api/getPassword/route.ts +++ b/src/app/api/getPassword/route.ts @@ -2,52 +2,32 @@ import gIndexConfig from "config"; import { NextRequest, NextResponse } from "next/server"; import getSearchParams from "utils/apiHelper/getSearchParams"; -import { - decryptData, - encryptData, -} from "utils/encryptionHelper/hash"; +import { decryptData, encryptData } from "utils/encryptionHelper/hash"; import ExtendedError from "utils/extendedError"; +import { gdriveFilesList } from "utils/gdrive"; import gdrive from "utils/gdriveInstance"; -import { - APIGetPasswordResponse, - ErrorResponse, -} from "types/api/response"; +import { APIGetPasswordResponse, ErrorResponse } from "types/api/response"; export async function GET(request: NextRequest) { const reqStart = Date.now(); try { const { path } = getSearchParams(request.url, ["path"]); - if (!path) - throw new ExtendedError( - "Path is required", - 400, - "Can't find path in query, please check your query", - ); - const mappedPath: Record< - "name" | "id" | "mimeType", - string - >[] = JSON.parse(decryptData(path)); - const passwordParents = mappedPath.map( - (path) => `'${decryptData(path.id)}' in parents`, - ); + if (!path) throw new ExtendedError("Path is required", 400, "Can't find path in query, please check your query"); + const mappedPath: Record<"name" | "id" | "mimeType", string>[] = JSON.parse(decryptData(path)); + const passwordParents = mappedPath.map((path) => `'${decryptData(path.id)}' in parents`); const query: string[] = [ "trashed = false", - `name = '${ - gIndexConfig.apiConfig.specialFile.password - }' and (${passwordParents.join(" or ")})`, + `name = '${gIndexConfig.apiConfig.specialFile.password}' and (${passwordParents.join(" or ")})`, ]; - const passwordData = await gdrive.files.list({ + const passwordData = await gdriveFilesList({ q: query.join(" and "), fields: "files(id, name, parents)", }); const protectedPaths = mappedPath.map((path) => { - const isPasswordExist = passwordData.data.files?.find( - (file) => - file.parents?.[0] === decryptData(path.id), - ); + const isPasswordExist = passwordData.data.files?.find((file) => file.parents?.[0] === decryptData(path.id)); if (isPasswordExist) return { name: path.name, @@ -77,21 +57,14 @@ export async function GET(request: NextRequest) { ); }); - const passwordContent = await Promise.all( - _passwordContent, - ); + const passwordContent = await Promise.all(_passwordContent); let prevPath = [""]; - let password: Record< - "relativePath" | "password", - string - >[] = []; + let password: Record<"relativePath" | "password", string>[] = []; mappedPath.forEach((path) => { prevPath.push(path.name); - const isPasswordExist = passwordContent.find( - (password) => password.path === path.name, - ); + const isPasswordExist = passwordContent.find((password) => password.path === path.name); if (isPasswordExist) { password.push({ relativePath: prevPath.join("/"), diff --git a/src/app/api/getReadme/route.ts b/src/app/api/getReadme/route.ts index fd7f307..cb4b52e 100644 --- a/src/app/api/getReadme/route.ts +++ b/src/app/api/getReadme/route.ts @@ -3,29 +3,21 @@ import { NextRequest, NextResponse } from "next/server"; import getSearchParams from "utils/apiHelper/getSearchParams"; import { decryptData } from "utils/encryptionHelper/hash"; +import { gdriveFilesList } from "utils/gdrive"; import gdrive from "utils/gdriveInstance"; -import { - APIGetReadmeResponse, - ErrorResponse, -} from "types/api/response"; +import { APIGetReadmeResponse, ErrorResponse } from "types/api/response"; export async function GET(request: NextRequest) { const reqStart = Date.now(); try { - const { encryptedId } = getSearchParams(request.url, [ - "encryptedId", - ]); + const { encryptedId } = getSearchParams(request.url, ["encryptedId"]); const query: string[] = [ ...gIndexConfig.apiConfig.defaultQuery, - `'${ - encryptedId - ? decryptData(encryptedId) - : gIndexConfig.apiConfig.rootFolder - }' in parents`, + `'${encryptedId ? decryptData(encryptedId) : gIndexConfig.apiConfig.rootFolder}' in parents`, ]; - const folderContent = await gdrive.files.list({ + const folderContent = await gdriveFilesList({ q: query.join(" and "), fields: `files(${gIndexConfig.apiConfig.defaultField}), nextPageToken`, orderBy: gIndexConfig.apiConfig.defaultOrder, @@ -33,9 +25,7 @@ export async function GET(request: NextRequest) { pageToken: undefined, }); const isReadmeExist = folderContent.data.files?.find( - (file) => - file.name === - gIndexConfig.apiConfig.specialFile.readme, + (file) => file.name === gIndexConfig.apiConfig.specialFile.readme, ); let data: string | null = null; diff --git a/src/app/api/search/route.ts b/src/app/api/search/route.ts index b6c268f..6c1f365 100644 --- a/src/app/api/search/route.ts +++ b/src/app/api/search/route.ts @@ -4,6 +4,7 @@ import { NextRequest, NextResponse } from "next/server"; import getSearchParams from "utils/apiHelper/getSearchParams"; import { encryptData } from "utils/encryptionHelper/hash"; import ExtendedError from "utils/extendedError"; +import { gdriveFilesList } from "utils/gdrive"; import gdrive from "utils/gdriveInstance"; import { IGDriveFiles } from "types/api/files"; @@ -15,7 +16,7 @@ export async function GET(request: NextRequest) { const { query } = getSearchParams(request.url, ["query"]); if (!query) throw new ExtendedError("Missing query", 400, "Search query is required"); - const searchFiles = await gdrive.files.list({ + const searchFiles = await gdriveFilesList({ q: [...gIndexConfig.apiConfig.defaultQuery, `name contains '${query}'`].join(" and "), fields: `files(${gIndexConfig.apiConfig.defaultField}), nextPageToken`, orderBy: "name_natural desc", diff --git a/src/config/gIndex.config.ts b/src/config/gIndex.config.ts index 06fe9d1..774bfa1 100644 --- a/src/config/gIndex.config.ts +++ b/src/config/gIndex.config.ts @@ -39,8 +39,8 @@ const config: gIndexConfig = { * You need to create a new folder and share it with the service account * Then, copy the folder id and paste it here */ - rootFolder: "1KgPV6QB1GYT8fmn2uTfbtr9rDXqcRR0j", - + rootFolder: "", + isTeamDrive: false, // Set this to true if you're using Team Drive defaultQuery: ["trashed = false", "(not mimeType contains 'google-apps' or mimeType contains 'folder')"], defaultField: "id, name, mimeType, thumbnailLink, fileExtension, modifiedTime, size, imageMediaMetadata, videoMediaMetadata, webContentLink, trashed", @@ -165,6 +165,7 @@ interface gIndexConfig { apiConfig: { rootFolder: string; + isTeamDrive: boolean; defaultQuery: string[]; defaultField: string; defaultOrder: string; diff --git a/src/pages/[...path]/index.tsx b/src/pages/[...path]/index.tsx index 35420e4..1857586 100644 --- a/src/pages/[...path]/index.tsx +++ b/src/pages/[...path]/index.tsx @@ -10,6 +10,7 @@ import PasswordLayout from "components/Layout/Password"; import PreviewLayout from "components/Layout/Preview"; import { decryptData, encryptData } from "utils/encryptionHelper/hash"; +import { gdriveFilesList } from "utils/gdrive"; import gdrive from "utils/gdriveInstance"; import { addNewPassword, checkPathPassword } from "utils/passwordHelper"; @@ -196,10 +197,11 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { const fetchRootId = gdrive.files.get({ fileId: gIndexConfig.apiConfig.rootFolder, fields: "id", + supportsAllDrives: true, }); const fetchPathId = path.map(async (path) => { const query = ["trashed = false", `name = '${path}'`]; - const fetchFolderContents = await gdrive.files.list({ + const fetchFolderContents = await gdriveFilesList({ q: `${query.join(" and ")}`, fields: "files(id, name, mimeType, parents)", }); diff --git a/src/utils/gdrive.ts b/src/utils/gdrive.ts new file mode 100644 index 0000000..55ef4e7 --- /dev/null +++ b/src/utils/gdrive.ts @@ -0,0 +1,30 @@ +import gIndexConfig from "config"; +import { GaxiosResponse } from "gaxios"; +import type { drive_v3 } from "googleapis"; + +import gdrive from "utils/gdriveInstance"; + +interface ListFiles extends drive_v3.Params$Resource$Files$List {} + +export async function gdriveFilesList({ + q, + fields, + orderBy, + pageSize, + pageToken, +}: ListFiles): Promise> { + const result = await gdrive.files.list({ + q, + fields, + orderBy, + pageSize, + pageToken, + ...(gIndexConfig.apiConfig.isTeamDrive && { + supportsAllDrives: true, + includeItemsFromAllDrives: true, + driveId: gIndexConfig.apiConfig.rootFolder, + corpora: "drive", + }), + }); + return result; +} From 904fa23307d36423007207a9cc76fecd146ac1b0 Mon Sep 17 00:00:00 2001 From: loadingthedev Date: Mon, 25 Sep 2023 11:45:45 +0530 Subject: [PATCH 2/3] feat: shared drive file download and view --- src/app/api/download/[encryptedId]/[fileName]/route.ts | 2 ++ src/app/api/getData/route.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/app/api/download/[encryptedId]/[fileName]/route.ts b/src/app/api/download/[encryptedId]/[fileName]/route.ts index 1837237..f64bd9e 100644 --- a/src/app/api/download/[encryptedId]/[fileName]/route.ts +++ b/src/app/api/download/[encryptedId]/[fileName]/route.ts @@ -37,10 +37,12 @@ export async function GET( const _fileMeta = gdrive.files.get({ fileId: decryptData(data.id), fields: "id, name, size, mimeType, webContentLink", + supportsAllDrives: gIndexConfig.apiConfig.isTeamDrive, }); const _fileContent = gdrive.files.get( { fileId: decryptData(data.id), + supportsAllDrives: gIndexConfig.apiConfig.isTeamDrive, alt: "media", }, { responseType: "stream" }, diff --git a/src/app/api/getData/route.ts b/src/app/api/getData/route.ts index f2dab9a..004ae48 100644 --- a/src/app/api/getData/route.ts +++ b/src/app/api/getData/route.ts @@ -71,6 +71,7 @@ export async function GET(request: NextRequest) { const fileContent = await gdrive.files.get({ fileId: decryptData(encryptedId), fields: gIndexConfig.apiConfig.defaultField, + supportsAllDrives: gIndexConfig.apiConfig.isTeamDrive, }); if (isHiddenFile(fileContent.data.name as string)) throw new ExtendedError("File not found", 404, "File you are looking for is not found"); From 1c5e7c57f3618479a59102ee593fde8d78a412b9 Mon Sep 17 00:00:00 2001 From: loadingthedev Date: Mon, 25 Sep 2023 20:26:03 +0530 Subject: [PATCH 3/3] changes regarding td support --- src/app/api/getPassword/route.ts | 1 + src/app/api/search/redirect/[encryptedId]/route.ts | 2 ++ src/app/api/search/route.ts | 1 - src/config/gIndex.config.ts | 2 +- src/pages/[...path]/index.tsx | 2 +- 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/app/api/getPassword/route.ts b/src/app/api/getPassword/route.ts index 432e023..d3a1afa 100644 --- a/src/app/api/getPassword/route.ts +++ b/src/app/api/getPassword/route.ts @@ -47,6 +47,7 @@ export async function GET(request: NextRequest) { { fileId: path.passwordId as string, alt: "media", + supportsAllDrives: gIndexConfig.apiConfig.isTeamDrive, }, { responseType: "text" }, ) diff --git a/src/app/api/search/redirect/[encryptedId]/route.ts b/src/app/api/search/redirect/[encryptedId]/route.ts index c33e76a..40bc12d 100644 --- a/src/app/api/search/redirect/[encryptedId]/route.ts +++ b/src/app/api/search/redirect/[encryptedId]/route.ts @@ -20,6 +20,7 @@ export async function GET(request: NextRequest, { params }: { params: { encrypte const fileContent = await gdrive.files.get({ fileId: lastId, fields: "id, name, mimeType, parents", + supportsAllDrives: gIndexConfig.apiConfig.isTeamDrive, }); if (fileContent) { path.push(fileContent.data.name as string); @@ -29,6 +30,7 @@ export async function GET(request: NextRequest, { params }: { params: { encrypte const folderContent = await gdrive.files.get({ fileId: lastId, fields: "id, name, mimeType, parents", + supportsAllDrives: gIndexConfig.apiConfig.isTeamDrive, }); if (folderContent.data.id === gIndexConfig.apiConfig.rootFolder) { lastId = ""; diff --git a/src/app/api/search/route.ts b/src/app/api/search/route.ts index 6c1f365..2b448ba 100644 --- a/src/app/api/search/route.ts +++ b/src/app/api/search/route.ts @@ -5,7 +5,6 @@ import getSearchParams from "utils/apiHelper/getSearchParams"; import { encryptData } from "utils/encryptionHelper/hash"; import ExtendedError from "utils/extendedError"; import { gdriveFilesList } from "utils/gdrive"; -import gdrive from "utils/gdriveInstance"; import { IGDriveFiles } from "types/api/files"; import { APISearchResponse, ErrorResponse } from "types/api/response"; diff --git a/src/config/gIndex.config.ts b/src/config/gIndex.config.ts index 774bfa1..f077f5c 100644 --- a/src/config/gIndex.config.ts +++ b/src/config/gIndex.config.ts @@ -39,7 +39,7 @@ const config: gIndexConfig = { * You need to create a new folder and share it with the service account * Then, copy the folder id and paste it here */ - rootFolder: "", + rootFolder: "1KgPV6QB1GYT8fmn2uTfbtr9rDXqcRR0j", isTeamDrive: false, // Set this to true if you're using Team Drive defaultQuery: ["trashed = false", "(not mimeType contains 'google-apps' or mimeType contains 'folder')"], defaultField: diff --git a/src/pages/[...path]/index.tsx b/src/pages/[...path]/index.tsx index 1857586..9a3a67d 100644 --- a/src/pages/[...path]/index.tsx +++ b/src/pages/[...path]/index.tsx @@ -197,7 +197,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { const fetchRootId = gdrive.files.get({ fileId: gIndexConfig.apiConfig.rootFolder, fields: "id", - supportsAllDrives: true, + supportsAllDrives: gIndexConfig.apiConfig.isTeamDrive, }); const fetchPathId = path.map(async (path) => { const query = ["trashed = false", `name = '${path}'`];