Enable raw file content proxy for files up to 4MB (#546)

Co-authored-by: spencerwooo <spencer.woo@outlook.com>
This commit is contained in:
浅秋枫影 2022-03-16 16:56:32 +08:00 committed by GitHub
parent 520792d86f
commit 7df0453269
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 23 additions and 65 deletions

View file

@ -82,13 +82,6 @@ const DownloadButtonGroup = () => {
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')}
/> */}
<DownloadButton
onClickCallback={() => {
clipboard.copy(`${getBaseUrl()}/api/raw/?path=${asPath}${hashedToken ? `&odpt=${hashedToken}` : ''}`)

View file

@ -370,7 +370,7 @@ const FileListing: FC<{ query?: ParsedUrlQuery }> = ({ query }) => {
{readmeFile && (
<div className="mt-4">
<MarkdownPreview file={readmeFile} path={path} standalone={false} />
<MarkdownPreview file={readmeFile} path={path} standalone={false} proxy={true} />
</div>
)}
</>

View file

@ -20,11 +20,16 @@ const MarkdownPreview: FC<{
file: any
path: string
standalone?: boolean
}> = ({ file, path, standalone = true }) => {
proxy?: boolean
}> = ({ file, path, standalone = true, proxy = false }) => {
// The parent folder of the markdown file, which is also the relative image folder
const parentPath = standalone ? path.substring(0, path.lastIndexOf('/')) : path
const { response: content, error, validating } = useFileContent(`/api/raw/?path=${parentPath}/${file.name}`, path)
const {
response: content,
error,
validating,
} = useFileContent(`/api/raw/?path=${parentPath}/${file.name}${proxy ? `&proxy=true` : ''}`, path)
const { t } = useTranslation()
// Check if the image is relative path instead of a absolute url

View file

@ -8,7 +8,6 @@ const PDFEmbedPreview: React.FC<{ file: any }> = ({ file }) => {
const { asPath } = useRouter()
const hashedToken = getStoredToken(asPath)
// const url = `/api/proxy?url=${encodeURIComponent(...)}&inline=true`
const pdfPath = encodeURIComponent(
`${getBaseUrl()}/api/raw/?path=${asPath}${hashedToken ? `&odpt=${hashedToken}` : ''}`
)

View file

@ -132,14 +132,6 @@ const VideoPreview: FC<{ file: OdFileObject }> = ({ file }) => {
btnText={t('Download')}
btnIcon="file-download"
/>
{/* <DownloadButton
onClickCallback={() =>
window.open(`/api/proxy?url=${encodeURIComponent(...)}`)
}
btnColor="teal"
btnText={t('Proxy download')}
btnIcon="download"
/> */}
<DownloadButton
onClickCallback={() => {
clipboard.copy(`${getBaseUrl()}/api/raw/?path=${asPath}${hashedToken ? `&odpt=${hashedToken}` : ''}`)

View file

@ -1,42 +0,0 @@
import axios from 'axios'
import type { NextApiRequest, NextApiResponse } from 'next'
import apiConfig from '../../config/api.config'
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
// 'inline' is used for previewing PDF files inside the browser directly
const { url, inline = false } = req.query
if (!url || typeof url !== 'string') {
res.status(400).json({ error: 'Bad request, URL is not valid.' })
return
}
// Only handle urls that start with OneDrive's own direct link (or SharePoint's):
// https://public.*.files.1drv.com/y4m0G_0GPeS8AXGrux-lVV79eU1F38VbWxtCSi-8-aUkBLeZH1H6...
const hostname = new URL(url).hostname
if (hostname.match(new RegExp(apiConfig.directLinkRegex)) === null) {
res
.status(400)
.json({ error: `URL forbidden, only OneDrive direct links that match ${apiConfig.directLinkRegex} are allowed.` })
return
}
const { headers, data: stream } = await axios.get(url as string, {
responseType: 'stream',
})
// Check if requested file is PDF based on content-type
if (headers['content-type'] === 'application/pdf' && inline) {
// Get filename from content-disposition header
const filename = headers['content-disposition'].split(/filename[*]?=/)[1]
// Remove original content-disposition header
delete headers['content-disposition']
// Add new inline content-disposition header along with filename
headers['content-disposition'] = `inline; filename*=UTF-8''${filename}`
}
// Send data stream as response
res.writeHead(200, headers)
stream.pipe(res)
}

View file

@ -4,7 +4,7 @@ import type { NextApiRequest, NextApiResponse } from 'next'
import axios from 'axios'
import Cors from 'cors'
import { driveApi } from '../../config/api.config'
import { driveApi, cacheControlHeader } from '../../config/api.config'
import { encodePath, getAccessToken, checkAuthRoute } from '.'
// CORS middleware for raw links: https://nextjs.org/docs/api-routes/api-middlewares
@ -28,7 +28,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
return
}
const { path = '/', odpt = '' } = req.query
const { path = '/', odpt = '', proxy = false } = req.query
// Sometimes the path parameter is defaulted to '[...path]' which we need to handle
if (path === '[...path]') {
@ -65,12 +65,23 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
headers: { Authorization: `Bearer ${accessToken}` },
params: {
// OneDrive international version fails when only selecting the downloadUrl (what a stupid bug)
select: 'id,@microsoft.graph.downloadUrl',
select: 'id,size,@microsoft.graph.downloadUrl',
},
})
if ('@microsoft.graph.downloadUrl' in data) {
res.redirect(data['@microsoft.graph.downloadUrl'])
// Only proxy raw file content response for files up to 4MB
if (proxy && 'size' in data && data['size'] < 4194304) {
const { headers, data: stream } = await axios.get(data['@microsoft.graph.downloadUrl'] as string, {
responseType: 'stream',
})
headers['Cache-Control'] = cacheControlHeader
// Send data stream as response
res.writeHead(200, headers)
stream.pipe(res)
} else {
res.redirect(data['@microsoft.graph.downloadUrl'])
}
} else {
res.status(404).json({ error: 'No download url found.' })
}