Skip to content

Commit

Permalink
Standalone raw file redirects (spencerwooo#428)
Browse files Browse the repository at this point in the history
  • Loading branch information
spencerwooo authored Feb 14, 2022
1 parent 0fda1c9 commit 9493ce9
Show file tree
Hide file tree
Showing 30 changed files with 364 additions and 184 deletions.
15 changes: 12 additions & 3 deletions components/CustomEmbedLinkMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { useClipboard } from 'use-clipboard-copy'

import { getBaseUrl } from '../utils/getBaseUrl'
import { getStoredToken } from '../utils/protectedRouteHandler'
import { getReadablePath } from '../utils/getReadablePath'

export default function CustomEmbedLinkMenu({
path,
Expand All @@ -18,6 +20,9 @@ export default function CustomEmbedLinkMenu({
}) {
const { t } = useTranslation()
const clipboard = useClipboard()

const hashedToken = getStoredToken(path)

const closeMenu = () => setMenuOpen(false)

const filename = path.substring(path.lastIndexOf('/') + 1)
Expand Down Expand Up @@ -77,21 +82,25 @@ export default function CustomEmbedLinkMenu({
/>
<h4 className="py-2 text-xs font-medium uppercase tracking-wider">{t('Default')}</h4>
<div className="mb-2 rounded border border-gray-400/20 bg-gray-50 p-1 font-mono dark:bg-gray-800">
{`${getBaseUrl()}/api?path=${path}&raw=true`}
{`${getBaseUrl()}/api/raw/?path=${getReadablePath(path)}${hashedToken ? `&odpt=${hashedToken}` : ''}`}
</div>
<h4 className="py-2 text-xs font-medium uppercase tracking-wider">{t('Customised')}</h4>
<div className="mb-2 rounded border border-gray-400/20 bg-gray-50 p-1 font-mono dark:bg-gray-800">
<span>{`${getBaseUrl()}/api/name/`}</span>
<span className="underline decoration-blue-400 decoration-wavy">{name}</span>
<span>{`?path=${path}&raw=true`}</span>
<span>{`/?path=${getReadablePath(path)}${hashedToken ? `&odpt=${hashedToken}` : ''}`}</span>
</div>
</div>

<div className="mb-2 mt-6 text-right">
<button
className="rounded-lg bg-gradient-to-r from-cyan-500 to-blue-500 px-4 py-2 text-center text-sm font-medium text-white hover:bg-gradient-to-bl focus:ring-4 focus:ring-cyan-300 dark:focus:ring-cyan-800"
onClick={() => {
clipboard.copy(`${getBaseUrl()}/api/name/${name}?path=${path}&raw=true`)
clipboard.copy(
`${getBaseUrl()}/api/name/${name}/?path=${getReadablePath(path)}${
hashedToken ? `&odpt=${hashedToken}` : ''
}`
)
toast.success(t('Copied customised link to clipboard.'))
closeMenu()
}}
Expand Down
23 changes: 14 additions & 9 deletions components/DownloadBtnGtoup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useRouter } from 'next/router'

import { getBaseUrl } from '../utils/getBaseUrl'
import { getReadablePath } from '../utils/getReadablePath'
import { getStoredToken } from '../utils/protectedRouteHandler'
import CustomEmbedLinkMenu from './CustomEmbedLinkMenu'

const btnStyleMap = (btnColor?: string) => {
Expand Down Expand Up @@ -62,8 +63,10 @@ export const DownloadButton = ({
)
}

const DownloadButtonGroup: React.FC<{ downloadUrl: string }> = ({ downloadUrl }) => {
const DownloadButtonGroup = () => {
const { asPath } = useRouter()
const hashedToken = getStoredToken(asPath)

const clipboard = useClipboard()
const [menuOpen, setMenuOpen] = useState(false)

Expand All @@ -74,22 +77,24 @@ const DownloadButtonGroup: React.FC<{ downloadUrl: string }> = ({ downloadUrl })
<CustomEmbedLinkMenu menuOpen={menuOpen} setMenuOpen={setMenuOpen} path={asPath} />
<div className="flex flex-wrap justify-center gap-2">
<DownloadButton
onClickCallback={() => window.open(downloadUrl)}
onClickCallback={() => window.open(`/api/raw/?path=${asPath}${hashedToken ? `&odpt=${hashedToken}` : ''}`)}
btnColor="blue"
btnText={t('Download')}
btnIcon="file-download"
btnTitle={t('Download the file directly through OneDrive')}
/>
{/* <DownloadButton
onClickCallback={() => window.open(`/api/proxy?url=${encodeURIComponent(downloadUrl)}`)}
btnColor="teal"
btnText={t('Proxy download')}
btnIcon="download"
btnTitle={t('Download the file with the stream proxied through Vercel Serverless')}
/> */}
onClickCallback={() => window.open(`/api/proxy?url=${encodeURIComponent(downloadUrl)}`)}
btnColor="teal"
btnText={t('Proxy download')}
btnIcon="download"
btnTitle={t('Download the file with the stream proxied through Vercel Serverless')}
/> */}
<DownloadButton
onClickCallback={() => {
clipboard.copy(`${getBaseUrl()}/api?path=${getReadablePath(asPath)}&raw=true`)
clipboard.copy(
`${getBaseUrl()}/api/raw/?path=${getReadablePath(asPath)}${hashedToken ? `&odpt=${hashedToken}` : ''}`
)
toast.success(t('Copied direct link to clipboard.'))
}}
btnColor="pink"
Expand Down
21 changes: 13 additions & 8 deletions components/FileListing.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { OdFileObject, OdFolderChildren, OdFolderObject } from '../types'
import { ParsedUrlQuery } from 'querystring'
import { FC, MouseEventHandler, SetStateAction, useEffect, useRef, useState } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import toast, { Toaster } from 'react-hot-toast'
import emojiRegex from 'emoji-regex'

import { ParsedUrlQuery } from 'querystring'
import { FC, MouseEventHandler, SetStateAction, useEffect, useRef, useState } from 'react'

import dynamic from 'next/dynamic'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
Expand All @@ -13,6 +13,7 @@ import useLocalStorage from '../utils/useLocalStorage'
import { getPreviewType, preview } from '../utils/getPreviewType'
import { useProtectedSWRInfinite } from '../utils/fetchWithSWR'
import { getExtension, getFileIcon } from '../utils/getFileIcon'
import { getStoredToken } from '../utils/protectedRouteHandler'
import {
DownloadingToast,
downloadMultipleFiles,
Expand All @@ -36,7 +37,6 @@ import ImagePreview from './previews/ImagePreview'
import DefaultPreview from './previews/DefaultPreview'
import { PreviewContainer } from './previews/Containers'

import type { OdFileObject, OdFolderChildren, OdFolderObject } from '../types'
import FolderListLayout from './FolderListLayout'
import FolderGridLayout from './FolderGridLayout'

Expand Down Expand Up @@ -134,9 +134,9 @@ export const Checkbox: FC<{
)
}

export const Downloading: FC<{ title: string }> = ({ title }) => {
export const Downloading: FC<{ title: string; style: string }> = ({ title, style }) => {
return (
<span title={title} className="rounded p-2" role="status">
<span title={title} className={`${style} rounded`} role="status">
<LoadingIcon
// Use fontawesome far theme via class `svg-inline--fa` to get style `vertical-align` only
// for consistent icon alignment, as class `align-*` cannot satisfy it
Expand All @@ -155,6 +155,7 @@ const FileListing: FC<{ query?: ParsedUrlQuery }> = ({ query }) => {
}>({})

const router = useRouter()
const hashedToken = getStoredToken(router.asPath)
const [layout, _] = useLocalStorage('preferredLayout', layouts[0])

const { t } = useTranslation()
Expand Down Expand Up @@ -237,7 +238,10 @@ const FileListing: FC<{ query?: ParsedUrlQuery }> = ({ query }) => {
const folder = folderName ? decodeURIComponent(folderName) : undefined
const files = getFiles()
.filter(c => selected[c.id])
.map(c => ({ name: c.name, url: c['@microsoft.graph.downloadUrl'] }))
.map(c => ({
name: c.name,
url: `/api/raw/?path=${path}/${c.name}${hashedToken ? `&odpt=${hashedToken}` : ''}`,
}))

if (files.length == 1) {
const el = document.createElement('a')
Expand Down Expand Up @@ -278,9 +282,10 @@ const FileListing: FC<{ query?: ParsedUrlQuery }> = ({ query }) => {
)
continue
}
const hashedTokenForPath = getStoredToken(p)
yield {
name: c?.name,
url: c ? c['@microsoft.graph.downloadUrl'] : undefined,
url: `/api/raw/?path=${p}${hashedTokenForPath ? `&odpt=${hashedTokenForPath}` : ''}`,
path: p,
isFolder,
}
Expand Down
20 changes: 15 additions & 5 deletions components/FolderGridLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ import { getBaseUrl } from '../utils/getBaseUrl'
import { formatModifiedDateTime } from '../utils/fileDetails'
import { getReadablePath } from '../utils/getReadablePath'
import { Checkbox, ChildIcon, ChildName, Downloading } from './FileListing'
import { getStoredToken } from '../utils/protectedRouteHandler'

const GridItem = ({ c, path }: { c: OdFolderChildren; path: string }) => {
// We use the generated medium thumbnail for rendering preview images (excluding folders)
const thumbnailUrl = 'folder' in c ? null : `/api/thumbnail?path=${path}&size=medium`
const hashedToken = getStoredToken(path)
const thumbnailUrl =
'folder' in c ? null : `/api/thumbnail/?path=${path}&size=medium${hashedToken ? `&odpt=${hashedToken}` : ''}`

// Some thumbnails are broken, so we check for onerror event in the image component
const [brokenThumbnail, setBrokenThumbnail] = useState(false)
Expand Down Expand Up @@ -66,6 +69,7 @@ const FolderGridLayout = ({
toast,
}) => {
const clipboard = useClipboard()
const hashedToken = getStoredToken(path)

const { t } = useTranslation()

Expand All @@ -84,7 +88,7 @@ const FolderGridLayout = ({
title={t('Select all files')}
/>
{totalGenerating ? (
<Downloading title={t('Downloading selected files, refresh page to cancel')} />
<Downloading title={t('Downloading selected files, refresh page to cancel')} style="p-1.5" />
) : (
<button
title={t('Download selected files')}
Expand Down Expand Up @@ -118,7 +122,7 @@ const FolderGridLayout = ({
<FontAwesomeIcon icon={['far', 'copy']} />
</span>
{folderGenerating[c.id] ? (
<Downloading title={t('Downloading folder, refresh page to cancel')} />
<Downloading title={t('Downloading folder, refresh page to cancel')} style="px-1.5 py-1" />
) : (
<span
title={t('Download folder')}
Expand All @@ -135,7 +139,11 @@ const FolderGridLayout = ({
title={t('Copy raw file permalink')}
className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600"
onClick={() => {
clipboard.copy(`${getBaseUrl()}/api?path=${getReadablePath(getItemPath(c.name))}&raw=true`)
clipboard.copy(
`${getBaseUrl()}/api/raw/?path=${getReadablePath(getItemPath(c.name))}${
hashedToken ? `&odpt=${hashedToken}` : ''
}`
)
toast.success(t('Copied raw file permalink.'))
}}
>
Expand All @@ -144,7 +152,9 @@ const FolderGridLayout = ({
<a
title={t('Download file')}
className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600"
href={c['@microsoft.graph.downloadUrl']}
href={`${getBaseUrl()}/api/raw/?path=${getReadablePath(getItemPath(c.name))}${
hashedToken ? `&odpt=${hashedToken}` : ''
}`}
>
<FontAwesomeIcon icon={['far', 'arrow-alt-circle-down']} />
</a>
Expand Down
17 changes: 11 additions & 6 deletions components/FolderListLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { humanFileSize, formatModifiedDateTime } from '../utils/fileDetails'
import { getReadablePath } from '../utils/getReadablePath'

import { Downloading, Checkbox, ChildIcon, ChildName } from './FileListing'
import { getStoredToken } from '../utils/protectedRouteHandler'

const FileListItem: FC<{ fileContent: OdFolderChildren }> = ({ fileContent: c }) => {
return (
Expand Down Expand Up @@ -45,9 +46,13 @@ const FolderListLayout = ({
toast,
}) => {
const clipboard = useClipboard()
const hashedToken = getStoredToken(path)

const { t } = useTranslation()

// Get item path from item name
const getItemPath = (name: string) => `${path === '/' ? '' : path}/${encodeURIComponent(name)}`

return (
<div className="rounded bg-white dark:bg-gray-900 dark:text-gray-100">
<div className="grid grid-cols-12 items-center space-x-2 border-b border-gray-900/10 px-3 dark:border-gray-500/30">
Expand All @@ -72,7 +77,7 @@ const FolderListLayout = ({
title={t('Select files')}
/>
{totalGenerating ? (
<Downloading title={t('Downloading selected files, refresh page to cancel')} />
<Downloading title={t('Downloading selected files, refresh page to cancel')} style="p-1.5" />
) : (
<button
title={t('Download selected files')}
Expand Down Expand Up @@ -113,7 +118,7 @@ const FolderListLayout = ({
<FontAwesomeIcon icon={['far', 'copy']} />
</span>
{folderGenerating[c.id] ? (
<Downloading title={t('Downloading folder, refresh page to cancel')} />
<Downloading title={t('Downloading folder, refresh page to cancel')} style="px-1.5 py-1" />
) : (
<span
title={t('Download folder')}
Expand All @@ -134,9 +139,9 @@ const FolderListLayout = ({
className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600"
onClick={() => {
clipboard.copy(
`${getBaseUrl()}/api?path=${getReadablePath(
`${path === '/' ? '' : path}/${encodeURIComponent(c.name)}`
)}&raw=true`
`${getBaseUrl()}/api/raw/?path=${getReadablePath(getItemPath(c.name))}${
hashedToken ? `&odpt=${hashedToken}` : ''
}`
)
toast.success(t('Copied raw file permalink.'))
}}
Expand All @@ -146,7 +151,7 @@ const FolderListLayout = ({
<a
title={t('Download file')}
className="cursor-pointer rounded px-1.5 py-1 hover:bg-gray-300 dark:hover:bg-gray-600"
href={c['@microsoft.graph.downloadUrl']}
href={`/api/raw/?path=${getItemPath(c.name)}${hashedToken ? `&odpt=${hashedToken}` : ''}`}
>
<FontAwesomeIcon icon={['far', 'arrow-alt-circle-down']} />
</a>
Expand Down
2 changes: 1 addition & 1 deletion components/MultiFileDownloader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ export async function* traverseFolder(path: string): AsyncGenerator<TraverseItem
i,
path,
data: await fetcher(
next ? `/api?path=${path}&next=${next}` : `/api?path=${path}`,
next ? `/api/?path=${path}&next=${next}` : `/api?path=${path}`,
hashedToken ?? undefined
).catch(error => ({ i, path, error })),
}
Expand Down
4 changes: 2 additions & 2 deletions components/SearchModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function mapAbsolutePath(path: string): string {
function useDriveItemSearch() {
const [query, setQuery] = useState('')
const searchDriveItem = async (q: string) => {
const { data } = await axios.get<OdSearchResult>(`/api/search?q=${q}`)
const { data } = await axios.get<OdSearchResult>(`/api/search/?q=${q}`)

// Map parentReference to the absolute path of the search result
data.map(item => {
Expand Down Expand Up @@ -111,7 +111,7 @@ function SearchResultItemTemplate({
}

function SearchResultItemLoadRemote({ result }: { result: OdSearchResult[number] }) {
const { data, error }: SWRResponse<OdDriveItem, string> = useSWR(`/api/item?id=${result.id}`, fetcher)
const { data, error }: SWRResponse<OdDriveItem, string> = useSWR(`/api/item/?id=${result.id}`, fetcher)

const { t } = useTranslation()

Expand Down
8 changes: 5 additions & 3 deletions components/previews/AudioPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import DownloadButtonGroup from '../DownloadBtnGtoup'
import { DownloadBtnContainer, PreviewContainer } from './Containers'
import { LoadingIcon } from '../Loading'
import { formatModifiedDateTime } from '../../utils/fileDetails'
import { getStoredToken } from '../../utils/protectedRouteHandler'

enum PlayerState {
Loading,
Expand All @@ -21,12 +22,13 @@ enum PlayerState {
const AudioPreview: FC<{ file: OdFileObject }> = ({ file }) => {
const { t } = useTranslation()
const { asPath } = useRouter()
const hashedToken = getStoredToken(asPath)

const rapRef = useRef<ReactAudioPlayer>(null)
const [playerStatus, setPlayerStatus] = useState(PlayerState.Loading)

// Render audio thumbnail, and also check for broken thumbnails
const thumbnail = `/api/thumbnail?path=${asPath}&size=medium`
const thumbnail = `/api/thumbnail/?path=${asPath}&size=medium${hashedToken ? `&odpt=${hashedToken}` : ''}`
const [brokenThumbnail, setBrokenThumbnail] = useState(false)

useEffect(() => {
Expand Down Expand Up @@ -92,7 +94,7 @@ const AudioPreview: FC<{ file: OdFileObject }> = ({ file }) => {

<ReactAudioPlayer
className="h-11 w-full"
src={file['@microsoft.graph.downloadUrl']}
src={`/api/raw/?path=${asPath}${hashedToken ? `&odpt=${hashedToken}` : ''}`}
ref={rapRef}
controls
preload="auto"
Expand All @@ -102,7 +104,7 @@ const AudioPreview: FC<{ file: OdFileObject }> = ({ file }) => {
</PreviewContainer>

<DownloadBtnContainer>
<DownloadButtonGroup downloadUrl={file['@microsoft.graph.downloadUrl']} />
<DownloadButtonGroup />
</DownloadBtnContainer>
</>
)
Expand Down
Loading

0 comments on commit 9493ce9

Please sign in to comment.