add @user

This commit is contained in:
2026-01-16 16:48:55 +09:00
parent 40815a3b60
commit e704e52761
20 changed files with 328 additions and 41 deletions

View File

@@ -199,12 +199,14 @@ function getFaviconDir(did: string): string {
async function downloadFavicon(url: string, filepath: string): Promise<boolean> {
try {
const res = await fetch(url)
const res = await fetch(url, { redirect: 'follow' })
if (!res.ok) return false
const buffer = await res.arrayBuffer()
if (buffer.byteLength === 0) return false
fs.writeFileSync(filepath, Buffer.from(buffer))
return true
} catch {
} catch (err) {
console.error(`Failed to download ${url}:`, err)
return false
}
}
@@ -226,8 +228,21 @@ function getServiceDomain(collection: string): string | null {
return null
}
// Common service domains to always download favicons for
const COMMON_SERVICE_DOMAINS = [
'bsky.app',
'syui.ai',
'atproto.com',
'whtwnd.com',
'frontpage.fyi',
'pinksea.art',
'linkat.blue',
'tangled.sh',
'leaflet.pub',
]
function getServiceDomains(collections: string[]): string[] {
const domains = new Set<string>()
const domains = new Set<string>(COMMON_SERVICE_DOMAINS)
for (const col of collections) {
const domain = getServiceDomain(col)
if (domain) domains.add(domain)
@@ -241,21 +256,22 @@ async function downloadFavicons(did: string, domains: string[]): Promise<void> {
fs.mkdirSync(faviconDir, { recursive: true })
}
// Known favicon URLs (prefer official sources over Google)
// Others will use Google's favicon API as fallback
const faviconUrls: Record<string, string> = {
'bsky.app': 'https://bsky.app/static/favicon-32x32.png',
'syui.ai': 'https://syui.ai/favicon.png',
}
for (const domain of domains) {
const url = faviconUrls[domain]
if (!url) continue
const filepath = path.join(faviconDir, `${domain}.png`)
if (!fs.existsSync(filepath)) {
const ok = await downloadFavicon(url, filepath)
if (ok) {
console.log(`Downloaded: ${domain}.png`)
}
if (fs.existsSync(filepath)) continue
// Try known URL first, then fallback to Google's favicon API
const url = faviconUrls[domain] || `https://www.google.com/s2/favicons?domain=${domain}&sz=32`
const ok = await downloadFavicon(url, filepath)
if (ok) {
console.log(`Downloaded: ${domain}.png`)
}
}
}
@@ -801,12 +817,20 @@ async function generate() {
console.log('Generated: /app.html')
// Generate _redirects for Cloudflare Pages (SPA routes)
const redirects = `/app / 301
/oauth/* /app.html 200
// Static files (index.html, post/*/index.html) are served automatically
// Dynamic routes are rewritten to app.html
const redirects = `/oauth/* /app.html 200
/at/* /app.html 200
/new /app.html 200
/app /app.html 200
`
fs.writeFileSync(path.join(distDir, '_redirects'), redirects)
console.log('Generated: /_redirects')
// Generate 404.html as SPA fallback for unmatched routes (like /@handle)
fs.writeFileSync(path.join(distDir, '404.html'), spaHtml)
console.log('Generated: /404.html')
// Copy static files
const filesToCopy = ['favicon.png', 'favicon.svg', 'config.json', 'networks.json', 'client-metadata.json', 'links.json']
for (const file of filesToCopy) {