feat: add og image storage caching example.

This commit is contained in:
thorwebdev committed 2022-11-23 16:41:23 +08:00
1 parent e281ce19bd
commit 32f39cf1d9
7 files changed
+250 -6

No files matched your search

+1 -1
View File
@@ -21,7 +21,7 @@ By creating a supabase client with the auth context from the function, you can d
```js lines=14,17-19,22-23 title=supabase/functions/select-from-table-with-auth-rls/index.ts
import { serve } from 'https://deno.land/std@0.131.0/http/server.ts'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.0.0-rc.12'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
serve(async (req: Request) => {
try {
@@ -0,0 +1,21 @@
# Open Graph (OG) Image Generation with Supabase Storage CDN Caching
Generate Open Graph images with Deno and Supabase Edge Functions and cache the generated image with Supabase Storage CDN.
- Docs: https://deno.land/x/og_edge@0.0.2
- Examples: https://vercel.com/docs/concepts/functions/edge-functions/og-image-examples
- Demo: https://obuldanrptloktxcffvn.functions.supabase.co/launchweek-ticket-og?ticketNumber=1234&username=thorwebdev&name=Thor%20%E9%9B%B7%E7%A5%9E%20Schaeff
## Run locally
```bash
supabase functions serve og-image-with-storage-cdn --no-verify-jwt
```
Navigate to http://localhost:54321/functions/v1/og-image-with-storage-cdn?ticketNumber=3524&username=thorwebdev&name=Thor%20%E9%9B%B7%E7%A5%9E%20Schaeff
## Deploy
```bash
supabase functions deploy og-image-with-storage-cdn --no-verify-jwt
```
@@ -0,0 +1,205 @@
import React from 'https://esm.sh/react@18.2.0?deno-std=0.140.0'
import { ImageResponse } from 'https://deno.land/x/og_edge@0.0.4/mod.ts'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
import { corsHeaders } from '../_shared/cors.ts'
const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw6'
const BACKGROUND_IMAGE_STD = `${STORAGE_URL}/lw6_ticket_regular.jpg`
const BACKGROUND_IMAGE_GOLDEN = `${STORAGE_URL}/lw6_ticket_gold.jpg`
const SUPA_CHECKMARK = `${STORAGE_URL}/supaverified.png`
// Load custom font
const FONT_URL = `${STORAGE_URL}/CircularStd-Bold.otf`
const font = fetch(new URL(FONT_URL, import.meta.url)).then((res) => res.arrayBuffer())
export async function handler(req: Request) {
const url = new URL(req.url)
const ticketNumber = url.searchParams.get('ticketNumber')
const username = url.searchParams.get('username') ?? url.searchParams.get('amp;username')
const name = url.searchParams.get('name') ?? url.searchParams.get('amp;name')
const golden = url.searchParams.get('golden') ?? url.searchParams.get('amp;golden')
if (!username || !ticketNumber || !name)
return new Response(JSON.stringify({ error: 'missing params' }), {
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
status: 400,
})
try {
// Try to get image from Supabase Storage CDN.
const storageResponse = await fetch(`${STORAGE_URL}/tickets/${username}.png?version=dev`) // TODO change for prod.
if (storageResponse.ok) return storageResponse
// Else, generate image ad upload to storage.
const fontData = await font
const numDigits = `${Number(ticketNumber)}`.length
const prefix = `00000000`.slice(numDigits)
const generatedImage = new ImageResponse(
(
<div
style={{
width: '2000px',
height: '1000px',
backgroundColor: '#000',
color: '#fff',
fontFamily: '"Circular"',
overflow: 'hidden',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{/* Background image */}
<img
width="2000"
height="1000"
style={{
position: 'absolute',
top: '0',
left: '0',
zIndex: '-9000',
}}
src={golden ? BACKGROUND_IMAGE_GOLDEN : BACKGROUND_IMAGE_STD}
/>
{/* GitHub Avatar image */}
<img
width="270"
height="270"
style={{
position: 'absolute',
top: '360',
left: '460',
borderRadius: 135,
}}
src={`https://github.com/${username}.png`}
/>
{/* Name & username */}
<div
style={{
display: 'flex',
justifyContent: 'center',
flexDirection: 'column',
position: 'absolute',
top: '360',
left: '815',
width: '600',
height: '270',
overflow: 'hidden',
textOverflow: 'clip',
}}
>
<p
style={{
fontSize: '70',
lineHeight: 0.9,
}}
>
{name}
</p>
{/* Username and supaverified checkmark */}
<div
style={{
display: 'flex',
fontSize: '35',
}}
>
<span>{`@${username}`}</span>
<span
style={{
marginTop: '5',
marginLeft: '10',
}}
>
<img width="44" height="44" src={SUPA_CHECKMARK} />
</span>
</div>
</div>
{/* Date */}
<p
style={{
position: 'absolute',
top: '750',
left: '815',
fontSize: '25',
}}
>
December 12th, 2022
</p>
{/* URL */}
<p
style={{
position: 'absolute',
top: '750',
left: '1120',
fontSize: '25',
}}
>
supabase.com/launch-week
</p>
{/* Ticket No */}
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
position: 'absolute',
bottom: '385',
right: '60',
width: '705',
height: '215',
transform: 'rotate(90deg)',
}}
>
<p
style={{
fontSize: '80',
}}
>
{`№ ${prefix}${ticketNumber}`}
</p>
</div>
</div>
),
{
width: 2000,
height: 1000,
fonts: [
{
name: 'Circular',
data: fontData,
style: 'normal',
},
],
headers: {
'content-type': 'image/png',
'cache-control': 'public, max-age=31536000, s-maxage=31536000, no-transform, immutable',
'cdn-cache-control': 'max-age=31536000',
},
}
)
const supabaseAdminClient = createClient(
// Supabase API URL - env var exported by default when deployed.
Deno.env.get('SUPABASE_URL') ?? '',
// Supabase API SERVICE ROLE KEY - env var exported by default when deployed.
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
)
// Upload image to storage.
const { error } = await supabaseAdminClient.storage
.from('images')
.upload(`lw6/tickets/${username}.png`, generatedImage.body!, {
// cacheControl: '31536000', // TODO add for prod
upsert: false,
})
if (error) throw error
return generatedImage
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
status: 400,
})
}
}
@@ -0,0 +1,11 @@
// Follow this setup guide to integrate the Deno language server with your editor:
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
import { serve } from 'https://deno.land/std@0.140.0/http/server.ts'
import { handler } from './handler.tsx'
console.log(`Function "og-image-with-storage-cdn" up and running!`)
serve(handler)
@@ -5,6 +5,14 @@ Generate Open Graph images with Deno and Supabase Edge Functions, no framework n
- Docs: https://deno.land/x/og_edge@0.0.2
- Demo: https://zdtdtxajzydjqzuktnqx.functions.supabase.co/opengraph
## Run locally
```bash
supabase functions serve opengraph --no-verify-jwt
```
Navigate to http://localhost:54321/functions/v1/opengraph
## Deploy
```bash
@@ -1,7 +1,7 @@
import React from 'https://esm.sh/react@18.2.0?deno-std=0.140.0'
import { ImageResponse } from 'https://deno.land/x/og_edge@0.0.2/mod.ts'
import { ImageResponse } from 'https://deno.land/x/og_edge@0.0.4/mod.ts'
export async function handler(req: Request) {
export function handler(req: Request) {
return new ImageResponse(
(
<div
@@ -6,7 +6,6 @@ import { serve } from 'https://deno.land/std@0.140.0/http/server.ts'
import { handler } from './handler.tsx'
// @ts-ignore
serve(handler)
console.log(`Function "opengraph" up and running!`)
// To deploy: supabase functions deploy opengraph --no-verify-jwt
serve(handler)