bingo: SEO pass and resized photos
All checks were successful
Build and Deploy to Production / test (push) Successful in 22s
Build and Deploy to Production / build (push) Successful in 1m1s
Build and Deploy to Production / deploy (push) Successful in 39s

The bingo host fell through to asxp.io's public/, so crawlers got
asxp.io's robots.txt pointing at asxp.io's sitemap (and the keys and
invoice JS were served too). Turn static files off for BingoWeb and give
it its own robots.txt and a sitemap with hreflang alternates.

The card loaded 15.6 MB of full-size photos as cell backgrounds. Derive
480/1200 px WebP variants with ImageMagick on store (memory/area limits,
input coder pinned to the sniffed type), render cells as <img> with lazy
loading past the first two rows, and add an admin action that rebuilds
variants from the stored originals.

Head gets per-locale titles and descriptions carrying the live score,
og:image from the latest hit, Twitter card, og:locale alternates,
JSON-LD WebSite + ItemList, and Vary: Accept-Language on the redirect.

Assisted-by: Claude:opus-5
This commit is contained in:
Sergei Poljanski 2026-10-10 06:26:03 +04:00
commit 201a70b07b
Signed by: asxpi
GPG key ID: 4F8851660FA4121B
11 changed files with 236 additions and 23 deletions

View file

@ -127,9 +127,9 @@ module BingoAdmin
dc.photo_source_url = url
Bingo::Photo.fetch(url)
end
dc.photo_key = Bingo::Photo.store(dc.slug, bytes)
dc.save_changes
session[:bingo_flash] = 'Photo stored.'
Bingo::Photo.attach(dc, bytes)
session[:bingo_flash] = dc.photo_variants ? 'Photo stored.' : 'Photo stored (no resized variants; check logs).'
rescue Bingo::Photo::Error => e
session[:bingo_flash] = "Photo failed: #{e.message}"
end
@ -138,7 +138,7 @@ module BingoAdmin
app.post '/admin/bingo/dc/:id/photo/delete' do
dc = Bingo::Datacenter[params[:id].to_i] or halt 404
dc.update(photo_key: nil)
dc.update(photo_key: nil, photo_variants: false)
session[:bingo_flash] = 'Photo removed from card (object kept in bucket).'
redirect "/admin/bingo/dc/#{dc.id}"
end
@ -150,7 +150,7 @@ module BingoAdmin
scope = Bingo::Datacenter.exclude(photo_source_url: nil)
scope = scope.where(photo_key: nil) unless params[:all] == '1'
scope.each do |dc|
dc.update(photo_key: Bingo::Photo.store(dc.slug, Bingo::Photo.fetch(dc.photo_source_url)))
Bingo::Photo.attach(dc, Bingo::Photo.fetch(dc.photo_source_url))
ok += 1
rescue Bingo::Photo::Error => e
failed << "#{dc.slug}: #{e.message}"
@ -159,6 +159,15 @@ module BingoAdmin
redirect '/admin/bingo'
end
# Regenerate resized variants from the originals already in the bucket.
app.post '/admin/bingo/photos/rebuild' do
dcs = Bingo::Datacenter.exclude(photo_key: nil).all
dcs.each { |dc| Bingo::Photo.rebuild(dc) }
failed = dcs.reject(&:photo_variants).map(&:slug)
session[:bingo_flash] = "Rebuilt #{dcs.size - failed.size}/#{dcs.size}.#{" Failed: #{failed.join(', ')}" if failed.any?}"
redirect '/admin/bingo'
end
app.get '/admin/bingo/p/:file' do
bytes, type = Bingo::Photo.read(params[:file])
content_type type