Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: enable cache again #250

Merged
merged 2 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 106 additions & 104 deletions image/src/routes/ipfs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { cache } from 'hono/cache'
import { CACHE_DAY, Env } from '../utils/constants'
import { fetchIPFS } from '../utils/ipfs'
import { getImageByPath, ipfsToCFI } from '../utils/cloudflare-images'
Expand All @@ -9,130 +10,131 @@ import type { ResponseType } from '../utils/types'
const app = new Hono<{ Bindings: Env }>()

app.use('/*', cors({ origin: allowedOrigin }))
app.get('/*', async (c) => {
const { original } = c.req.query()
const isOriginal = original === 'true'
const isHead = c.req.method === 'HEAD'

const url = new URL(c.req.url)
const path = url.pathname.replace('/ipfs/', '')
const fullPath = `${path}${url.search}`

// TODO: check response from cache
// related issue: TypeError: Can't modify immutable headers.
// ----------------------------------------
let response = undefined
console.log('response', response)

// contruct r2 object
const objectName = `ipfs/${path}`
const object = await c.env.MY_BUCKET.get(objectName)
// TODO: check which one is faster to get mimeType from r2 or kv (probably kv, because only store mimeType string on kv)
const mimeType = object?.httpMetadata?.contentType
console.log('object', object)
console.log('mime type', mimeType)

// 1. check existing image on cf-images && !isOriginal
// ----------------------------------------
console.log('step 1')
if (mimeType?.includes('image') && !isOriginal && !isHead) {
const publicUrl = await getImageByPath({
token: c.env.IMAGE_API_TOKEN,
imageAccount: c.env.CF_IMAGE_ACCOUNT,
path: fullPath,
})
app.get(
'/*',
cache({
cacheName: 'ipfs-path',
cacheControl: 'max-age=86400, stale-while-revalidate=3600',
}),
async (c) => {
const { original } = c.req.query()
const isOriginal = original === 'true'
const isHead = c.req.method === 'HEAD'

const url = new URL(c.req.url)
const path = url.pathname.replace('/ipfs/', '')
const fullPath = `${path}${url.search}`

// contruct r2 object
const objectName = `ipfs/${path}`
const object = await c.env.MY_BUCKET.get(objectName)
// TODO: check which one is faster to get mimeType from r2 or kv (probably kv, because only store mimeType string on kv)
const mimeType = object?.httpMetadata?.contentType
console.log('object', object)
console.log('mime type', mimeType)

// 1. check existing image on cf-images && !isOriginal
// ----------------------------------------
console.log('step 1')
if (mimeType?.includes('image') && !isOriginal && !isHead) {
const publicUrl = await getImageByPath({
token: c.env.IMAGE_API_TOKEN,
imageAccount: c.env.CF_IMAGE_ACCOUNT,
path: fullPath,
})

if (publicUrl) {
return c.redirect(publicUrl)
if (publicUrl) {
return c.redirect(publicUrl)
}
}
}

// 2. check object from r2
// ----------------------------------------
console.log('step 2')
const renderR2Object = (r2Object: R2ObjectBody, mime?: string) => {
// add trailing slash for html
if (mime?.includes('html')) {
// add trailing slash
if (!url.pathname.endsWith('/')) {
return c.redirect(`${url.pathname}/${url.search}`)
// 2. check object from r2
// ----------------------------------------
console.log('step 2')
const renderR2Object = (r2Object: R2ObjectBody, mime?: string) => {
// add trailing slash for html
if (mime?.includes('html')) {
// add trailing slash
if (!url.pathname.endsWith('/')) {
return c.redirect(`${url.pathname}/${url.search}`)
}
}
}

const headers = new Headers()
r2Object.writeHttpMetadata(headers)
headers.set('etag', r2Object.httpEtag)
const headers = new Headers()
r2Object.writeHttpMetadata(headers)
headers.set('etag', r2Object.httpEtag)

const statusCode = c.req.raw.headers.get('range') !== null ? 206 : 200
const statusCode = c.req.raw.headers.get('range') !== null ? 206 : 200

response = new Response(r2Object.body, {
headers,
status: r2Object.body ? statusCode : 304,
})
const response = new Response(r2Object.body, {
headers,
status: r2Object.body ? statusCode : 304,
})

response.headers.append('cache-control', `s-maxage=${CACHE_DAY}`)
response.headers.append(
'content-range',
`bytes 0-${r2Object.size - 1}/${r2Object.size}`
)
response.headers.append('cache-control', `s-maxage=${CACHE_DAY}`)
response.headers.append(
'content-range',
`bytes 0-${r2Object.size - 1}/${r2Object.size}`
)

return response
}
return response
}

if (object !== null) {
return renderR2Object(object, mimeType)
}
if (object !== null) {
return renderR2Object(object, mimeType)
}

// 3. upload object to r2
// ----------------------------------------
console.log('step 3')
if (object === null) {
const status = await fetchIPFS({
path: fullPath,
gateway1: c.env.DEDICATED_GATEWAY,
gateway2: c.env.DEDICATED_BACKUP_GATEWAY,
})
// 3. upload object to r2
// ----------------------------------------
console.log('step 3')
if (object === null) {
const status = await fetchIPFS({
path: fullPath,
gateway1: c.env.DEDICATED_GATEWAY,
gateway2: c.env.DEDICATED_BACKUP_GATEWAY,
})

const contentLength = status.response?.headers.get('content-length')
const contentLength = status.response?.headers.get('content-length')

if (status.ok && status.response?.body && status.response?.headers) {
let body
if (status.ok && status.response?.body && status.response?.headers) {
let body

if (contentLength === null) {
body = await status.response?.text()
} else {
body = status.response.body as ResponseType
}
if (contentLength === null) {
body = await status.response?.text()
} else {
body = status.response.body as ResponseType
}

await c.env.MY_BUCKET.put(objectName, body, {
httpMetadata: status.response.headers,
})
await c.env.MY_BUCKET.put(objectName, body, {
httpMetadata: status.response.headers,
})
}
}
}

// 4. upload images to cf-images and return it if !isOriginal
// ----------------------------------------
console.log('step 4')
const imageUrl = await ipfsToCFI({
path,
token: c.env.IMAGE_API_TOKEN,
gateway: c.env.DEDICATED_GATEWAY,
imageAccount: c.env.CF_IMAGE_ACCOUNT,
})

if (imageUrl && !isOriginal && !isHead) {
return c.redirect(imageUrl)
}
// 4. upload images to cf-images and return it if !isOriginal
// ----------------------------------------
console.log('step 4')
const imageUrl = await ipfsToCFI({
path,
token: c.env.IMAGE_API_TOKEN,
gateway: c.env.DEDICATED_GATEWAY,
imageAccount: c.env.CF_IMAGE_ACCOUNT,
})

// 5. return object from r2
// ----------------------------------------
console.log('step 5')
const newObject = await c.env.MY_BUCKET.get(objectName)
if (imageUrl && !isOriginal && !isHead) {
return c.redirect(imageUrl)
}

// 5. return object from r2
// ----------------------------------------
console.log('step 5')
const newObject = await c.env.MY_BUCKET.get(objectName)

if (newObject !== null) {
return renderR2Object(newObject, newObject?.httpMetadata?.contentType)
if (newObject !== null) {
return renderR2Object(newObject, newObject?.httpMetadata?.contentType)
}
}
})
)

app.delete('/*', async (c) => {
const url = new URL(c.req.url)
Expand Down
4 changes: 2 additions & 2 deletions image/src/tests/ipfs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ test('ipfs - 302 - html', async () => {
expect(data).toMatchInlineSnapshot(`
Blob {
Symbol(kHandle): Blob {},
Symbol(kLength): 657,
Symbol(kLength): 656,
Symbol(kType): "text/html",
}
`)
Expand All @@ -136,7 +136,7 @@ test('ipfs - 200 - html', async () => {
expect(data).toMatchInlineSnapshot(`
Blob {
Symbol(kHandle): Blob {},
Symbol(kLength): 657,
Symbol(kLength): 656,
Symbol(kType): "text/html",
}
`)
Expand Down
Loading