diff --git a/config/site.json b/config/site.json index ffb9579..c3ae4e7 100644 --- a/config/site.json +++ b/config/site.json @@ -1,6 +1,7 @@ { "icon": "/icons/128.png", "title": "Spencer's OneDrive Index", + "maxItems": 100, "googleFont": "Rubik", "googleFontWeights": [ "400", diff --git a/pages/api/index.ts b/pages/api/index.ts index 2e2e728..25f38ce 100644 --- a/pages/api/index.ts +++ b/pages/api/index.ts @@ -12,7 +12,7 @@ const encodePath = (path: string) => { if (encodedPath === '/' || encodedPath === '') { return '' } - return encodeURIComponent(':' + encodedPath) + return `:${encodeURIComponent(encodedPath)}` } // Store access token in memory, cuz Vercel doesn't provide key-value storage natively @@ -43,7 +43,7 @@ const getAccessToken = async () => { } export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const { path = '/', raw = false } = req.query + const { path = '/', raw = false, t = '' } = req.query if (path === '[...path]') { res.status(400).json({ error: 'No path specified.' }) return @@ -113,6 +113,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } // Querying current path identity (file or folder) and follow up query childrens in folder + // console.log(accessToken) + const { data: identityData } = await axios.get(requestUrl, { headers: { Authorization: `Bearer ${accessToken}` }, params: { @@ -123,11 +125,29 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) if ('folder' in identityData) { const { data: folderData } = await axios.get(`${requestUrl}:/children`, { headers: { Authorization: `Bearer ${accessToken}` }, - params: { - select: '@microsoft.graph.downloadUrl,name,size,id,lastModifiedDateTime,folder,file', - }, + params: t + ? { + select: '@microsoft.graph.downloadUrl,name,size,id,lastModifiedDateTime,folder,file', + top: siteConfig.maxItems, + $skipToken: t, + } + : { + select: '@microsoft.graph.downloadUrl,name,size,id,lastModifiedDateTime,folder,file', + top: siteConfig.maxItems, + }, }) - res.status(200).json({ path, identityData, folderData }) + + // Extract next page token from full @odata.nextLink + const nextPage = folderData['@odata.nextLink'] + ? folderData['@odata.nextLink'].match(/&\$skiptoken=(.+)/i)[1] + : null + + // Return paging token if specified + if (nextPage) { + res.status(200).json({ path, identityData, folderData, nextPage }) + } else { + res.status(200).json({ path, identityData, folderData }) + } return } res.status(200).json({ path, identityData }) diff --git a/utils/tools.ts b/utils/tools.ts index 5de7921..0c90c9c 100644 --- a/utils/tools.ts +++ b/utils/tools.ts @@ -15,7 +15,7 @@ export const getBaseUrl = () => { return '' } -// Common axios fetch function +// Common axios fetch function for use with useSWR const fetcher = (url: string, token?: string) => { return token ? axios @@ -45,6 +45,7 @@ export const useStaleSWR = (url: Key, path: string = '') => { return useSWR([url, hashedToken], fetcher, revalidationOptions) } +// Hash password token with SHA256 const encryptToken = (token: string) => { return sha256(token).toString() } @@ -58,6 +59,11 @@ export const compareHashedToken = (odTokenHeader: string, dotPassword: string) = return encryptToken(dotPassword.trim()) === odTokenHeader } +/** + * Match the specified route against a list of predefined routes + * @param route directory path + * @returns whether the directory is protected + */ export const matchProtectedRoute = (route: string) => { const protectedRoutes: string[] = siteConfig.protectedRoutes let authTokenPath = ''