forked from Mirrors/elk
Compare commits
35 commits
main
...
feat-incre
Author | SHA1 | Date | |
---|---|---|---|
|
5b37047b00 | ||
|
7b973811c0 | ||
|
83a4ee75d0 | ||
|
705ce6ada4 | ||
|
e18443ab6f | ||
|
200bc3d3f1 | ||
|
27c0c4d6f2 | ||
|
49a9291339 | ||
|
eab2342d91 | ||
|
a650b1f369 | ||
|
174b2b2d83 | ||
|
bb013ddd18 | ||
|
797b99f122 | ||
|
e88d255c07 | ||
|
5e717cac03 | ||
|
49290b7386 | ||
|
5da564dda4 | ||
|
cfacff87be | ||
|
dff937aa89 | ||
|
ed091d284f | ||
|
2f72c03fd9 | ||
|
26bc4e7251 | ||
|
a34e7e23f0 | ||
|
0f1937340d | ||
|
b90479d169 | ||
|
bd0a2517c6 | ||
|
a4eae9891b | ||
|
2c3e0253f6 | ||
|
2a819c6d0c | ||
|
19c8393c7c | ||
|
f73219f9bd | ||
|
32e68b6c65 | ||
|
5607c2fe37 | ||
|
4e81d0e27d | ||
|
cab3ed4ad4 |
5 changed files with 72 additions and 3 deletions
|
@ -10,3 +10,5 @@ NUXT_STORAGE_DRIVER=
|
|||
NUXT_STORAGE_FS_BASE=
|
||||
|
||||
NUXT_PUBLIC_DISABLE_VERSION_CHECK=
|
||||
|
||||
NUXT_OPENGRAPH_API=
|
||||
|
|
|
@ -17,7 +17,7 @@ export default defineComponent({
|
|||
required: false,
|
||||
},
|
||||
},
|
||||
setup(props, { attrs }) {
|
||||
setup(props, { attrs, emit }) {
|
||||
const placeholderSrc = ref<string>()
|
||||
const isLoaded = ref(false)
|
||||
|
||||
|
@ -26,6 +26,9 @@ export default defineComponent({
|
|||
img.onload = () => {
|
||||
isLoaded.value = true
|
||||
}
|
||||
img.onerror = (ev) => {
|
||||
emit('onerror', ev)
|
||||
}
|
||||
img.src = props.src
|
||||
if (props.srcset)
|
||||
img.srcset = props.srcset
|
||||
|
|
|
@ -11,6 +11,11 @@ const props = defineProps<{
|
|||
const alt = $computed(() => `${props.card.title} - ${props.card.title}`)
|
||||
const isSquare = $computed(() => props.smallPictureOnly || props.card.width === props.card.height)
|
||||
const providerName = $computed(() => props.card.providerName ? props.card.providerName : new URL(props.card.url).hostname)
|
||||
const useFallback = ref(false)
|
||||
const imageSrcset = $computed(() => props.card.image
|
||||
? `${props.card.image}${useFallback.value ? '' : `, /api/og-image/${encodeURIComponent(props.card.url)} 2x`}`
|
||||
: '',
|
||||
)
|
||||
|
||||
// TODO: handle card.type: 'photo' | 'video' | 'rich';
|
||||
</script>
|
||||
|
@ -42,10 +47,13 @@ const providerName = $computed(() => props.card.providerName ? props.card.provid
|
|||
<CommonBlurhash
|
||||
:blurhash="card.blurhash"
|
||||
:src="card.image"
|
||||
:srcset="imageSrcset"
|
||||
:width="card.width"
|
||||
:height="card.height"
|
||||
:alt="alt"
|
||||
w-full h-full object-cover
|
||||
w-full
|
||||
h-full object-cover
|
||||
@onerror="useFallback = true"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { fileURLToPath } from 'node:url'
|
||||
import Inspect from 'vite-plugin-inspect'
|
||||
import { isCI } from 'std-env'
|
||||
import { isCI, isDevelopment } from 'std-env'
|
||||
import { i18n } from './config/i18n'
|
||||
|
||||
const isPreview = process.env.PULL_REQUEST === 'true'
|
||||
|
@ -65,6 +65,7 @@ export default defineNuxtConfig({
|
|||
namespaceId: '',
|
||||
apiToken: '',
|
||||
},
|
||||
opengraphApi: '',
|
||||
public: {
|
||||
env: isCI ? isPreview ? 'staging' : 'production' : 'local',
|
||||
translateApi: '',
|
||||
|
@ -86,6 +87,14 @@ export default defineNuxtConfig({
|
|||
crawlLinks: false,
|
||||
routes: ['/', '/200.html'],
|
||||
},
|
||||
routeRules: {
|
||||
'/api/og-image/**': {
|
||||
static: isCI,
|
||||
cache: !isCI && !isDevelopment
|
||||
? { maxAge: 86400 } // 1 day
|
||||
: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
app: {
|
||||
keepalive: true,
|
||||
|
|
47
server/api/og-image/[url].ts
Normal file
47
server/api/og-image/[url].ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
// This API-Endpoint will be cached via netlify builder function -> nitro.routeRules['/api/og-image/**']
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const { url } = getRouterParams(event)
|
||||
|
||||
const cardUrl = decodeURIComponent(url || '')
|
||||
|
||||
if (!cardUrl) {
|
||||
sendError(event, {
|
||||
statusCode: 422,
|
||||
fatal: false,
|
||||
message: 'Missing cardUrl.',
|
||||
name: 'OgImageError',
|
||||
unhandled: false,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// First we want to try to get the og:image from the html
|
||||
// But sometimes it is not included due to async JS loading
|
||||
const ogImageUrl = await resolveOgImageUrlManually(cardUrl)
|
||||
|
||||
if (!ogImageUrl) {
|
||||
// If nothing helped, send 404 so the srcset can fallback to the default image
|
||||
sendError(event, {
|
||||
statusCode: 404,
|
||||
fatal: false,
|
||||
message: 'Could not find og:image.',
|
||||
name: 'OgImageError',
|
||||
unhandled: false,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
return $fetch(ogImageUrl, {
|
||||
responseType: 'stream',
|
||||
})
|
||||
})
|
||||
|
||||
const OG_IMAGE_RE = /<meta[^>]*property="og:image"[^>]*content="([^"]+)"|<meta[^>]*content="([^"]+)"[^>]*property="og:image"/
|
||||
|
||||
async function resolveOgImageUrlManually(cardUrl: string): Promise<string> {
|
||||
const html = await $fetch<string>(cardUrl)
|
||||
|
||||
const match = html.match(OG_IMAGE_RE)
|
||||
return match?.[1] ?? match?.[2] ?? ''
|
||||
}
|
Loading…
Reference in a new issue