[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
1import { addServerHandler, createResolver, defineNuxtModule, useNuxt, useRuntimeConfig } from 'nuxt/kit'
2import { $fetch } from 'ofetch'
3import { put, head } from '@vercel/blob'
4
5import { listAllRecords } from './shared/atproto-read'
6import type { DevRoeTalk } from '../shared/lex'
7
8export default defineNuxtModule({
9 meta: {
10 name: 'slides',
11 },
12 async setup () {
13 const nuxt = useNuxt()
14 const config = useRuntimeConfig()
15
16 if (!config.github.token || nuxt.options._prepare || nuxt.options.test) return
17
18 if (nuxt.options.dev) {
19 const resolver = createResolver(import.meta.url)
20 addServerHandler({
21 route: '/slides/:id.pdf',
22 handler: resolver.resolve('./runtime/server/slides/[id].pdf'),
23 })
24 return
25 }
26
27 const $gh = $fetch.create({
28 baseURL: 'https://api.github.com/repos/danielroe/slides/releases',
29 headers: {
30 Authorization: `token ${config.github.token}`,
31 },
32 })
33
34 const token = config.blobReadWriteToken
35
36 const releases = await fetchSlideReleases()
37
38 for (const release of releases) {
39 const pathname = `slides/${release}.pdf`
40
41 const existingBlob = await head(pathname, { token }).catch(() => null)
42 if (!existingBlob) {
43 const ghRelease = await $gh<GitHubRelease>(`/tags/${release}`)
44 const id = ghRelease?.assets.find(a => a.name.endsWith('.pdf'))?.id
45
46 if (!id) continue
47
48 const file = await $gh(`/assets/${id}`, {
49 responseType: 'arrayBuffer',
50 headers: { Accept: 'application/octet-stream' },
51 })
52
53 // Upload file to Vercel Blob
54 const blob = await put(pathname, Buffer.from(file), {
55 access: 'public',
56 token,
57 contentType: 'application/pdf',
58 })
59
60 nuxt.options.routeRules ||= {}
61 nuxt.options.routeRules[`/slides/${release}.pdf`] = {
62 redirect: blob.url,
63 }
64 }
65 else {
66 // File already exists, set up route rule with existing blob URL
67 nuxt.options.routeRules ||= {}
68 nuxt.options.routeRules[`/slides/${release}.pdf`] = {
69 redirect: existingBlob.url,
70 }
71 }
72 }
73 },
74})
75
76async function fetchSlideReleases (): Promise<string[]> {
77 const records = await listAllRecords<DevRoeTalk.Record>('dev.roe.talk')
78 return records
79 .map(r => r.value.slides)
80 .filter((s): s is string => Boolean(s))
81}
82
83interface GitHubRelease {
84 assets: Array<{ id: number, name: string }>
85}