mirror of https://github.com/requarks/wiki
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
46 lines
1.6 KiB
46 lines
1.6 KiB
import crypto from 'node:crypto'
|
|
import { validate as uuidValidate } from 'uuid'
|
|
import type { FastifyInstance } from 'fastify'
|
|
|
|
/**
|
|
* A thumbnail is generated once, at upload time, and an asset that changes gets a new ID — so the
|
|
* bytes behind a given URL never change.
|
|
*/
|
|
const THUMB_CACHE = 'public, max-age=31536000, immutable'
|
|
|
|
/**
|
|
* _thumb Routes
|
|
*
|
|
* Public, like `_site` and `_user`: a thumbnail is a shrunken copy of an asset already served on the
|
|
* pages that embed it, and the URL has to be known to be asked for. Only assets that have a preview
|
|
* answer here — everything else, including every non-image, is a 404 the file manager draws a file
|
|
* type icon for.
|
|
*/
|
|
async function routes(app: FastifyInstance) {
|
|
app.get<{ Params: { fileName: string } }>('/:fileName', async (req, reply) => {
|
|
// -> `.webp` is part of the URL so that the extension matches what is served, but the ID is the
|
|
// only part that identifies anything
|
|
const assetId = req.params.fileName.replace(/\.webp$/i, '')
|
|
if (!uuidValidate(assetId)) {
|
|
return reply.notFound('Thumbnail not found')
|
|
}
|
|
|
|
const preview = await WIKI.models.assets.getThumbnail(assetId)
|
|
if (!preview) {
|
|
return reply.notFound('Thumbnail not found')
|
|
}
|
|
|
|
const etag = `"${crypto.createHash('sha1').update(preview).digest('hex')}"`
|
|
reply.header('ETag', etag)
|
|
reply.header('Cache-Control', THUMB_CACHE)
|
|
reply.header('X-Content-Type-Options', 'nosniff')
|
|
if (req.headers['if-none-match'] === etag) {
|
|
return reply.code(304).send()
|
|
}
|
|
|
|
return reply.type('image/webp').send(preview)
|
|
})
|
|
}
|
|
|
|
export default routes
|