also use accept header as source of auto format

This commit is contained in:
cupcakearmy 2022-02-05 23:22:23 +01:00
parent 6f3f9dd9d9
commit 3a80516401
No known key found for this signature in database
GPG Key ID: 3235314B4D31232F

View File

@ -9,8 +9,9 @@ import {
IsUrl, IsUrl,
ValidateNested, ValidateNested,
} from 'class-validator' } from 'class-validator'
import { RouteHandlerMethod } from 'fastify' import type { RouteHandlerMethod } from 'fastify'
import { flatten, unflatten } from 'flat' import { flatten, unflatten } from 'flat'
import type { IncomingHttpHeaders } from 'http2'
import ms from 'ms' import ms from 'ms'
import sharp, { FitEnum, FormatEnum } from 'sharp' import sharp, { FitEnum, FormatEnum } from 'sharp'
@ -116,7 +117,7 @@ export class TransformQueryBase {
@ValidateNested() @ValidateNested()
op: ComplexParameter[] = [] op: ComplexParameter[] = []
constructor(data: any, options: { ua?: string }) { constructor(data: any, options: { headers: IncomingHttpHeaders }) {
Object.assign(this, data) Object.assign(this, data)
if (this.width) this.width = parseInt(this.width as any) if (this.width) this.width = parseInt(this.width as any)
@ -128,8 +129,9 @@ export class TransformQueryBase {
// @ts-ignore // @ts-ignore
this.format = new ComplexParameter((this.format as any) || 'auto') this.format = new ComplexParameter((this.format as any) || 'auto')
if ((this.format.name as string) === 'auto') { if ((this.format.name as string) === 'auto') {
if (!options.ua) throw new Error('cannot use auto format without user agent') if (!options.headers) throw new Error('cannot use auto format without user agent')
this.autoFormat(options.ua)
this.autoFormat(options.headers)
} }
validateSyncOrFail(this) validateSyncOrFail(this)
@ -157,10 +159,29 @@ export class TransformQueryBase {
return new URLSearchParams(sortObjectByKeys(data)).toString() return new URLSearchParams(sortObjectByKeys(data)).toString()
} }
autoFormat(ua: string) { autoFormat(headers: IncomingHttpHeaders) {
if (supportsAvif(ua)) this.format!.name = 'avif' const ua = headers['user-agent']
else if (supportsWebP(ua)) this.format!.name = 'webp' const accept = headers['accept'] // Accept: image/avif,image/webp,*/*
else this.format!.name = 'jpeg' if (accept) {
const acceptTypes = accept.split(',')
for (const type of acceptTypes) {
if (type.startsWith('image/')) {
this.format!.name = type.split('/')[1] as any
return
}
}
}
if (ua) {
if (supportsAvif(ua)) {
this.format!.name = 'avif'
return
}
if (supportsWebP(ua)) {
this.format!.name = 'webp'
return
}
}
this.format!.name = 'jpeg'
} }
get hash(): string { get hash(): string {
@ -170,7 +191,7 @@ export class TransformQueryBase {
export const image: RouteHandlerMethod = async (request, reply) => { export const image: RouteHandlerMethod = async (request, reply) => {
try { try {
const q = new TransformQueryBase(request.query, { ua: request.headers['user-agent'] }) const q = new TransformQueryBase(request.query, { headers: request.headers })
if (Config.allowedDomains) { if (Config.allowedDomains) {
if (!testForPrefixOrRegexp(q.url, Config.allowedDomains)) if (!testForPrefixOrRegexp(q.url, Config.allowedDomains))
@ -183,14 +204,17 @@ export const image: RouteHandlerMethod = async (request, reply) => {
return ForbiddenError(reply, 'origin not allowed') return ForbiddenError(reply, 'origin not allowed')
} }
const hash = q.hash
// @ts-ignore // @ts-ignore
reply.etag(q.hash) reply.etag(hash)
// @ts-ignore // @ts-ignore
reply.expires(new Date(Date.now() + ms(Config.maxAge))) reply.expires(new Date(Date.now() + ms(Config.maxAge)))
let stream: NodeJS.ReadableStream let stream: NodeJS.ReadableStream
App.log.debug('Serving image. Hash: ' + hash)
try { try {
stream = await storage.readStream(q.hash) stream = await storage.readStream(hash)
} catch { } catch {
App.log.debug(`Transforming`) App.log.debug(`Transforming`)
stream = await transform(q) stream = await transform(q)