Merge pull request #4 from loadingthedev/support-shared-drive

Add support for Shared drive
This commit is contained in:
Arief Rachmawan
2023-09-25 22:03:59 +07:00
committed by GitHub
9 changed files with 84 additions and 132 deletions
@@ -37,10 +37,12 @@ export async function GET(
const _fileMeta = gdrive.files.get({ const _fileMeta = gdrive.files.get({
fileId: decryptData(data.id), fileId: decryptData(data.id),
fields: "id, name, size, mimeType, webContentLink", fields: "id, name, size, mimeType, webContentLink",
supportsAllDrives: gIndexConfig.apiConfig.isTeamDrive,
}); });
const _fileContent = gdrive.files.get( const _fileContent = gdrive.files.get(
{ {
fileId: decryptData(data.id), fileId: decryptData(data.id),
supportsAllDrives: gIndexConfig.apiConfig.isTeamDrive,
alt: "media", alt: "media",
}, },
{ responseType: "stream" }, { responseType: "stream" },
+24 -73
View File
@@ -3,90 +3,58 @@ import { drive_v3 } from "googleapis";
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import getSearchParams from "utils/apiHelper/getSearchParams"; import getSearchParams from "utils/apiHelper/getSearchParams";
import { import { decryptData, encryptData } from "utils/encryptionHelper/hash";
decryptData,
encryptData,
} from "utils/encryptionHelper/hash";
import ExtendedError from "utils/extendedError"; import ExtendedError from "utils/extendedError";
import { gdriveFilesList } from "utils/gdrive";
import gdrive from "utils/gdriveInstance"; import gdrive from "utils/gdriveInstance";
import { IGDriveFiles } from "types/api/files"; import { IGDriveFiles } from "types/api/files";
import { import { APIGetFileResponse, ErrorResponse } from "types/api/response";
APIGetFileResponse,
ErrorResponse,
} from "types/api/response";
function isHiddenFile(name: string) { function isHiddenFile(name: string) {
return gIndexConfig.apiConfig.hiddenFiles.find((item) => return gIndexConfig.apiConfig.hiddenFiles.find((item) => name.startsWith(item)) ? true : false;
name.startsWith(item),
)
? true
: false;
} }
function generateFileObject( function generateFileObject(data: drive_v3.Schema$File): IGDriveFiles {
data: drive_v3.Schema$File,
): IGDriveFiles {
return { return {
mimeType: data.mimeType as string, mimeType: data.mimeType as string,
fileExtension: (data.fileExtension as string) ?? null, fileExtension: (data.fileExtension as string) ?? null,
encryptedId: encryptData(data.id as string), encryptedId: encryptData(data.id as string),
name: data.name as string, name: data.name as string,
trashed: (data.trashed as boolean) ?? false, trashed: (data.trashed as boolean) ?? false,
modifiedTime: new Date( modifiedTime: new Date(data.modifiedTime as string).toLocaleDateString(),
data.modifiedTime as string, encryptedWebContentLink: encryptData(data.webContentLink as string),
).toLocaleDateString(),
encryptedWebContentLink: encryptData(
data.webContentLink as string,
),
size: Number(data.size ?? 0), size: Number(data.size ?? 0),
thumbnailLink: (data.thumbnailLink as string) ?? null, thumbnailLink: (data.thumbnailLink as string) ?? null,
imageMediaMetadata: data.imageMediaMetadata imageMediaMetadata: data.imageMediaMetadata
? { ? {
width: Number(data.imageMediaMetadata.width ?? 0), width: Number(data.imageMediaMetadata.width ?? 0),
height: Number( height: Number(data.imageMediaMetadata.height ?? 0),
data.imageMediaMetadata.height ?? 0, rotation: Number(data.imageMediaMetadata.rotation ?? 0),
),
rotation: Number(
data.imageMediaMetadata.rotation ?? 0,
),
} }
: null, : null,
videoMediaMetadata: data.videoMediaMetadata videoMediaMetadata: data.videoMediaMetadata
? { ? {
width: Number(data.videoMediaMetadata.width ?? 0), width: Number(data.videoMediaMetadata.width ?? 0),
height: Number( height: Number(data.videoMediaMetadata.height ?? 0),
data.videoMediaMetadata.height ?? 0, durationMillis: Number(data.videoMediaMetadata.durationMillis ?? 0),
),
durationMillis: Number(
data.videoMediaMetadata.durationMillis ?? 0,
),
} }
: null, : null,
}; };
} }
function generateFolderObject( function generateFolderObject(data: drive_v3.Schema$File): IGDriveFiles {
data: drive_v3.Schema$File,
): IGDriveFiles {
return { return {
mimeType: data.mimeType as string, mimeType: data.mimeType as string,
encryptedId: encryptData(data.id as string), encryptedId: encryptData(data.id as string),
name: data.name as string, name: data.name as string,
trashed: (data.trashed as boolean) ?? false, trashed: (data.trashed as boolean) ?? false,
modifiedTime: new Date( modifiedTime: new Date(data.modifiedTime as string).toLocaleDateString(),
data.modifiedTime as string,
).toLocaleDateString(),
}; };
} }
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
const reqStart = Date.now(); const reqStart = Date.now();
try { try {
const { encryptedId, isFile, pageToken } = const { encryptedId, isFile, pageToken } = getSearchParams(request.url, ["encryptedId", "isFile", "pageToken"]);
getSearchParams(request.url, [
"encryptedId",
"isFile",
"pageToken",
]);
let data: { let data: {
file: IGDriveFiles | null; file: IGDriveFiles | null;
@@ -103,54 +71,37 @@ export async function GET(request: NextRequest) {
const fileContent = await gdrive.files.get({ const fileContent = await gdrive.files.get({
fileId: decryptData(encryptedId), fileId: decryptData(encryptedId),
fields: gIndexConfig.apiConfig.defaultField, fields: gIndexConfig.apiConfig.defaultField,
supportsAllDrives: gIndexConfig.apiConfig.isTeamDrive,
}); });
if (isHiddenFile(fileContent.data.name as string)) if (isHiddenFile(fileContent.data.name as string))
throw new ExtendedError( throw new ExtendedError("File not found", 404, "File you are looking for is not found");
"File not found",
404,
"File you are looking for is not found",
);
const payload: IGDriveFiles = generateFileObject( const payload: IGDriveFiles = generateFileObject(fileContent.data);
fileContent.data,
);
data.file = payload; data.file = payload;
} else { } else {
const filterName = gIndexConfig.apiConfig.hiddenFiles const filterName = gIndexConfig.apiConfig.hiddenFiles.map((item) => `name != '${item}'`).join(" and ");
.map((item) => `name != '${item}'`)
.join(" and ");
const query: string[] = [ const query: string[] = [
...gIndexConfig.apiConfig.defaultQuery, ...gIndexConfig.apiConfig.defaultQuery,
`'${ `'${encryptedId ? decryptData(encryptedId) : gIndexConfig.apiConfig.rootFolder}' in parents`,
encryptedId
? decryptData(encryptedId)
: gIndexConfig.apiConfig.rootFolder
}' in parents`,
`${filterName}`, `${filterName}`,
]; ];
const folderContent = await gdrive.files.list({ const folderContent = await gdriveFilesList({
q: query.join(" and "), q: query.join(" and "),
fields: `files(${gIndexConfig.apiConfig.defaultField}), nextPageToken`, fields: `files(${gIndexConfig.apiConfig.defaultField}), nextPageToken`,
orderBy: gIndexConfig.apiConfig.defaultOrder, orderBy: gIndexConfig.apiConfig.defaultOrder,
pageSize: gIndexConfig.apiConfig.itemsPerPage, pageSize: gIndexConfig.apiConfig.itemsPerPage,
pageToken: pageToken ?? undefined, pageToken: pageToken ?? undefined,
supportsAllDrives: gIndexConfig.apiConfig.isTeamDrive,
includeItemsFromAllDrives: gIndexConfig.apiConfig.isTeamDrive,
}); });
const listFolders: IGDriveFiles[] = const listFolders: IGDriveFiles[] =
folderContent.data.files folderContent.data.files
?.filter( ?.filter((item) => item.mimeType === "application/vnd.google-apps.folder")
(item) =>
item.mimeType ===
"application/vnd.google-apps.folder",
)
.map((item) => generateFolderObject(item)) ?? []; .map((item) => generateFolderObject(item)) ?? [];
const listFiles: IGDriveFiles[] = const listFiles: IGDriveFiles[] =
folderContent.data.files folderContent.data.files
?.filter( ?.filter((item) => item.mimeType !== "application/vnd.google-apps.folder")
(item) =>
item.mimeType !==
"application/vnd.google-apps.folder",
)
.map((item) => generateFileObject(item)) ?? []; .map((item) => generateFileObject(item)) ?? [];
data = { data = {
+13 -39
View File
@@ -2,52 +2,32 @@ import gIndexConfig from "config";
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import getSearchParams from "utils/apiHelper/getSearchParams"; import getSearchParams from "utils/apiHelper/getSearchParams";
import { import { decryptData, encryptData } from "utils/encryptionHelper/hash";
decryptData,
encryptData,
} from "utils/encryptionHelper/hash";
import ExtendedError from "utils/extendedError"; import ExtendedError from "utils/extendedError";
import { gdriveFilesList } from "utils/gdrive";
import gdrive from "utils/gdriveInstance"; import gdrive from "utils/gdriveInstance";
import { import { APIGetPasswordResponse, ErrorResponse } from "types/api/response";
APIGetPasswordResponse,
ErrorResponse,
} from "types/api/response";
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
const reqStart = Date.now(); const reqStart = Date.now();
try { try {
const { path } = getSearchParams(request.url, ["path"]); const { path } = getSearchParams(request.url, ["path"]);
if (!path) if (!path) throw new ExtendedError("Path is required", 400, "Can't find path in query, please check your query");
throw new ExtendedError( const mappedPath: Record<"name" | "id" | "mimeType", string>[] = JSON.parse(decryptData(path));
"Path is required", const passwordParents = mappedPath.map((path) => `'${decryptData(path.id)}' in parents`);
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[] = [ const query: string[] = [
"trashed = false", "trashed = false",
`name = '${ `name = '${gIndexConfig.apiConfig.specialFile.password}' and (${passwordParents.join(" or ")})`,
gIndexConfig.apiConfig.specialFile.password
}' and (${passwordParents.join(" or ")})`,
]; ];
const passwordData = await gdrive.files.list({ const passwordData = await gdriveFilesList({
q: query.join(" and "), q: query.join(" and "),
fields: "files(id, name, parents)", fields: "files(id, name, parents)",
}); });
const protectedPaths = mappedPath.map((path) => { const protectedPaths = mappedPath.map((path) => {
const isPasswordExist = passwordData.data.files?.find( const isPasswordExist = passwordData.data.files?.find((file) => file.parents?.[0] === decryptData(path.id));
(file) =>
file.parents?.[0] === decryptData(path.id),
);
if (isPasswordExist) if (isPasswordExist)
return { return {
name: path.name, name: path.name,
@@ -67,6 +47,7 @@ export async function GET(request: NextRequest) {
{ {
fileId: path.passwordId as string, fileId: path.passwordId as string,
alt: "media", alt: "media",
supportsAllDrives: gIndexConfig.apiConfig.isTeamDrive,
}, },
{ responseType: "text" }, { responseType: "text" },
) )
@@ -77,21 +58,14 @@ export async function GET(request: NextRequest) {
); );
}); });
const passwordContent = await Promise.all( const passwordContent = await Promise.all(_passwordContent);
_passwordContent,
);
let prevPath = [""]; let prevPath = [""];
let password: Record< let password: Record<"relativePath" | "password", string>[] = [];
"relativePath" | "password",
string
>[] = [];
mappedPath.forEach((path) => { mappedPath.forEach((path) => {
prevPath.push(path.name); prevPath.push(path.name);
const isPasswordExist = passwordContent.find( const isPasswordExist = passwordContent.find((password) => password.path === path.name);
(password) => password.path === path.name,
);
if (isPasswordExist) { if (isPasswordExist) {
password.push({ password.push({
relativePath: prevPath.join("/"), relativePath: prevPath.join("/"),
+6 -16
View File
@@ -3,29 +3,21 @@ import { NextRequest, NextResponse } from "next/server";
import getSearchParams from "utils/apiHelper/getSearchParams"; import getSearchParams from "utils/apiHelper/getSearchParams";
import { decryptData } from "utils/encryptionHelper/hash"; import { decryptData } from "utils/encryptionHelper/hash";
import { gdriveFilesList } from "utils/gdrive";
import gdrive from "utils/gdriveInstance"; import gdrive from "utils/gdriveInstance";
import { import { APIGetReadmeResponse, ErrorResponse } from "types/api/response";
APIGetReadmeResponse,
ErrorResponse,
} from "types/api/response";
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
const reqStart = Date.now(); const reqStart = Date.now();
try { try {
const { encryptedId } = getSearchParams(request.url, [ const { encryptedId } = getSearchParams(request.url, ["encryptedId"]);
"encryptedId",
]);
const query: string[] = [ const query: string[] = [
...gIndexConfig.apiConfig.defaultQuery, ...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 "), q: query.join(" and "),
fields: `files(${gIndexConfig.apiConfig.defaultField}), nextPageToken`, fields: `files(${gIndexConfig.apiConfig.defaultField}), nextPageToken`,
orderBy: gIndexConfig.apiConfig.defaultOrder, orderBy: gIndexConfig.apiConfig.defaultOrder,
@@ -33,9 +25,7 @@ export async function GET(request: NextRequest) {
pageToken: undefined, pageToken: undefined,
}); });
const isReadmeExist = folderContent.data.files?.find( const isReadmeExist = folderContent.data.files?.find(
(file) => (file) => file.name === gIndexConfig.apiConfig.specialFile.readme,
file.name ===
gIndexConfig.apiConfig.specialFile.readme,
); );
let data: string | null = null; let data: string | null = null;
@@ -20,6 +20,7 @@ export async function GET(request: NextRequest, { params }: { params: { encrypte
const fileContent = await gdrive.files.get({ const fileContent = await gdrive.files.get({
fileId: lastId, fileId: lastId,
fields: "id, name, mimeType, parents", fields: "id, name, mimeType, parents",
supportsAllDrives: gIndexConfig.apiConfig.isTeamDrive,
}); });
if (fileContent) { if (fileContent) {
path.push(fileContent.data.name as string); 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({ const folderContent = await gdrive.files.get({
fileId: lastId, fileId: lastId,
fields: "id, name, mimeType, parents", fields: "id, name, mimeType, parents",
supportsAllDrives: gIndexConfig.apiConfig.isTeamDrive,
}); });
if (folderContent.data.id === gIndexConfig.apiConfig.rootFolder) { if (folderContent.data.id === gIndexConfig.apiConfig.rootFolder) {
lastId = ""; lastId = "";
+2 -2
View File
@@ -4,7 +4,7 @@ import { NextRequest, NextResponse } from "next/server";
import getSearchParams from "utils/apiHelper/getSearchParams"; import getSearchParams from "utils/apiHelper/getSearchParams";
import { encryptData } from "utils/encryptionHelper/hash"; import { encryptData } from "utils/encryptionHelper/hash";
import ExtendedError from "utils/extendedError"; import ExtendedError from "utils/extendedError";
import gdrive from "utils/gdriveInstance"; import { gdriveFilesList } from "utils/gdrive";
import { IGDriveFiles } from "types/api/files"; import { IGDriveFiles } from "types/api/files";
import { APISearchResponse, ErrorResponse } from "types/api/response"; import { APISearchResponse, ErrorResponse } from "types/api/response";
@@ -15,7 +15,7 @@ export async function GET(request: NextRequest) {
const { query } = getSearchParams(request.url, ["query"]); const { query } = getSearchParams(request.url, ["query"]);
if (!query) throw new ExtendedError("Missing query", 400, "Search query is required"); 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 "), q: [...gIndexConfig.apiConfig.defaultQuery, `name contains '${query}'`].join(" and "),
fields: `files(${gIndexConfig.apiConfig.defaultField}), nextPageToken`, fields: `files(${gIndexConfig.apiConfig.defaultField}), nextPageToken`,
orderBy: "name_natural desc", orderBy: "name_natural desc",
+2 -1
View File
@@ -40,7 +40,7 @@ const config: gIndexConfig = {
* Then, copy the folder id and paste it here * Then, copy the folder id and paste it here
*/ */
rootFolder: "1KgPV6QB1GYT8fmn2uTfbtr9rDXqcRR0j", 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')"], defaultQuery: ["trashed = false", "(not mimeType contains 'google-apps' or mimeType contains 'folder')"],
defaultField: defaultField:
"id, name, mimeType, thumbnailLink, fileExtension, modifiedTime, size, imageMediaMetadata, videoMediaMetadata, webContentLink, trashed", "id, name, mimeType, thumbnailLink, fileExtension, modifiedTime, size, imageMediaMetadata, videoMediaMetadata, webContentLink, trashed",
@@ -165,6 +165,7 @@ interface gIndexConfig {
apiConfig: { apiConfig: {
rootFolder: string; rootFolder: string;
isTeamDrive: boolean;
defaultQuery: string[]; defaultQuery: string[];
defaultField: string; defaultField: string;
defaultOrder: string; defaultOrder: string;
+3 -1
View File
@@ -10,6 +10,7 @@ import PasswordLayout from "components/Layout/Password";
import PreviewLayout from "components/Layout/Preview"; import PreviewLayout from "components/Layout/Preview";
import { decryptData, encryptData } from "utils/encryptionHelper/hash"; import { decryptData, encryptData } from "utils/encryptionHelper/hash";
import { gdriveFilesList } from "utils/gdrive";
import gdrive from "utils/gdriveInstance"; import gdrive from "utils/gdriveInstance";
import { addNewPassword, checkPathPassword } from "utils/passwordHelper"; import { addNewPassword, checkPathPassword } from "utils/passwordHelper";
@@ -196,10 +197,11 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
const fetchRootId = gdrive.files.get({ const fetchRootId = gdrive.files.get({
fileId: gIndexConfig.apiConfig.rootFolder, fileId: gIndexConfig.apiConfig.rootFolder,
fields: "id", fields: "id",
supportsAllDrives: gIndexConfig.apiConfig.isTeamDrive,
}); });
const fetchPathId = path.map(async (path) => { const fetchPathId = path.map(async (path) => {
const query = ["trashed = false", `name = '${path}'`]; const query = ["trashed = false", `name = '${path}'`];
const fetchFolderContents = await gdrive.files.list({ const fetchFolderContents = await gdriveFilesList({
q: `${query.join(" and ")}`, q: `${query.join(" and ")}`,
fields: "files(id, name, mimeType, parents)", fields: "files(id, name, mimeType, parents)",
}); });
+30
View File
@@ -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<GaxiosResponse<drive_v3.Schema$FileList>> {
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;
}