ai generated solutions to our ai generated problems

This commit is contained in:
2026-06-15 08:59:15 +01:00
parent 91a657522a
commit 5bd8cab0ce
8 changed files with 56 additions and 778 deletions
+2 -214
View File
@@ -46,9 +46,6 @@ const UPTIME_HISTORY_LIMIT = Number(process.env.UPTIME_HISTORY_LIMIT || 336)
const ANALYTICS_DATABASE_FILE = process.env.ANALYTICS_DATABASE_FILE || 'viewers.sqlite'
const ANALYTICS_RETENTION_DAYS = Number(process.env.ANALYTICS_RETENTION_DAYS || 30)
const ANALYTICS_ACTIVE_WINDOW_SECONDS = Number(process.env.ANALYTICS_ACTIVE_WINDOW_SECONDS || 75)
const LASTFM_API_KEY = process.env.LASTFM_API_KEY || ''
const LASTFM_USERNAME = process.env.LASTFM_USERNAME || ''
const LASTFM_HISTORY_FILE = process.env.LASTFM_HISTORY_FILE || 'lastfm-song-of-day.json'
const API_CACHE_TTL_MS = Number(process.env.API_CACHE_TTL_MS || 15000)
const API_RATE_LIMIT_WINDOW_MS = Number(process.env.API_RATE_LIMIT_WINDOW_MS || 60000)
const API_RATE_LIMIT_MAX = Number(process.env.API_RATE_LIMIT_MAX || 120)
@@ -129,7 +126,7 @@ const CSP_DIRECTIVES = [
"style-src 'self'",
"style-src-elem 'self'",
"style-src-attr 'unsafe-inline'",
"img-src 'self' data: blob: https://*.basemaps.cartocdn.com https://basemaps.cartocdn.com https://lastfm.freetls.fastly.net https://*.lastfm.freetls.fastly.net",
"img-src 'self' data: blob: https://*.basemaps.cartocdn.com https://basemaps.cartocdn.com",
"media-src https://*.dzcdn.net https://*.deezer.com",
"font-src 'self' data:",
"connect-src 'self' https://challenges.cloudflare.com",
@@ -182,199 +179,6 @@ let uptimeDb = null
let analyticsDb = null
let latestUptimeSnapshot = null
function songHistoryPath() {
const fileName = path.basename(LASTFM_HISTORY_FILE || 'lastfm-song-of-day.json')
return path.join(uptimeStoragePath(), fileName)
}
function readSongHistory() {
try {
return JSON.parse(fs.readFileSync(songHistoryPath(), 'utf8'))
} catch {
return { picks: [] }
}
}
function writeSongHistory(history) {
const storageDir = uptimeStoragePath()
fs.mkdirSync(storageDir, { recursive: true })
fs.writeFileSync(songHistoryPath(), JSON.stringify(history, null, 2))
}
function todayKey() {
return new Date().toISOString().slice(0, 10)
}
function lastfmImage(track) {
const images = Array.isArray(track.image) ? track.image : []
const image = [...images].reverse().find((item) => item?.['#text'])
const url = image?.['#text'] || ''
if (!url) return ''
try {
const parsed = new URL(url)
if (parsed.protocol !== 'https:') return ''
if (parsed.hostname !== 'lastfm.freetls.fastly.net' && !parsed.hostname.endsWith('.lastfm.freetls.fastly.net')) return ''
return parsed.toString()
} catch {
return ''
}
}
function normalizeLastfmTrack(track) {
const artist = track?.artist?.['#text'] || track?.artist?.name || ''
const name = track?.name || ''
if (!artist || !name) return null
return {
id: `${artist.toLowerCase()}::${name.toLowerCase()}`,
artist,
name,
album: track?.album?.['#text'] || '',
url: track?.url || '',
image: lastfmImage(track),
played_at: track?.date?.uts ? Number(track.date.uts) : null,
}
}
function randomItem(items) {
if (!items.length) return null
const index = crypto.randomInt(0, items.length)
return items[index]
}
function normalizeComparable(value) {
return String(value || '')
.toLowerCase()
.normalize('NFKD')
.replace(/[\u0300-\u036f]/g, '')
.replace(/&/g, ' and ')
.replace(/[^a-z0-9]+/g, ' ')
.trim()
}
function previewUrl(value) {
if (!value) return ''
try {
const parsed = new URL(value)
if (parsed.protocol === 'http:') parsed.protocol = 'https:'
if (parsed.protocol !== 'https:') return ''
if (!parsed.hostname.endsWith('.dzcdn.net') && !parsed.hostname.endsWith('.deezer.com')) return ''
if (!parsed.pathname.endsWith('.mp3')) return ''
return parsed.toString()
} catch {
return ''
}
}
async function findDeezerPreview(track) {
if (!track?.artist || !track?.name) return null
const url = new URL('https://api.deezer.com/search/track')
url.searchParams.set('q', `${track.artist} ${track.name}`)
url.searchParams.set('limit', '5')
const result = await requestJson(url.toString(), 8000)
const candidates = Array.isArray(result.body?.data) ? result.body.data : []
const targetArtist = normalizeComparable(track.artist)
const targetTitle = normalizeComparable(track.name)
const matched = candidates.find((candidate) => {
const candidateArtist = normalizeComparable(candidate?.artist?.name)
const candidateTitle = normalizeComparable(candidate?.title_short || candidate?.title)
if (!candidateArtist || !candidateTitle) return false
return (
candidateTitle === targetTitle &&
(candidateArtist === targetArtist ||
candidateArtist.includes(targetArtist) ||
targetArtist.includes(candidateArtist))
)
}) || candidates[0]
const preview = previewUrl(matched?.preview)
if (!preview) return null
return {
preview_url: preview,
preview_provider: 'Deezer',
preview_track_url: matched?.link || '',
}
}
async function songOfTheDay() {
if (!LASTFM_API_KEY || !LASTFM_USERNAME) {
return {
configured: false,
error: 'Last.fm is not configured',
}
}
const date = todayKey()
const history = readSongHistory()
const existing = (history.picks || []).find((pick) => pick.date === date)
if (existing?.track) {
if (!existing.track.preview_url) {
try {
const preview = await findDeezerPreview(existing.track)
if (preview) {
existing.track = { ...existing.track, ...preview }
writeSongHistory(history)
}
} catch {
// The Last.fm card still works without a preview.
}
}
return { configured: true, date, track: existing.track }
}
const to = Math.floor(Date.now() / 1000)
const from = to - 7 * 24 * 60 * 60
const url = new URL('https://ws.audioscrobbler.com/2.0/')
url.searchParams.set('method', 'user.getrecenttracks')
url.searchParams.set('user', LASTFM_USERNAME)
url.searchParams.set('api_key', LASTFM_API_KEY)
url.searchParams.set('format', 'json')
url.searchParams.set('from', String(from))
url.searchParams.set('to', String(to))
url.searchParams.set('limit', '200')
const result = await requestJson(url.toString(), 10000)
const tracks = (result.body?.recenttracks?.track || [])
.map(normalizeLastfmTrack)
.filter(Boolean)
const uniqueTracks = Array.from(new Map(tracks.map((track) => [track.id, track])).values())
if (!uniqueTracks.length) {
return {
configured: true,
date,
error: 'No Last.fm tracks found from the last week',
}
}
const previousIds = new Set((history.picks || []).map((pick) => pick.track?.id).filter(Boolean))
const available = uniqueTracks.filter((track) => !previousIds.has(track.id))
let track = randomItem(available.length ? available : uniqueTracks)
try {
const preview = await findDeezerPreview(track)
if (preview) track = { ...track, ...preview }
} catch {
// Preview playback is opportunistic; metadata should still render.
}
const nextHistory = {
picks: [
...(history.picks || []).filter((pick) => pick.date !== date),
{ date, track },
]
.sort((a, b) => String(a.date).localeCompare(String(b.date)))
.slice(-366),
}
writeSongHistory(nextHistory)
return { configured: true, date, track }
}
function sendJson(res, status, body, headers = {}) {
send(res, status, JSON.stringify(body), { ...jsonHeaders, ...headers })
}
@@ -2085,6 +1889,7 @@ function htmlWithSeo(req, data) {
.replaceAll('__SEO_ROBOTS__', escapeHtml(seo.robots))
.replaceAll('__SEO_CANONICAL__', escapeHtml(canonicalUrl))
.replaceAll('__SEO_JSON_LD__', routeStructuredData(origin, seo, canonicalUrl).replace(/</g, '\\u003c'))
.replaceAll('__TURNSTILE_SESSION__', isTurnstileSessionVerified(req) ? 'verified' : 'required')
}
function sendHtml(req, res, data, status = 200) {
@@ -2256,23 +2061,6 @@ const server = http.createServer((req, res) => {
return
}
if (req.method === 'GET' && req.url === '/api/song-of-day') {
if (!isSameOriginRequest(req)) {
sendJson(res, 403, { error: 'Song of the day is restricted to this site' })
return
}
if (isRateLimited(req)) {
sendJson(res, 429, { error: 'Too many requests' }, { 'retry-after': String(Math.ceil(API_RATE_LIMIT_WINDOW_MS / 1000)) })
return
}
songOfTheDay()
.then((data) => sendJson(res, data.error ? 503 : 200, data))
.catch((error) => sendJson(res, 502, { configured: Boolean(LASTFM_API_KEY && LASTFM_USERNAME), error: 'Last.fm request failed', detail: error.message }))
return
}
if (req.method === 'POST' && req.url === '/api/viewers/event') {
if (!isSameOriginRequest(req)) {
sendJson(res, 403, { error: 'Analytics events are restricted to this site' })