diff --git a/modules/runtime/server/_social/bluesky.get.ts b/modules/runtime/server/_social/bluesky.get.ts index c15bf3e2..0abe12cc 100644 --- a/modules/runtime/server/_social/bluesky.get.ts +++ b/modules/runtime/server/_social/bluesky.get.ts @@ -1,73 +1,70 @@ import { AppBskyEmbedImages, AppBskyFeedPost, AtpAgent } from '@atproto/api' import MagicString from 'magic-string' -export default defineLazyEventHandler(async () => { - const { identifier, password } = useRuntimeConfig().social.networks.bluesky +export default defineEventHandler(async event => { const agent = new AtpAgent({ service: 'https://bsky.social' }) + const { identifier, password } = useRuntimeConfig(event).social.networks.bluesky await agent.login({ identifier, password, }) - - return defineEventHandler(async () => { - const posts = await agent.getAuthorFeed({ actor: identifier }) - return Promise.all( - posts.data.feed - .filter(p => AppBskyFeedPost.isRecord(p.post.record) && p.post.author.handle === identifier && !p.reply && (!p.post.embed || AppBskyEmbedImages.isMain(p.post.embed))) - .map(p => { - const post = p.post.record as AppBskyFeedPost.Record - const embed = AppBskyEmbedImages.isMain(p.post.embed) ? p.post.embed : undefined - const text = new MagicString(post.text) - for (const facet of post.facets || []) { - const startIndex = [...post.text].findIndex( - (_, i) => - Buffer.byteLength(post.text.slice(0, i + 1)) - > facet.index.byteStart, - ) - const endIndex = [...post.text].findIndex( - (_, i) => - Buffer.byteLength(post.text.slice(0, i + 1)) - >= facet.index.byteEnd, - ) - for (const feature of facet.features) { - if (feature.$type === 'app.bsky.richtext.facet#link') { - text.appendRight(startIndex, ``) - text.appendLeft(endIndex === -1 ? post.text.length : endIndex + 1, '') - continue - } + const posts = await agent.getAuthorFeed({ actor: identifier }) + return Promise.all( + posts.data.feed + .filter(p => AppBskyFeedPost.isRecord(p.post.record) && p.post.author.handle === identifier && !p.reply && (!p.post.embed || AppBskyEmbedImages.isMain(p.post.embed))) + .map(p => { + const post = p.post.record as AppBskyFeedPost.Record + const embed = AppBskyEmbedImages.isMain(p.post.embed) ? p.post.embed : undefined + const text = new MagicString(post.text) + for (const facet of post.facets || []) { + const startIndex = [...post.text].findIndex( + (_, i) => + Buffer.byteLength(post.text.slice(0, i + 1)) + > facet.index.byteStart, + ) + const endIndex = [...post.text].findIndex( + (_, i) => + Buffer.byteLength(post.text.slice(0, i + 1)) + >= facet.index.byteEnd, + ) + for (const feature of facet.features) { + if (feature.$type === 'app.bsky.richtext.facet#link') { + text.appendRight(startIndex, ``) + text.appendLeft(endIndex === -1 ? post.text.length : endIndex + 1, '') + continue } } + } - text.replaceAll(/[<>&]/g, r => ({ - '<': '<', - '>': '>', - '&': '&', - }[r]!)) + text.replaceAll(/[<>&]/g, r => ({ + '<': '<', + '>': '>', + '&': '&', + }[r]!)) - for (const match of post.text.matchAll(/\n\n/g)) { - text.overwrite(match.index!, match.index! + 2, '

') - } + for (const match of post.text.matchAll(/\n\n/g)) { + text.overwrite(match.index!, match.index! + 2, '

') + } - text.prepend('

') - text.append('

') + text.prepend('

') + text.append('

') - return { - network: 'bluesky' as const, - accountLink: `https://staging.bsky.app/profile/${p.post.author.handle}`, - avatar: p.post.author.avatar, - handle: p.post.author.displayName, - createdAt: post.createdAt, - permalink: + return { + network: 'bluesky' as const, + accountLink: `https://staging.bsky.app/profile/${p.post.author.handle}`, + avatar: p.post.author.avatar, + handle: p.post.author.displayName, + createdAt: post.createdAt, + permalink: `https://staging.bsky.app/profile/${p.post.author.handle}/post` + p.post.uri.match(/(\/[^/]+)$/)?.[1], - html: text.toString().replace(/\n/g, '
'), - media: embed?.images?.map(i => ({ - url: i.fullsize, - alt: i.alt, - })), - } - }), - ) - }) + html: text.toString().replace(/\n/g, '
'), + media: embed?.images?.map(i => ({ + url: i.fullsize, + alt: i.alt, + })), + } + }), + ) }) diff --git a/modules/runtime/server/_social/mastodon.get.ts b/modules/runtime/server/_social/mastodon.get.ts index ebe9a5c7..cbe9736d 100644 --- a/modules/runtime/server/_social/mastodon.get.ts +++ b/modules/runtime/server/_social/mastodon.get.ts @@ -1,8 +1,8 @@ import { createRestAPIClient } from 'masto' import { parseURL, withProtocol } from 'ufo' -export default defineLazyEventHandler(async () => { - const acct = useRuntimeConfig().social.networks.mastodon.identifier +export default defineEventHandler(async event => { + const acct = useRuntimeConfig(event).social.networks.mastodon.identifier const server = acct.split('@')[1] if (!server) throw createError('Invalid Mastodon account identifier') @@ -21,38 +21,35 @@ export default defineLazyEventHandler(async () => { url: withProtocol(host!, protocol!), }) const { id } = await client.v1.accounts.lookup({ acct }) - - return defineEventHandler(async () => { - const posts = await client.v1.accounts - .$select(id) - .statuses.list({ limit: 200 }) - return Promise.all( - posts - .filter(p => p.content && !p.inReplyToId) - .map(p => ({ - network: 'mastodon' as const, - accountLink: `https://elk.zone/${host}/@${p.account.acct}`, - avatar: p.account.avatar, - handle: p.account.displayName.replace( - /:([a-z-]+):/g, - (string, shortcode) => { - const emoji = p.account.emojis.find( - e => e.shortcode === shortcode, - ) - if (!emoji) return string - return `${shortcode} emoji` - }, - ), - createdAt: p.createdAt, - permalink: p.url?.replace('https://', 'https://elk.zone/') ?? p.uri, - media: p.mediaAttachments.map(m => ({ - url: m.url, - width: m.meta?.original?.width, - height: m.meta?.original?.height, - alt: m.description, - })), - html: p.content, + const posts = await client.v1.accounts + .$select(id) + .statuses.list({ limit: 200 }) + return Promise.all( + posts + .filter(p => p.content && !p.inReplyToId) + .map(p => ({ + network: 'mastodon' as const, + accountLink: `https://elk.zone/${host}/@${p.account.acct}`, + avatar: p.account.avatar, + handle: p.account.displayName.replace( + /:([a-z-]+):/g, + (string, shortcode) => { + const emoji = p.account.emojis.find( + e => e.shortcode === shortcode, + ) + if (!emoji) return string + return `${shortcode} emoji` + }, + ), + createdAt: p.createdAt, + permalink: p.url?.replace('https://', 'https://elk.zone/') ?? p.uri, + media: p.mediaAttachments.map(m => ({ + url: m.url, + width: m.meta?.original?.width, + height: m.meta?.original?.height, + alt: m.description, })), - ) - }) + html: p.content, + })), + ) }) diff --git a/modules/runtime/server/auth/github/[slug].ts b/modules/runtime/server/auth/github/[slug].ts index 5d6ef574..df7039a8 100644 --- a/modules/runtime/server/auth/github/[slug].ts +++ b/modules/runtime/server/auth/github/[slug].ts @@ -1,7 +1,7 @@ import type { RuntimeConfig } from 'nuxt/schema' export default defineEventHandler(async event => { - const config = useRuntimeConfig() + const config = useRuntimeConfig(event) const slug = getRouterParam(event, 'slug') const repo = slug && config.invites?.map?.[slug as keyof RuntimeConfig['invites']['map']] if (!repo) { diff --git a/server/api/feedback.post.ts b/server/api/feedback.post.ts index 6ac62f4f..e5544bc9 100644 --- a/server/api/feedback.post.ts +++ b/server/api/feedback.post.ts @@ -1,5 +1,5 @@ export default defineEventHandler(async event => { - const config = useRuntimeConfig() + const config = useRuntimeConfig(event) const { feedback } = await readBody(event) if (!feedback) { throw createError({ statusCode: 400, statusMessage: 'Missing feedback' }) diff --git a/server/api/idea.post.ts b/server/api/idea.post.ts index 1827c4a6..62f818b8 100644 --- a/server/api/idea.post.ts +++ b/server/api/idea.post.ts @@ -16,6 +16,7 @@ export default defineEventHandler(async event => { } const { idea } = await readBody(event) await sendEmail( + event, 'Idea from website', [`Idea from ${payload.name}:`, '', idea].join('\n'), ) diff --git a/server/api/question.ts b/server/api/question.ts index a8706f8d..9ab5bd6e 100644 --- a/server/api/question.ts +++ b/server/api/question.ts @@ -5,6 +5,6 @@ export default defineEventHandler(async event => { const { question } = await readBody(event) if (!question) throw createError({ statusCode: 422 }) event.waitUntil(createTypefullyDraft(event, question).catch(console.error)) - await sendEmail('Anonymous question', question) + await sendEmail(event, 'Anonymous question', question) return null }) diff --git a/server/api/sponsors.get.ts b/server/api/sponsors.get.ts index 2da3dc32..265a2ebe 100644 --- a/server/api/sponsors.get.ts +++ b/server/api/sponsors.get.ts @@ -1,10 +1,10 @@ import { getSponsors } from '../utils/sponsors' export default defineCachedEventHandler( - async () => { + async event => { if (import.meta.test) return [] - const sponsors = await getSponsors() + const sponsors = await getSponsors(event) return sponsors .map(s => s.avatarUrl?.replace(/(\?|%3Fu).*$/, '')) .filter((r): r is string => !!r) diff --git a/server/api/streams.ts b/server/api/streams.ts index ab657833..d55407d1 100644 --- a/server/api/streams.ts +++ b/server/api/streams.ts @@ -1,7 +1,7 @@ -export default defineCachedEventHandler(async () => { +export default defineCachedEventHandler(async event => { if (import.meta.test) return [] - const config = useRuntimeConfig() + const config = useRuntimeConfig(event) const token = await $fetch( 'https://id.twitch.tv/oauth2/token', { diff --git a/server/routes/auth/[...provider].get.ts b/server/routes/auth/[...provider].get.ts index 7cff3070..76fc72a4 100644 --- a/server/routes/auth/[...provider].get.ts +++ b/server/routes/auth/[...provider].get.ts @@ -8,7 +8,7 @@ export default defineEventHandler(async event => { }) } - const config = useRuntimeConfig() + const config = useRuntimeConfig(event) const { access_token } = await $fetch<{ access_token: string }>( 'https://github.com/login/oauth/access_token', { @@ -38,7 +38,7 @@ export default defineEventHandler(async event => { console.error('viewer', err) return {} }), - getSponsors() + getSponsors(event) .then(r => r.map(s => s.id)) .catch(err => { console.error('sponsor', err) diff --git a/server/routes/v/[slug].ts b/server/routes/v/[slug].ts index d204eeba..bbfbb599 100644 --- a/server/routes/v/[slug].ts +++ b/server/routes/v/[slug].ts @@ -1,5 +1,5 @@ export default defineEventHandler(async event => { - const config = useRuntimeConfig() + const config = useRuntimeConfig(event) const slug = getRouterParam(event, 'slug') if (!slug || !/^[\da-z]+$/.test(slug)) { throw createError({ statusCode: 400, statusMessage: 'Missing slug' }) diff --git a/server/utils/email.ts b/server/utils/email.ts index c9c966b2..c04d0f9e 100644 --- a/server/utils/email.ts +++ b/server/utils/email.ts @@ -1,8 +1,10 @@ -export function sendEmail (subject: string, body: string) { +import type { H3Event } from 'h3' + +export function sendEmail (event: H3Event, subject: string, body: string) { return $fetch('https://api.resend.com/email', { method: 'POST', headers: { - Authorization: `Bearer ${useRuntimeConfig().resendApiKey}`, + Authorization: `Bearer ${useRuntimeConfig(event).resendApiKey}`, }, body: { subject, diff --git a/server/utils/session.ts b/server/utils/session.ts index 2b28f877..cf406563 100644 --- a/server/utils/session.ts +++ b/server/utils/session.ts @@ -8,7 +8,7 @@ interface SessionData { } export function getUserSession (event: H3Event) { - const config = useRuntimeConfig() + const config = useRuntimeConfig(event) return getSession(event, { password: config.sessionPassword, name: 'token', @@ -17,7 +17,7 @@ export function getUserSession (event: H3Event) { } export function setUserSession (event: H3Event, data: any) { - const config = useRuntimeConfig() + const config = useRuntimeConfig(event) return updateSession( event, { @@ -30,7 +30,7 @@ export function setUserSession (event: H3Event, data: any) { } export function clearUserSession (event: H3Event) { - const config = useRuntimeConfig() + const config = useRuntimeConfig(event) return clearSession(event, { password: config.sessionPassword, name: 'token', diff --git a/server/utils/sponsors.ts b/server/utils/sponsors.ts index 550d3b9c..2adb0344 100644 --- a/server/utils/sponsors.ts +++ b/server/utils/sponsors.ts @@ -1,3 +1,4 @@ +import type { H3Event } from 'h3' import { query } from './github' interface Sponsor { @@ -12,8 +13,8 @@ interface CacheEntry { mtime: number } -export async function getSponsors (): Promise { - if (!useRuntimeConfig().github.token) return [] +export async function getSponsors (event: H3Event): Promise { + if (!useRuntimeConfig(event).github.token) return [] const entry: CacheEntry = ((await useStorage().getItem('sponsors')) as any) || {} const ttl = 60 @@ -22,12 +23,12 @@ export async function getSponsors (): Promise { const expired = Date.now() - (entry.mtime || 0) > ttl if (!entry.value || expired) { entry.value = await query( - useRuntimeConfig().github.token, + useRuntimeConfig(event).github.token, sponsorQuery, ).then(r => r.data?.user.sponsors.edges.map((e: any) => e.node) || []) // my ID - entry.value.push({ id: useRuntimeConfig().github.id }) + entry.value.push({ id: useRuntimeConfig(event).github.id }) // NuxtLabs entry.value.push({