diff --git a/.env.example b/.env.example
index 8b9a630..8cb0bfe 100644
--- a/.env.example
+++ b/.env.example
@@ -20,3 +20,5 @@ MAIL_TO=ie@asxp.io
# Datacenter bingo hostname (same process, routed by Host). Unset = bingo off.
# BINGO_HOST=bingo.localhost
+# Optional second hostname serving the same card.
+# BINGO_ALT_HOST=bingo2.localhost
diff --git a/.forgejo/workflows/deploy.yaml b/.forgejo/workflows/deploy.yaml
index 2bc2ed3..1b469e0 100644
--- a/.forgejo/workflows/deploy.yaml
+++ b/.forgejo/workflows/deploy.yaml
@@ -140,6 +140,7 @@ jobs:
LTC_ADDRESS=${LTC_ADDRESS}
CRYPTO_ADDRESSES=${CRYPTO_ADDRESSES}
BINGO_HOST=datacenterbingo.xyz
+ BINGO_ALT_HOST=bingo.asxp.io
EOF
chmod 600 /opt/asxpio/.env
diff --git a/bingo_web.rb b/bingo_web.rb
index c85cac2..600259c 100644
--- a/bingo_web.rb
+++ b/bingo_web.rb
@@ -2,13 +2,15 @@
# AsxpioWeb (config.ru dispatches by Host). Read-only: no session, no CSRF.
# Content is managed from asxp.io/admin/bingo (lib/bingo_admin.rb).
class BingoWeb < Sinatra::Base
+ HOSTS = [ENV['BINGO_HOST'], ENV['BINGO_ALT_HOST']].compact.reject(&:empty?).freeze
+
configure do
set :root, $root
set :views, File.join($root, 'views', 'bingo')
set :erb, layout: :layout, escape_html: true
set :show_exceptions, $env == 'development'
set :host_authorization,
- { permitted_hosts: $env == 'production' && ENV['BINGO_HOST'] ? [ENV['BINGO_HOST']] : [] }
+ { permitted_hosts: $env == 'production' ? HOSTS : [] }
end
helpers do
diff --git a/config.ru b/config.ru
index 3f1c1f3..7698a33 100644
--- a/config.ru
+++ b/config.ru
@@ -2,16 +2,19 @@ require 'sinatra/base'
require './asxpio.rb'
require './bingo_web.rb'
-# One process, two sites: BINGO_HOST gets the bingo, everything else asxp.io.
-asxpio = AsxpioWeb.new
-bingo = BingoWeb.new
-bingo_host = ENV['BINGO_HOST']
+# One process, two sites: the bingo hosts get BingoWeb, everything else
+# asxp.io. BINGO_HOST is canonical (www. redirects to it); BINGO_ALT_HOST
+# serves the same card, e.g. while the canonical domain's DNS settles.
+asxpio = AsxpioWeb.new
+bingo = BingoWeb.new
+bingo_host = ENV['BINGO_HOST']
+bingo_hosts = BingoWeb::HOSTS
run lambda { |env|
req = Rack::Request.new(env)
if bingo_host && req.host == "www.#{bingo_host}"
[301, { 'location' => "https://#{bingo_host}#{req.fullpath}" }, []]
else
- (bingo_host && req.host == bingo_host ? bingo : asxpio).call(env)
+ (bingo_hosts.include?(req.host) ? bingo : asxpio).call(env)
end
}
diff --git a/docker-compose.yml b/docker-compose.yml
index 9a22d6a..8ee718f 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -19,8 +19,9 @@ services:
- "traefik.http.routers.asxpio.service=asxpio"
- "traefik.http.services.asxpio.loadbalancer.server.port=3000"
# Datacenter bingo: same container, routed by Host in config.ru.
- # BINGO_HOST is interpolated from /opt/asxpio/.env (written by deploy).
- - "traefik.http.routers.asxpio-bingo.rule=Host(`${BINGO_HOST}`) || Host(`www.${BINGO_HOST}`)"
+ # BINGO_HOST / BINGO_ALT_HOST are interpolated from /opt/asxpio/.env
+ # (written by deploy).
+ - "traefik.http.routers.asxpio-bingo.rule=Host(`${BINGO_HOST}`) || Host(`www.${BINGO_HOST}`) || Host(`${BINGO_ALT_HOST}`)"
- "traefik.http.routers.asxpio-bingo.entrypoints=websecure"
- "traefik.http.routers.asxpio-bingo.tls=true"
- "traefik.http.routers.asxpio-bingo.tls.certresolver=letsencrypt"
diff --git a/views/bingo/index.erb b/views/bingo/index.erb
index 681f008..bf809f1 100644
--- a/views/bingo/index.erb
+++ b/views/bingo/index.erb
@@ -62,7 +62,10 @@
-
+
<% b.cells.each do |dc| %>
diff --git a/views/bingo/layout.erb b/views/bingo/layout.erb
index af36788..5a1d402 100644
--- a/views/bingo/layout.erb
+++ b/views/bingo/layout.erb
@@ -93,6 +93,7 @@
dialog ul { padding-left: 18px; }
dialog button { font: inherit; padding: 6px 14px; border-radius: 6px; border: 1px solid var(--line); background: var(--bg); color: var(--ink); cursor: pointer; }
footer { margin-top: 32px; color: var(--muted); font-size: 13px; }
+ footer p { margin: 0 0 4px; }
@media (max-width: 560px) {
.cell { padding: 5px; border-radius: 6px; }
.cell .sub, .cell .n, .cell .tag { display: none; }