chore: moves file handler into catch all GET rest handler

This commit is contained in:
Jarrod Flesch
2024-03-07 12:02:44 -05:00
parent fcd647832c
commit 42eed07dfd
6 changed files with 138 additions and 144 deletions

View File

@@ -1,6 +0,0 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from '@payload-config'
import { GET_STATIC_FILE } from '@payloadcms/next/routes/index.js'
export const GET = GET_STATIC_FILE(config)

View File

@@ -6,5 +6,3 @@ export {
PATCH as REST_PATCH,
POST as REST_POST,
} from './rest/index.js'
export { GET as GET_STATIC_FILE } from './rest/[collection]/file/[filename]/route.js'

View File

@@ -1,135 +0,0 @@
import type { Collection, PayloadRequest, SanitizedConfig, Where } from 'payload/types'
import fsPromises from 'fs/promises'
import httpStatus from 'http-status'
import path from 'path'
import { executeAccess } from 'payload/auth'
import { APIError, Forbidden } from 'payload/errors'
import { streamFile } from '../../../../../next-stream-file/index.js'
import { createPayloadRequest } from '../../../../../utilities/createPayloadRequest.js'
import { RouteError } from '../../../RouteError.js'
import { endpointsAreDisabled } from '../../../checkEndpoints.js'
async function checkFileAccess({
collection,
filename,
req,
}: {
collection: Collection
filename: string
req: PayloadRequest
}) {
const { config } = collection
const disableEndpoints = endpointsAreDisabled({ endpoints: config.endpoints, request: req })
if (disableEndpoints) return disableEndpoints
const accessResult = await executeAccess({ isReadingStaticFile: true, req }, config.access.read)
if (typeof accessResult === 'object') {
const queryToBuild: Where = {
and: [
{
or: [
{
filename: {
equals: filename,
},
},
],
},
accessResult,
],
}
if (config.upload.imageSizes) {
config.upload.imageSizes.forEach(({ name }) => {
queryToBuild.and[0].or.push({
[`sizes.${name}.filename`]: {
equals: filename,
},
})
})
}
const doc = await req.payload.db.findOne({
collection: config.slug,
req,
where: queryToBuild,
})
if (!doc) {
throw new Forbidden(req.t)
}
}
}
export const GET =
(config: Promise<SanitizedConfig>) =>
async (request: Request, { params }: { params: { collection: string; filename: string } }) => {
const { collection: collectionSlug, filename } = params
let req: PayloadRequest
let collection: Collection
try {
req = await createPayloadRequest({
config,
params: { collection: collectionSlug },
request,
})
collection = req.payload.collections?.[collectionSlug]
if (!collection) {
throw new APIError(`Media collection not found: ${collectionSlug}`, httpStatus.BAD_REQUEST)
}
if (!collection.config.upload) {
throw new APIError(
`This collection is not an upload collection: ${collectionSlug}`,
httpStatus.BAD_REQUEST,
)
}
if (collection.config.upload.disableLocalStorage && !collection.config.upload.handlers) {
throw new APIError(
`This collection has local storage disabled: ${collectionSlug}`,
httpStatus.BAD_REQUEST,
)
}
await checkFileAccess({
collection,
filename,
req,
})
let response: Response = null
if (collection.config.upload.handlers?.length) {
for (const handler of collection.config.upload.handlers) {
response = await handler(req, { params })
}
return response
}
const fileDir = collection.config.upload?.staticDir || collection.config.slug
const filePath = path.resolve(`${fileDir}/${filename}`)
const stats = await fsPromises.stat(filePath)
const data = streamFile(filePath)
return new Response(data, {
headers: new Headers({
'content-length': stats.size + '',
}),
status: httpStatus.OK,
})
} catch (error) {
return RouteError({
collection,
err: error,
req,
})
}
}

View File

@@ -0,0 +1,59 @@
import type { Collection, PayloadRequest, Where } from 'payload/types'
import { executeAccess } from 'payload/auth'
import { Forbidden } from 'payload/errors'
import { endpointsAreDisabled } from '../checkEndpoints.js'
export async function checkFileAccess({
collection,
filename,
req,
}: {
collection: Collection
filename: string
req: PayloadRequest
}) {
const { config } = collection
const disableEndpoints = endpointsAreDisabled({ endpoints: config.endpoints, request: req })
if (disableEndpoints) return disableEndpoints
const accessResult = await executeAccess({ isReadingStaticFile: true, req }, config.access.read)
if (typeof accessResult === 'object') {
const queryToBuild: Where = {
and: [
{
or: [
{
filename: {
equals: filename,
},
},
],
},
accessResult,
],
}
if (config.upload.imageSizes) {
config.upload.imageSizes.forEach(({ name }) => {
queryToBuild.and[0].or.push({
[`sizes.${name}.filename`]: {
equals: filename,
},
})
})
}
const doc = await req.payload.db.findOne({
collection: config.slug,
req,
where: queryToBuild,
})
if (!doc) {
throw new Forbidden(req.t)
}
}
}

View File

@@ -0,0 +1,73 @@
import type { Collection, PayloadRequest } from 'payload/types'
import fsPromises from 'fs/promises'
import httpStatus from 'http-status'
import path from 'path'
import { APIError } from 'payload/errors'
import { streamFile } from '../../../next-stream-file/index.js'
import { RouteError } from '../RouteError.js'
import { checkFileAccess } from './checkFileAccess.js'
// /:collectionSlug/file/:filename
type Args = {
collection: Collection
filename: string
req: PayloadRequest
}
export const getFile = async ({ collection, filename, req }: Args): Promise<Response> => {
try {
if (!collection.config.upload) {
throw new APIError(
`This collection is not an upload collection: ${collection.config.slug}`,
httpStatus.BAD_REQUEST,
)
}
if (collection.config.upload.disableLocalStorage && !collection.config.upload.handlers) {
throw new APIError(
`This collection has local storage disabled: ${collection.config.slug}`,
httpStatus.BAD_REQUEST,
)
}
await checkFileAccess({
collection,
filename,
req,
})
let response: Response = null
if (collection.config.upload.handlers?.length) {
for (const handler of collection.config.upload.handlers) {
response = await handler(req, {
params: {
collection: collection.config.slug,
filename,
},
})
}
return response
}
const fileDir = collection.config.upload?.staticDir || collection.config.slug
const filePath = path.resolve(`${fileDir}/${filename}`)
const stats = await fsPromises.stat(filePath)
const data = streamFile(filePath)
return new Response(data, {
headers: new Headers({
'content-length': stats.size + '',
}),
status: httpStatus.OK,
})
} catch (error) {
return RouteError({
collection,
err: error,
req,
})
}
}

View File

@@ -37,6 +37,7 @@ import { findVersions } from './collections/findVersions.js'
import { restoreVersion } from './collections/restoreVersion.js'
import { update } from './collections/update.js'
import { updateByID } from './collections/updateByID.js'
import { getFile } from './files/getFile.js'
import { docAccess as docAccessGlobal } from './globals/docAccess.js'
import { findOne } from './globals/findOne.js'
import { findVersionByID as findVersionByIdGlobal } from './globals/findVersionByID.js'
@@ -55,6 +56,7 @@ const endpoints = {
'doc-versions-by-id': findVersionByID,
find,
findByID,
getFile,
init,
me,
versions: findVersions,
@@ -205,7 +207,10 @@ export const GET =
}
break
case 3:
if (`doc-${slug2}-by-id` in endpoints.collection.GET) {
if (slug2 === 'file') {
// /:collection/file/:filename
res = await endpoints.collection.GET.getFile({ collection, filename: slug3, req })
} else if (`doc-${slug2}-by-id` in endpoints.collection.GET) {
// /:collection/access/:id
// /:collection/versions/:id