mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 23:53:05 +00:00
Derivada AVIF 720px q60 armazenada como objeto-irmão determinístico de cada foto aprovada (flickr-<id>-grade.avif), servida direto quando o cliente negocia Accept: image/avif; fallback JPEG on-the-fly em ausência/falha, heal preguiçoso via after(), warm no afterChange da aprovação e backfill pnpm archive:thumbnails idempotente com recibo e guardas *_CONFIRM. Sem migration, sem UI, sem mudança de URL pública ou pull-down. Closes #1424
149 lines
5.6 KiB
TypeScript
149 lines
5.6 KiB
TypeScript
/**
|
|
* C193/C199 — pure response rules of a private media artifact: the safe content
|
|
* type, the content disposition, the range/private headers and the classification
|
|
* of an absent object. No I/O and no `server-only`: the streaming utility and the
|
|
* unit tests share this module.
|
|
*
|
|
* The owner serves every private upload collection (`reelMedia`, `recordingMedia`)
|
|
* through the authenticated `/campanha` routes; the public `media` contract
|
|
* never touches this module.
|
|
*/
|
|
|
|
/**
|
|
* Only these may render inline; anything else is forced to download. C199 adds
|
|
* the video containers the recordings actually arrive in (MOV/MKV/WebM) — all
|
|
* inert media types, never markup executed in the campaign origin. S27 adds the
|
|
* image and audio families the content pieces accept (a published photo must
|
|
* render and an audio piece must play; both were degrading to octet-stream and
|
|
* turning into a download). HEIC/HEIF stay out on purpose (browsers do not
|
|
* render them) and `image/svg+xml` never enters (markup).
|
|
*/
|
|
const PRIVATE_MEDIA_INLINE_MIME_TYPES = [
|
|
'video/mp4',
|
|
'video/quicktime',
|
|
'video/x-matroska',
|
|
'video/webm',
|
|
'audio/mpeg',
|
|
'audio/mp4',
|
|
'audio/aac',
|
|
'audio/ogg',
|
|
'audio/opus',
|
|
'audio/wav',
|
|
'audio/webm',
|
|
'audio/flac',
|
|
'image/png',
|
|
'image/jpeg',
|
|
'image/webp',
|
|
'image/gif',
|
|
'image/avif',
|
|
] as const
|
|
|
|
export const PRIVATE_MEDIA_FALLBACK_MIME_TYPE = 'application/octet-stream'
|
|
export const PRIVATE_MEDIA_CACHE_CONTROL = 'private, no-store'
|
|
|
|
export type PrivateMediaRange = {
|
|
status: number
|
|
headers: Record<string, string>
|
|
}
|
|
|
|
/**
|
|
* True when the read error means the OBJECT is absent in whichever store served
|
|
* it: S3 `NoSuchKey`/`NotFound` (or a 404 in the SDK metadata) or the local disk
|
|
* `ENOENT`. Everything else — a checksum mismatch included — is a real read
|
|
* failure of an object that exists (C246 classifies it as corrupt, never as
|
|
* missing). Pure so the response paths, the integrity sweep and the unit spec
|
|
* share one classification.
|
|
*/
|
|
export const isPrivateMediaMissingError = (error: unknown): boolean => {
|
|
if ((error as { code?: unknown } | null)?.code === 'ENOENT') return true
|
|
if (!error || typeof error !== 'object') return false
|
|
const name = 'name' in error ? String(error.name) : ''
|
|
const errorCode = 'Code' in error ? String(error.Code) : ''
|
|
const metadata =
|
|
'$metadata' in error && error.$metadata && typeof error.$metadata === 'object'
|
|
? (error.$metadata as { httpStatusCode?: number })
|
|
: null
|
|
return (
|
|
name === 'NoSuchKey' ||
|
|
name === 'NotFound' ||
|
|
errorCode === 'NoSuchKey' ||
|
|
metadata?.httpStatusCode === 404
|
|
)
|
|
}
|
|
|
|
/**
|
|
* C248 — AVIF negotiation for the stored grade thumbnail. The token must be
|
|
* EXPLICIT (`image/avif`) with a positive quality: a missing header, a
|
|
* wildcard or `image/jpeg` stays on the JPEG fallback, so every client that
|
|
* never opted in (curl, the e2e API request context, a browser without the
|
|
* decoder) keeps the exact answer it has today. Pure, so the route and the
|
|
* unit spec share the classification.
|
|
*/
|
|
export const acceptsAvif = (acceptHeader: string | null | undefined): boolean => {
|
|
if (typeof acceptHeader !== 'string') return false
|
|
for (const part of acceptHeader.split(',')) {
|
|
const [rawType, ...params] = part.split(';')
|
|
if (rawType.trim().toLowerCase() !== 'image/avif') continue
|
|
|
|
let quality = 1
|
|
for (const param of params) {
|
|
const [rawKey, rawValue] = param.split('=')
|
|
if (rawKey?.trim().toLowerCase() !== 'q') continue
|
|
const parsed = Number(rawValue)
|
|
// A malformed weight must never arm AVIF: only a real positive q does.
|
|
quality = Number.isFinite(parsed) ? parsed : 0
|
|
}
|
|
return quality > 0
|
|
}
|
|
return false
|
|
}
|
|
|
|
const isInlineSafe = (mimeType: unknown): mimeType is string =>
|
|
typeof mimeType === 'string' &&
|
|
(PRIVATE_MEDIA_INLINE_MIME_TYPES as readonly string[]).includes(mimeType)
|
|
|
|
/**
|
|
* The served content type is the stored one only when it is safe to render
|
|
* inline; everything else degrades to `application/octet-stream` (never an
|
|
* attacker-chosen type executed in the campaign origin).
|
|
*/
|
|
export const privateMediaContentType = (mimeType: string | null | undefined): string =>
|
|
isInlineSafe(mimeType) ? mimeType : PRIVATE_MEDIA_FALLBACK_MIME_TYPE
|
|
|
|
/** Both `filename` and `filename*` (RFC 5987) so non-ASCII names survive. */
|
|
export const privateMediaContentDisposition = (
|
|
filename: string | null | undefined,
|
|
download: boolean,
|
|
): string => {
|
|
const name = filename?.trim() || 'arquivo'
|
|
const asciiFallback = name.replace(/[^\w.-]+/g, '_')
|
|
const disposition = download ? 'attachment' : 'inline'
|
|
return `${disposition}; filename="${asciiFallback}"; filename*=UTF-8''${encodeURIComponent(name)}`
|
|
}
|
|
|
|
/**
|
|
* The response headers of one artifact: the range headers computed from the
|
|
* real file size plus the private streaming contract. Pure.
|
|
*/
|
|
export const privateMediaHeaders = ({
|
|
range,
|
|
mimeType,
|
|
filename,
|
|
download,
|
|
}: {
|
|
range: PrivateMediaRange
|
|
mimeType: string | null | undefined
|
|
filename: string | null | undefined
|
|
download: boolean
|
|
}): Headers => {
|
|
const headers = new Headers(range.headers)
|
|
const contentType = privateMediaContentType(mimeType)
|
|
const forceDownload = download || contentType === PRIVATE_MEDIA_FALLBACK_MIME_TYPE
|
|
|
|
headers.set('Content-Type', contentType)
|
|
headers.set('Content-Disposition', privateMediaContentDisposition(filename, forceDownload))
|
|
headers.set('Cache-Control', PRIVATE_MEDIA_CACHE_CONTROL)
|
|
headers.set('X-Content-Type-Options', 'nosniff')
|
|
if (forceDownload) headers.set('Content-Security-Policy', "default-src 'none'")
|
|
return headers
|
|
}
|