bingo: ping IndexNow when the card changes
A newly recorded incident only showed up in search once a crawler happened to come back. IndexNow lets Bing, Yandex and the other participating engines know about changed URLs right away. Bingo::IndexNow posts the card URLs plus the affected datacenter pages to api.indexnow.org, in a background thread so admin saves don't wait. Incidents ping their datacenter. Edits ping the datacenter, or every page when positions move (create, delete, reorder, sort). A deleted datacenter's pages are included so the engines see the 404. BingoWeb serves the key at /<key>.txt. The key comes from the INDEXNOW Forgejo secret, written to .env as INDEXNOW_KEY. With no key set this does nothing. Assisted-by: Claude:opus-5
This commit is contained in:
parent
ca48f4292b
commit
26e862ff04
6 changed files with 106 additions and 2 deletions
39
lib/bingo.rb
39
lib/bingo.rb
|
|
@ -397,6 +397,45 @@ module Bingo
|
|||
end
|
||||
end
|
||||
|
||||
# Tells IndexNow engines (Bing, Yandex, Seznam, ...) which bingo URLs
|
||||
# changed, so new incidents get recrawled within minutes. The key comes
|
||||
# from INDEXNOW_KEY and BingoWeb serves it at /<key>.txt; without the key
|
||||
# or BINGO_HOST (dev, tests) this does nothing.
|
||||
module IndexNow
|
||||
ENDPOINT = URI('https://api.indexnow.org/indexnow')
|
||||
|
||||
module_function
|
||||
|
||||
def key = ENV['INDEXNOW_KEY'].to_s[/\A[A-Za-z0-9-]{8,128}\z/]
|
||||
def host = ENV['BINGO_HOST'].to_s[/\A\S+\z/]
|
||||
|
||||
# The card in every locale plus the pages of the given datacenters.
|
||||
def urls(dcs = [])
|
||||
paths = LOCALES.map { |l| "/#{l}" } + dcs.flat_map { |dc| LOCALES.map { |l| "/#{l}/dc/#{dc.slug}" } }
|
||||
paths.map { |p| "https://#{host}#{p}" }
|
||||
end
|
||||
|
||||
def payload(dcs = [])
|
||||
{ host: host, key: key, keyLocation: "https://#{host}/#{key}.txt", urlList: urls(dcs) }
|
||||
end
|
||||
|
||||
# Fire and forget: an admin save doesn't wait on a third party.
|
||||
def ping(dcs = [])
|
||||
return unless key && host
|
||||
body = JSON.generate(payload(dcs))
|
||||
Thread.new { deliver(body) }
|
||||
end
|
||||
|
||||
def deliver(body)
|
||||
res = Net::HTTP.start(ENDPOINT.host, ENDPOINT.port, use_ssl: true, open_timeout: 5, read_timeout: 10) do |http|
|
||||
http.post(ENDPOINT.path, body, 'Content-Type' => 'application/json; charset=utf-8')
|
||||
end
|
||||
$logger.warn("bingo: IndexNow answered #{res.code}") unless res.is_a?(Net::HTTPSuccess)
|
||||
rescue StandardError => e
|
||||
$logger.warn("bingo: IndexNow #{e.class}: #{e.message}")
|
||||
end
|
||||
end
|
||||
|
||||
# Positions stay dense 1..N; the card shows the first SIZE*SIZE, the rest
|
||||
# wait in reserve. Placing a datacenter inserts it at pos (default: last)
|
||||
# and shifts everyone after it.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue