mirror of https://gitlab.com/curben/blog
feat: cloudflare images
This commit is contained in:
parent
889875e673
commit
5565b5d5d8
|
@ -6,3 +6,5 @@ node_modules/
|
|||
package-lock.json
|
||||
public/
|
||||
.deploy*/
|
||||
bun.lockb
|
||||
.wrangler/
|
||||
|
|
|
@ -18,6 +18,7 @@ build:
|
|||
script:
|
||||
# Generate site
|
||||
- npm run build
|
||||
- npm run deploy-cf-images
|
||||
|
||||
rules:
|
||||
# Only trigger through push & "Run pipeline" events not in "site" branch; Skip in renovate job
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
import { join, relative } from 'node:path'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* Fetch and log a request
|
||||
* @param {Request} request
|
||||
*/
|
||||
async fetch (request) {
|
||||
const { pathname } = new URL(request.url)
|
||||
|
||||
if (pathname === '/images/favicon.ico') {
|
||||
return new Response('', {
|
||||
status: 404,
|
||||
headers: {
|
||||
'Cache-Control': 'no-cache'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// https://developers.cloudflare.com/images/url-format#supported-formats-and-limitations
|
||||
if (!/\.(jpe?g|png|gif|webp)$/i.test(pathname)) {
|
||||
return new Response('Invalid file extension', {
|
||||
status: 400,
|
||||
headers: {
|
||||
'Cache-Control': 'no-cache'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Cloudflare-specific options are in the cf object.
|
||||
const options = { cf: { image: {} } }
|
||||
|
||||
const numParts = pathname.split('/').length
|
||||
let imgPath = ''
|
||||
// original size
|
||||
if (numParts === 4) {
|
||||
imgPath = relative('/images', pathname)
|
||||
} else if (numParts === 5) {
|
||||
// Copy width size from path to request options
|
||||
const width = relative('/images', pathname).split('/')[0]
|
||||
const validSizes = new Set(['320', '468', '768'])
|
||||
|
||||
if (validSizes.has(width)) {
|
||||
imgPath = relative(join('/images', width), pathname)
|
||||
options.cf.image.width = width
|
||||
// serve original size if width is larger
|
||||
options.cf.image.fit = 'scale-down'
|
||||
} else {
|
||||
return new Response('Invalid width', {
|
||||
status: 400,
|
||||
headers: {
|
||||
'Cache-Control': 'no-cache'
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
return new Response('Invalid path', {
|
||||
status: 404,
|
||||
headers: {
|
||||
'Cache-Control': 'no-cache'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Your Worker is responsible for automatic format negotiation. Check the Accept header.
|
||||
const accept = request.headers.get('Accept')
|
||||
if (/image\/avif/.test(accept)) {
|
||||
options.cf.image.format = 'avif'
|
||||
} else if (/image\/webp/.test(accept)) {
|
||||
options.cf.image.format = 'webp'
|
||||
}
|
||||
|
||||
// Build a request that passes through request headers
|
||||
// Images are stored on https://gitlab.com/curben/blog/-/tree/site
|
||||
const imageURL = new URL(imgPath, 'https://curben.gitlab.io/blog/')
|
||||
const imageRequest = new Request(imageURL, {
|
||||
headers: request.headers
|
||||
})
|
||||
|
||||
let response = await fetch(imageRequest, options)
|
||||
// Reconstruct the Response object to make its headers mutable.
|
||||
response = new Response(response.body, response)
|
||||
|
||||
if (response.ok || response.redirected) {
|
||||
// Set cache for 1 week
|
||||
response.headers.set('Cache-Control', 'max-age=604800, public')
|
||||
// Set Vary header
|
||||
response.headers.set('Vary', 'Accept')
|
||||
return response
|
||||
} else if (response.status === 404) {
|
||||
// Custom 404 page
|
||||
const { status, statusText } = response
|
||||
|
||||
const htmlHeader = new Headers({
|
||||
...request.headers,
|
||||
Accept: 'text/html'
|
||||
})
|
||||
const page404 = new Request('https://curben.pages.dev/404', {
|
||||
headers: htmlHeader
|
||||
})
|
||||
response = await fetch(page404)
|
||||
const html = await response.text()
|
||||
return new Response(html, {
|
||||
status,
|
||||
statusText,
|
||||
headers: {
|
||||
...response.headers,
|
||||
'Cache-Control': 'no-cache',
|
||||
'Content-Type': 'text/html; charset=utf-8'
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return new Response(`Could not fetch the image, the origin returned HTTP error ${response.status}`, {
|
||||
status: response.status,
|
||||
headers: {
|
||||
'Cache-Control': 'no-cache'
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,8 @@
|
|||
"scripts": {
|
||||
"build": "hexo generate",
|
||||
"snyk": "snyk auth $SNYK_TOKEN && snyk-protect && snyk test && snyk monitor",
|
||||
"renovate": "renovate --platform 'gitlab' --autodiscover false --onboarding false --update-lock-files false --labels 'renovate' --require-config='ignored' \"$CI_PROJECT_PATH\""
|
||||
"renovate": "renovate --platform 'gitlab' --autodiscover false --onboarding false --update-lock-files false --labels 'renovate' --require-config='ignored' \"$CI_PROJECT_PATH\"",
|
||||
"deploy-cf-images": "npx wrangler deploy"
|
||||
},
|
||||
"dependencies": {
|
||||
"hexo": "^7.0.0",
|
||||
|
|
|
@ -20,18 +20,16 @@ hexo.extend.filter.register('marked:renderer', (renderer) => {
|
|||
// embed external image
|
||||
if (href.startsWith('http')) return `<img src="${href}" alt="${alt}" title="${title}">`
|
||||
|
||||
const fLink = (path, width) => {
|
||||
const query = new URLSearchParams('f=auto')
|
||||
if (typeof width === 'number') query.set('width', width)
|
||||
const url = new URL('http://example.com/' + join('img', path) + '?' + query)
|
||||
const fLink = (path, width = '') => {
|
||||
const url = new URL(join('images', width, path), 'http://example.com/')
|
||||
|
||||
return url.pathname + url.search
|
||||
return url.pathname
|
||||
}
|
||||
|
||||
return `<a href="${join('/img', href)}">` +
|
||||
`<img srcset="${fLink(href, 320)} 320w,` +
|
||||
`${fLink(href, 468)} 468w,` +
|
||||
`${fLink(href, 768)} 768w,` +
|
||||
`<img srcset="${fLink(href, '320')} 320w,` +
|
||||
`${fLink(href, '468')} 468w,` +
|
||||
`${fLink(href, '768')} 768w,` +
|
||||
`${fLink(href)} 800w"` +
|
||||
' sizes="(max-width: 320px) 320px,' +
|
||||
'(max-width: 468px) 468px,' +
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
name = "cf-images"
|
||||
main = "cf-images/index.js"
|
||||
compatibility_date = "2024-09-23"
|
||||
compatibility_flags = [ "nodejs_compat" ]
|
||||
send_metrics = false
|
||||
|
||||
route = { pattern = "https://mdleom.com/images/*", zone_name = "mdleom.com" }
|
Loading…
Reference in New Issue