mirror of
https://github.com/supabase/supabase.git
synced 2026-10-17 15:19:08 +03:00
feat: add lw7 ticket og.
This commit is contained in:
1 parent
0d5a5b7501
commit
50a2cddd8d
6 files changed
+283
-3
No files matched your search
@@ -18,7 +18,7 @@ export default function TicketActions({ username, golden = false }: Props) {
|
||||
const { userData } = useConfData()
|
||||
const tweetUrl = `https://twitter.com/intent/tweet?url=${permalink}&via=supabase&text=${text}`
|
||||
const linkedInUrl = `https://www.linkedin.com/sharing/share-offsite/?url=${permalink}`
|
||||
const downloadUrl = `https://obuldanrptloktxcffvn.functions.supabase.co/launchweek-ticket-og?username=${encodeURIComponent(
|
||||
const downloadUrl = `https://obuldanrptloktxcffvn.functions.supabase.co/lw7-ticket-og?username=${encodeURIComponent(
|
||||
userData.username ?? ''
|
||||
)}`
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ export default function Form({ defaultUsername = '', setTicketGenerationState }:
|
||||
fetch(`/launch-week/tickets/${username}`).catch((_) => {})
|
||||
// Prefetch ticket og image.
|
||||
fetch(
|
||||
`https://obuldanrptloktxcffvn.functions.supabase.co/launchweek-ticket-og?username=${encodeURIComponent(
|
||||
`https://obuldanrptloktxcffvn.functions.supabase.co/lw7-ticket-og?username=${encodeURIComponent(
|
||||
username ?? ''
|
||||
)}`
|
||||
).catch((_) => {})
|
||||
|
||||
@@ -36,7 +36,7 @@ export default function TicketShare({ username, ticketNumber, name, golden, refe
|
||||
return <Error statusCode={404} />
|
||||
}
|
||||
|
||||
const ogImageUrl = `https://obuldanrptloktxcffvn.functions.supabase.co/launchweek-ticket-og?username=${encodeURIComponent(
|
||||
const ogImageUrl = `https://obuldanrptloktxcffvn.functions.supabase.co/lw7-ticket-og?username=${encodeURIComponent(
|
||||
username ?? ''
|
||||
)}`
|
||||
|
||||
|
||||
@@ -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/lw7-ticket-og?username=thorwebdev
|
||||
|
||||
## Run locally
|
||||
|
||||
```bash
|
||||
supabase functions serve --no-verify-jwt
|
||||
```
|
||||
|
||||
Navigate to http://localhost:54321/functions/v1/lw7-ticket-og?username=thorwebdev
|
||||
|
||||
## Deploy
|
||||
|
||||
```bash
|
||||
supabase functions deploy lw7-ticket-og --no-verify-jwt
|
||||
```
|
||||
@@ -0,0 +1,248 @@
|
||||
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.7.1'
|
||||
import { corsHeaders } from '../_shared/cors.ts'
|
||||
|
||||
const STORAGE_URL = 'https://obuldanrptloktxcffvn.supabase.co/storage/v1/object/public/images/lw7'
|
||||
const BACKGROUND_SHAPE = `${STORAGE_URL}/lw7_ticket_shape.png`
|
||||
const BACKGROUND_IMAGE_STD = `${STORAGE_URL}/lw7_ticket_regular.png`
|
||||
const BACKGROUND_IMAGE_GOLDEN = `${STORAGE_URL}/lw7_ticket_gold.png`
|
||||
|
||||
// Load custom font
|
||||
const FONT_URL = `${STORAGE_URL}/CircularStd-Book.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 username = url.searchParams.get('username') ?? url.searchParams.get('amp;username')
|
||||
const userAgent = req.headers.get('user-agent')
|
||||
|
||||
try {
|
||||
if (!username) throw new Error('missing username param')
|
||||
// Track social shares
|
||||
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') ?? ''
|
||||
)
|
||||
if (userAgent?.toLocaleLowerCase().includes('twitter')) {
|
||||
const { error } = await supabaseAdminClient
|
||||
.from('lw7_tickets')
|
||||
.update({ sharedOnTwitter: 'now' })
|
||||
.eq('username', username)
|
||||
.is('sharedOnTwitter', null)
|
||||
if (error) console.log(error.message)
|
||||
} else if (userAgent?.toLocaleLowerCase().includes('linkedin')) {
|
||||
const { error } = await supabaseAdminClient
|
||||
.from('lw7_tickets')
|
||||
.update({ sharedOnLinkedIn: 'now' })
|
||||
.eq('username', username)
|
||||
.is('sharedOnLinkedIn', null)
|
||||
if (error) console.log(error.message)
|
||||
}
|
||||
|
||||
// TODO: reenable storage
|
||||
// Try to get image from Supabase Storage CDN.
|
||||
// const storageResponse = await fetch(`${STORAGE_URL}/tickets/${username}.png`)
|
||||
// if (storageResponse.ok) return storageResponse
|
||||
|
||||
// Get ticket data
|
||||
const { data, error } = await supabaseAdminClient
|
||||
.from('lw7_tickets_golden')
|
||||
.select('name, ticketNumber, golden')
|
||||
.eq('username', username)
|
||||
.maybeSingle()
|
||||
if (error) console.log(error.message)
|
||||
|
||||
const { name = 'Testr Test', ticketNumber = 1234 } = data ?? {}
|
||||
const golden = data?.golden ?? false
|
||||
|
||||
// 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: '1200px',
|
||||
height: '630px',
|
||||
backgroundColor: '#000',
|
||||
color: '#fff',
|
||||
fontFamily: '"Circular"',
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
{/* Background image color */}
|
||||
<img
|
||||
width="1200"
|
||||
height="630"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
left: '0',
|
||||
zIndex: '-9000',
|
||||
}}
|
||||
src={golden ? BACKGROUND_IMAGE_GOLDEN : BACKGROUND_IMAGE_STD}
|
||||
/>
|
||||
{/* Background image overlay */}
|
||||
<img
|
||||
width="1200"
|
||||
height="630"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
left: '0',
|
||||
zIndex: '-9000',
|
||||
}}
|
||||
src={BACKGROUND_SHAPE}
|
||||
/>
|
||||
{/* GitHub Avatar image */}
|
||||
<img
|
||||
width="200"
|
||||
height="200"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '215',
|
||||
left: '155',
|
||||
borderRadius: 100,
|
||||
}}
|
||||
src={`https://github.com/${username}.png`}
|
||||
/>
|
||||
{/* Name & username */}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'column',
|
||||
position: 'absolute',
|
||||
top: '215',
|
||||
left: '400',
|
||||
width: '550',
|
||||
height: '200',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'clip',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
color: 'transparent',
|
||||
backgroundImage:
|
||||
'linear-gradient(90deg, #F8F9FA 1.73%, rgba(248, 249, 250, 0.5) 100%)',
|
||||
backgroundClip: 'text',
|
||||
}}
|
||||
>
|
||||
<p
|
||||
style={{
|
||||
fontSize: '60px',
|
||||
lineHeight: '60px',
|
||||
}}
|
||||
>
|
||||
{name ?? username}
|
||||
</p>
|
||||
</div>
|
||||
{/* Username and supaverified checkmark */}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
fontSize: '25',
|
||||
color: golden ? '#fff' : '#A0A0A0',
|
||||
}}
|
||||
>
|
||||
<span>{`@${username}`}</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* Date */}
|
||||
<p
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '520',
|
||||
left: '400',
|
||||
fontSize: '22',
|
||||
color: golden ? '#fff' : '#A0A0A0',
|
||||
}}
|
||||
>
|
||||
December 12th 2022
|
||||
</p>
|
||||
{/* URL */}
|
||||
<p
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '520',
|
||||
left: '680',
|
||||
fontSize: '22',
|
||||
color: golden ? '#fff' : '#A0A0A0',
|
||||
}}
|
||||
>
|
||||
supabase.com/launch-week
|
||||
</p>
|
||||
</div>
|
||||
{/* Ticket No */}
|
||||
<div
|
||||
style={{
|
||||
color: '#fff',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
position: 'absolute',
|
||||
bottom: '225',
|
||||
right: '-165',
|
||||
width: '575',
|
||||
height: '175',
|
||||
transform: 'rotate(90deg)',
|
||||
}}
|
||||
>
|
||||
<p
|
||||
style={{
|
||||
fontSize: '70',
|
||||
}}
|
||||
>
|
||||
{`No ${prefix}${ticketNumber}`}
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
),
|
||||
{
|
||||
width: 1200,
|
||||
height: 630,
|
||||
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',
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// TODO: reenable storage
|
||||
// Upload image to storage.
|
||||
// const { error: storageError } = await supabaseAdminClient.storage
|
||||
// .from('images')
|
||||
// .upload(`lw7/tickets/${username}.png`, generatedImage.body!, {
|
||||
// contentType: 'image/png',
|
||||
// cacheControl: '31536000',
|
||||
// upsert: false,
|
||||
// })
|
||||
// if (storageError) throw storageError
|
||||
|
||||
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.177.0/http/server.ts'
|
||||
|
||||
import { handler } from './handler.tsx'
|
||||
|
||||
console.log(`Function "launchweek-ticket-og" up and running!`)
|
||||
|
||||
serve(handler)
|
||||
Reference in new issue
Block a user