[READ-ONLY] Mirror of https://github.com/danielroe/roe.dev. This is the code and content for my personal website, built in Nuxt. roe.dev
0

Configure Feed

Select the types of activity you want to include in your feed.

refactor: get image domains from sanity

+145 -5
+145
modules/sanity-image-domains.ts
··· 1 + import { defineNuxtModule, useRuntimeConfig } from 'nuxt/kit' 2 + import type { Nuxt } from '@nuxt/schema' 3 + import { createClient } from '@sanity/client' 4 + 5 + interface ModuleOptions { 6 + enabled: boolean 7 + } 8 + 9 + export default defineNuxtModule<ModuleOptions>({ 10 + meta: { 11 + name: 'sanity-image-domains', 12 + configKey: 'sanityImageDomains', 13 + }, 14 + defaults: { 15 + enabled: true, 16 + }, 17 + async setup (options: ModuleOptions, nuxt: Nuxt) { 18 + if (!options.enabled || nuxt.options.dev || nuxt.options._prepare) { 19 + return 20 + } 21 + 22 + const runtimeConfig = useRuntimeConfig() 23 + 24 + if (!runtimeConfig.sanity?.token) { 25 + console.log('⚠️ [sanity-image-domains] Sanity token not found - skipping dynamic image domains') 26 + return 27 + } 28 + 29 + try { 30 + const client = createClient({ 31 + projectId: '9bj3w2vo', 32 + dataset: 'production', 33 + token: runtimeConfig.sanity.token, 34 + apiVersion: '2025-02-19', 35 + useCdn: false, 36 + }) 37 + 38 + const urlQuery = `*[ 39 + _type in ["talk", "entity", "uses", "ama", "invite", "talkGroup"] 40 + ] { 41 + _type, 42 + "talkImages": select(_type == "talk" => image.asset->url), 43 + "entityLogos": select(_type == "entity" => logo.asset->url), 44 + "usesItemImages": select(_type == "uses" => items[].image.asset->url), 45 + "amaImages": select(_type == "ama" => image.asset->url), 46 + "inviteImages": select(_type == "invite" => image.asset->url), 47 + "talkGroupImages": select(_type == "talkGroup" => image.asset->url), 48 + "eventLinks": select(_type == "talk" && defined(link) => link), 49 + "videoUrls": select(_type == "talk" && defined(video) => video), 50 + "demoUrls": select(_type == "talk" && defined(demo) => demo), 51 + "repoUrls": select(_type == "talk" && defined(repo) => repo), 52 + "websiteUrls": select(_type == "entity" && defined(website) => website), 53 + "externalLinks": select(_type == "uses" => items[].links[].url) 54 + }` 55 + 56 + type URLQueryResponse = { 57 + _type: string 58 + talkImages?: string[] 59 + entityLogos?: string[] 60 + usesItemImages?: string[] 61 + amaImages?: string[] 62 + inviteImages?: string[] 63 + talkGroupImages?: string[] 64 + eventLinks?: string[] 65 + videoUrls?: string[] 66 + demoUrls?: string[] 67 + repoUrls?: string[] 68 + websiteUrls?: string[] 69 + externalLinks?: string[] 70 + } 71 + 72 + const results = await client.fetch<URLQueryResponse[]>(urlQuery) 73 + 74 + const domains = new Set<string>() 75 + 76 + for (const item of results) { 77 + const allUrls = [ 78 + item.talkImages, 79 + item.entityLogos, 80 + item.usesItemImages, 81 + item.amaImages, 82 + item.inviteImages, 83 + item.talkGroupImages, 84 + item.eventLinks, 85 + item.videoUrls, 86 + item.demoUrls, 87 + item.repoUrls, 88 + item.websiteUrls, 89 + ...(item.externalLinks || []), 90 + ].flat() 91 + 92 + for (const url of allUrls) { 93 + if (url) { 94 + try { 95 + const domain = new URL(url).hostname 96 + domains.add(domain) 97 + 98 + if (domain.includes('youtube')) { 99 + domains.add('i.ytimg.com') 100 + domains.add('img.youtube.com') 101 + } 102 + if (domain.includes('vimeo')) { 103 + domains.add('i.vimeocdn.com') 104 + } 105 + if (domain.includes('twitch')) { 106 + domains.add('static-cdn.jtvnw.net') 107 + } 108 + if (domain.includes('github')) { 109 + domains.add('avatars.githubusercontent.com') 110 + domains.add('raw.githubusercontent.com') 111 + } 112 + } 113 + catch { 114 + // Ignore invalid URLs 115 + } 116 + } 117 + } 118 + } 119 + 120 + const dynamicDomains = Array.from(domains).sort() 121 + 122 + if (dynamicDomains.length > 0) { 123 + console.log('✅ [sanity-image-domains] Found', dynamicDomains.length, 'dynamic image domains from Sanity') 124 + 125 + // Get current domains from config 126 + const currentDomains = nuxt.options.image?.domains || [] 127 + 128 + // Add dynamic domains to the existing ones, avoiding duplicates 129 + const allDomains = Array.from(new Set([...currentDomains, ...dynamicDomains])) 130 + 131 + // Update the image configuration 132 + nuxt.options.image = nuxt.options.image || {} 133 + nuxt.options.image.domains = allDomains 134 + 135 + console.log('✅ [sanity-image-domains] Total domains now:', allDomains.length) 136 + } 137 + else { 138 + console.log('ℹ️ [sanity-image-domains] No dynamic image domains found in Sanity content') 139 + } 140 + } 141 + catch (error) { 142 + console.error('❌ [sanity-image-domains] Failed to fetch dynamic image domains from Sanity:', error) 143 + } 144 + }, 145 + })
-5
nuxt.config.ts
··· 289 289 'www.dundeescript.co.uk', 290 290 'devoxx.gr', 291 291 'conference.vueschool.io', 292 - 'static-cdn.jtvnw.net', 293 - 'avatars.githubusercontent.com', 294 - 'raw.githubusercontent.com', 295 292 's3.nl-ams.scw.cloud', 296 293 'cdn.bsky.social', 297 294 'cdn.bsky.app', 298 - 'cdn.sanity.io', 299 295 'images.jsworldconference.com', 300 296 'www.middlesbroughfe.co.uk', 301 297 'res.cloudinary.com', 302 298 'cityjsconf.org', 303 299 'vuejsnation.com', 304 - 'vueconf.us', 305 300 'media.graphassets.com', 306 301 'secure.meetupstatic.com', 307 302 'cdn.evbuc.com',