diff --git a/src/app/actions.ts b/src/app/actions.ts index 18461be..f5081b7 100644 --- a/src/app/actions.ts +++ b/src/app/actions.ts @@ -4,14 +4,14 @@ import crypto from "crypto"; import { revalidatePath } from "next/cache"; import { cookies } from "next/headers"; import { z } from "zod"; -import { Schema_File } from "~/schema"; -import { decryptData, encryptData } from "~/utils/encryptionHelper/hash"; +import { decryptData, encryptData } from "~/utils/encryptionHelper"; import gdrive, { gdriveNoCache } from "~/utils/gdriveInstance"; import { Constant } from "~/types/constant"; +import { Schema_File } from "~/types/schema"; -import config from "~/config/gIndex.config"; +import config from "config"; export async function CheckSitePassword(): Promise<{ success: boolean; @@ -36,9 +36,7 @@ export async function CheckSitePassword(): Promise<{ const password = store.get(Constant.cookies_SitePassword)?.value || ""; const decryptedPassword = await decryptData(password); if (decryptedPassword !== process.env.SITE_PASSWORD) - throw new Error( - "Saved password is incorrect, please re-enter the password", - ); + throw new Error("Saved password is incorrect, please re-enter the password"); return { success: true, @@ -156,32 +154,58 @@ export async function CheckPaths(paths: string[]): Promise< const data = await Promise.all(promises); const notFoundIndex = data.findIndex((item) => !item); - if (notFoundIndex !== -1) - throw new Error(`Path not found: ${paths[notFoundIndex]}`); + if (notFoundIndex !== -1) throw new Error(`Path not found: ${paths[notFoundIndex]}`); // Check if each path is valid let valid = true; let invalidPath: string | undefined; const decryptedRootId = await decryptData(config.apiConfig.rootFolder); - for (const [index, path] of data.entries()) { + const dataWithIndex = data.map((item, index) => { + return { + ...item, + index: index, + }; + }); + const filteredPaths: { path: string; data: { id: string; parents: string; mimeType: string }[] }[] = []; + + for (const item of dataWithIndex) { if (!valid) break; - if (index === 0) { - if ( - path?.data[0].parents === decryptedRootId || - path?.data[0].parents === decryptedSharedDrive - ) - break; - } else { - if (path?.data[0].parents === data[index - 1]?.data[0].id) break; + // Loop through data + // If the current index is 0, check if the parents is the root folder or shared drive + // If the current index is not 0, check if the parents is the previous data id from the filteredPaths + + // Use filteredPaths because it will only contain the valid path + // So if filteredPaths length is not equal to paths length, then it means there is an invalid path + for (const path of item.data || []) { + if (item.index === 0) { + if (path.parents === decryptedRootId || path.parents === decryptedSharedDrive) { + filteredPaths.push({ + path: item.path!, + data: [{ id: path.id!, parents: path.parents!, mimeType: path.mimeType! }], + }); + break; + } + } else { + if (path.parents === filteredPaths[item.index - 1]?.data?.[0].id) { + filteredPaths.push({ + path: item.path!, + data: [{ id: path.id!, parents: path.parents!, mimeType: path.mimeType! }], + }); + break; + } + } } - valid = false; - invalidPath = data[index]?.path; } + if (filteredPaths.length !== paths.length) { + valid = false; + invalidPath = dataWithIndex[filteredPaths.length]?.path; + } + if (!valid) throw new Error(`Invalid path: ${invalidPath}`); const ids: { path: string; id: string }[] = []; - for (const item of data) { + for (const item of filteredPaths) { if (item) { const encryptedId = await encryptData(item.data[0].id as string); ids.push({ @@ -203,9 +227,7 @@ export async function CheckPaths(paths: string[]): Promise< }; } } -export async function CheckPassword( - paths: { path: string; id: string }[], -): Promise< +export async function CheckPassword(paths: { path: string; id: string }[]): Promise< | { success: false; message?: string; @@ -250,21 +272,15 @@ export async function CheckPassword( // To save processing time, skip if password file not found if (!password.files?.length) return { success: true }; - const protectedFolder = password.files - .filter((file) => ids.includes(file.parents?.[0] as string)) - ?.shift(); // Shift to get only the nearest folder + const protectedFolder = password.files.filter((file) => ids.includes(file.parents?.[0] as string))?.shift(); // Shift to get only the nearest folder // To save processing time, skip if all of the paths are not protected if (!protectedFolder) return { success: true }; const store = cookies(); - const folderIndex = ids.findIndex( - (item) => item === protectedFolder.parents?.[0], - ); + const folderIndex = ids.findIndex((item) => item === protectedFolder.parents?.[0]); - const cookiesValue = JSON.parse( - store.get(Constant.cookies_FolderPassword)?.value || "{}", - ); + const cookiesValue = JSON.parse(store.get(Constant.cookies_FolderPassword)?.value || "{}"); const currentFolder = paths[folderIndex]; if (!cookiesValue[currentFolder.id]) throw { @@ -321,9 +337,7 @@ export async function SetPassword( }> { try { const store = cookies(); - const cookiesValue = JSON.parse( - store.get(Constant.cookies_FolderPassword)?.value || "{}", - ); + const cookiesValue = JSON.parse(store.get(Constant.cookies_FolderPassword)?.value || "{}"); const updatedValue = { ...cookiesValue, [path]: await encryptData(password), @@ -352,13 +366,7 @@ export async function SetPassword( } } -export async function GetFiles({ - id, - pageToken, -}: { - id?: string; - pageToken?: string; -}): Promise<{ +export async function GetFiles({ id, pageToken }: { id?: string; pageToken?: string }): Promise<{ files: z.infer[]; nextPageToken?: string; }> { @@ -372,14 +380,10 @@ export async function GetFiles({ decryptedSharedDrive = await decryptData(config.apiConfig.sharedDrive); } - const filterName = config.apiConfig.hiddenFiles - .map((item) => `not name = '${item}'`) - .join(" and "); - const query: string = [ - ...config.apiConfig.defaultQuery, - `'${decryptedId}' in parents`, - `${filterName}`, - ].join(" and "); + const filterName = config.apiConfig.hiddenFiles.map((item) => `not name = '${item}'`).join(" and "); + const query: string = [...config.apiConfig.defaultQuery, `'${decryptedId}' in parents`, `${filterName}`].join( + " and ", + ); const data = await gdrive.files.list({ q: query, @@ -396,21 +400,16 @@ export async function GetFiles({ }); const encryptedData: z.infer[] = []; - if (!data.data.files?.length) - return { files: [], nextPageToken: undefined }; + if (!data.data.files?.length) return { files: [], nextPageToken: undefined }; for (const file of data.data.files) { encryptedData.push({ mimeType: file.mimeType as string, encryptedId: await encryptData(file.id as string), name: file.name as string, trashed: (file.trashed as boolean) ?? false, - modifiedTime: new Date( - file.modifiedTime as string, - ).toLocaleDateString(), + modifiedTime: new Date(file.modifiedTime as string).toLocaleDateString(), fileExtension: file.fileExtension || undefined, - encryptedWebContentLink: file.webContentLink - ? await encryptData(file.webContentLink) - : undefined, + encryptedWebContentLink: file.webContentLink ? await encryptData(file.webContentLink) : undefined, size: file.size ? Number(file.size) : undefined, thumbnailLink: file.thumbnailLink || undefined, imageMediaMetadata: file.imageMediaMetadata @@ -441,9 +440,7 @@ export async function GetFiles({ throw new Error(e.message); } } -export async function GetFile( - encryptedId: string, -): Promise> { +export async function GetFile(encryptedId: string): Promise> { try { const decryptedId = await decryptData(encryptedId); const { data: file } = await gdrive.files.get({ @@ -459,9 +456,7 @@ export async function GetFile( trashed: (file.trashed as boolean) ?? false, modifiedTime: new Date(file.modifiedTime as string).toLocaleDateString(), fileExtension: file.fileExtension || undefined, - encryptedWebContentLink: file.webContentLink - ? await encryptData(file.webContentLink) - : undefined, + encryptedWebContentLink: file.webContentLink ? await encryptData(file.webContentLink) : undefined, size: file.size ? Number(file.size) : undefined, thumbnailLink: file.thumbnailLink || undefined, imageMediaMetadata: file.imageMediaMetadata @@ -504,9 +499,7 @@ export async function SearchFile( const query: string[] = [ ...config.apiConfig.defaultQuery, `name contains '${keyword}'`, - config.apiConfig.hiddenFiles - .map((item) => `not name = '${item}'`) - .join(" and "), + config.apiConfig.hiddenFiles.map((item) => `not name = '${item}'`).join(" and "), ]; const data = await gdrive.files.list({ q: query.join(" and "), @@ -522,21 +515,16 @@ export async function SearchFile( }), }); const encryptedData: z.infer[] = []; - if (!data.data.files?.length) - return { files: [], nextPageToken: undefined }; + if (!data.data.files?.length) return { files: [], nextPageToken: undefined }; for (const file of data.data.files) { encryptedData.push({ mimeType: file.mimeType as string, encryptedId: await encryptData(file.id as string), name: file.name as string, trashed: (file.trashed as boolean) ?? false, - modifiedTime: new Date( - file.modifiedTime as string, - ).toLocaleDateString(), + modifiedTime: new Date(file.modifiedTime as string).toLocaleDateString(), fileExtension: file.fileExtension || undefined, - encryptedWebContentLink: file.webContentLink - ? await encryptData(file.webContentLink) - : undefined, + encryptedWebContentLink: file.webContentLink ? await encryptData(file.webContentLink) : undefined, size: file.size ? Number(file.size) : undefined, thumbnailLink: file.thumbnailLink || undefined, imageMediaMetadata: file.imageMediaMetadata @@ -611,8 +599,7 @@ export async function GetContent(encryptedId: string): Promise { }, ); - if (typeof file !== "string") - throw new Error("It seems the file is not a text file"); + if (typeof file !== "string") throw new Error("It seems the file is not a text file"); return file as string; } catch (error) { @@ -621,9 +608,7 @@ export async function GetContent(encryptedId: string): Promise { throw new Error(e.message); } } -export async function GetReadme( - encryptedId: string | undefined, -): Promise { +export async function GetReadme(encryptedId: string | undefined): Promise { try { let decryptedId; if (encryptedId) decryptedId = await decryptData(encryptedId); @@ -653,6 +638,8 @@ export async function GetReadme( corpora: "drive", }), }); + + console.log(data.files?.length); if (!data.files?.length) return null; let file; @@ -663,16 +650,15 @@ export async function GetReadme( // Check by mimetype, use google-apps.shortcut last file = data.files.find((item) => item.mimeType === "text/markdown"); if (!file) { - file = data.files.find( - (item) => item.mimeType === "application/vnd.google-apps.shortcut", - ); + file = data.files.find((item) => item.mimeType === "application/vnd.google-apps.shortcut"); } } if (!file) return null; if ( file.mimeType !== "text/markdown" && - file.mimeType !== "application/vnd.google-apps.shortcut" + file.mimeType !== "application/vnd.google-apps.shortcut" && + file.mimeType !== "text/plain" ) return null; @@ -685,8 +671,7 @@ export async function GetReadme( data: { shortcutDetails: { targetId: string; targetMimeType: string } }; }; if (!shortcutData.shortcutDetails.targetId) return null; - if (shortcutData.shortcutDetails.targetMimeType !== "text/markdown") - return null; + if (shortcutData.shortcutDetails.targetMimeType !== "text/markdown") return null; const { data: content } = await gdrive.files.get( { @@ -720,9 +705,7 @@ export async function GetReadme( throw new Error(e.message); } } -export async function GetBanner( - encryptedId: string | undefined, -): Promise { +export async function GetBanner(encryptedId: string | undefined): Promise { try { let decryptedId; if (encryptedId) decryptedId = await decryptData(encryptedId); @@ -770,14 +753,10 @@ export async function GetWebContent(encrypted: string): Promise { throw new Error(e.message); } } -export async function CreateDownloadToken( - duration: number = config.apiConfig.temporaryTokenDuration, -): Promise { +export async function CreateDownloadToken(duration: number = config.apiConfig.temporaryTokenDuration): Promise { try { const data = { - expiredAt: - Date.now() + - duration * 60 * 60 * 1000 * config.apiConfig.temporaryTokenDuration, + expiredAt: Date.now() + duration * 60 * 60 * 1000 * config.apiConfig.temporaryTokenDuration, }; const token = await encryptData(JSON.stringify(data)); return token; @@ -796,8 +775,7 @@ export async function CheckDownloadToken(token: string): Promise<{ if (decryptToken.expiredAt < Date.now()) { return { success: false, - message: - "Download token expired, please click the download button again to get a new token", + message: "Download token expired, please click the download button again to get a new token", }; } @@ -824,10 +802,7 @@ export async function GenerateAESKey(): Promise { throw new Error(e.message); } } -export async function VerifyAESKey( - data: string, - key: string, -): Promise { +export async function VerifyAESKey(data: string, key: string): Promise { try { let paddedKey; if (key.length < 16) {