Оформление оффлайн-страницы сайта

This commit is contained in:
2026-06-26 18:14:54 +03:00
parent 75feb9d15a
commit 6d75b4c130
3 changed files with 98 additions and 3 deletions
+21
View File
@@ -11,6 +11,25 @@
#
# SSE (/api/events): отдельный handle БЕЗ encode и с flush_interval -1 — иначе сжатие/
# буферизация задержат поток событий (реал-тайм перестанет работать).
#
# Туннель лёг (апстрим недоступен) → reverse_proxy отдаёт 502/503/504, и сниппет
# (offline) подменяет ответ на свою страницу-заглушку (/etc/caddy/maintenance/maintenance.html),
# вместо «зависшей загрузки» SPA. Файл заглушки — deploy/vps/maintenance.html.
# Заглушка «сайт недоступен» при недоступности апстрима. Импортируется в каждый сайт.
(offline) {
handle_errors {
@down expression `{http.error.status_code} in [502, 503, 504]`
handle @down {
root * /etc/caddy/maintenance
rewrite * /maintenance.html
header Cache-Control "no-store"
file_server {
status 503
}
}
}
}
forbiddenstars.ru {
tls /etc/caddy/certs/forbiddenstars.ru/fullchain.pem /etc/caddy/certs/forbiddenstars.ru/privkey.pem
@@ -24,6 +43,7 @@ forbiddenstars.ru {
encode zstd gzip
reverse_proxy 127.0.0.1:9000
}
import offline
}
forbidden-stars.ru {
@@ -38,4 +58,5 @@ forbidden-stars.ru {
encode zstd gzip
reverse_proxy 127.0.0.1:9001
}
import offline
}
+13 -3
View File
@@ -85,16 +85,26 @@ rm -rf /root/certs-tmp
> как «промежуточную» часть. Если дал один файл, где leaf и chain уже вместе, — это и есть
> `fullchain.pem` (склейка не нужна).
## 6. Caddyfile
Скопируй `deploy/vps/Caddyfile` из репозитория в `/etc/caddy/Caddyfile`, затем:
## 6. Caddyfile + страница-заглушка
Скопируй `deploy/vps/Caddyfile` из репозитория в `/etc/caddy/Caddyfile` и положи страницу
«сайт недоступен» (показывается, когда туннель к хосту лежит — вместо зависшей «Загрузки» SPA):
```bash
mkdir -p /etc/caddy/maintenance
# с локальной машины:
scp deploy/vps/maintenance.html root@186.246.51.17:/etc/caddy/maintenance/maintenance.html
# на VPS:
caddy validate --config /etc/caddy/Caddyfile
systemctl reload caddy
```
> Заглушка живёт в сниппете `(offline)` Caddyfile: при ответе апстрима 502/503/504
> Caddy отдаёт `/etc/caddy/maintenance/maintenance.html` со статусом 503 и `Cache-Control: no-store`.
> Правки текста/вида — в `deploy/vps/maintenance.html`, затем повтори `scp` (reload Caddy не нужен,
> файл читается на каждый запрос).
## 7. Проверка
1. Подними туннель прода на Pi (см. `deploy/pi/README.md`) и/или dev/test на ПК (`run.ps1` при `LOCAL_PUBLIC=vps`).
2. Открой `https://forbiddenstars.ru` и `https://forbidden-stars.ru`.
3. Пока соответствующий туннель не поднят — Caddy отдаёт `502`, это ожидаемо.
3. Пока соответствующий туннель не поднят — Caddy отдаёт страницу-заглушку «Технические шоколадки»
(HTTP 503), это ожидаемо.
Логи Caddy: `journalctl -u caddy -f`.
+64
View File
@@ -0,0 +1,64 @@
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="robots" content="noindex" />
<!-- Сама перепроверяется: когда хост вернётся, страница перезагрузится и пустит в приложение. -->
<meta http-equiv="refresh" content="20" />
<title>Сайт недоступен — Forbidden Stars</title>
<style>
:root {
--bg: #0f1115;
--surface: #171a21;
--border: #2a2f3a;
--text: #e8eaed;
--text-muted: #9aa0aa;
--accent: #e0533d;
}
* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
background: var(--bg);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
}
.card {
max-width: 420px;
width: 100%;
text-align: center;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 16px;
padding: 32px 24px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}
.emoji { font-size: 56px; line-height: 1; margin-bottom: 14px; }
h1 { font-size: 22px; margin: 0 0 10px; }
p { color: var(--text-muted); font-size: 15px; line-height: 1.5; margin: 0 0 18px; }
.hint { font-size: 13px; color: var(--text-muted); }
.dot {
display: inline-block;
width: 8px; height: 8px;
border-radius: 50%;
background: var(--accent);
margin-right: 6px;
animation: pulse 1.2s ease-in-out infinite;
}
@keyframes pulse { 0%, 100% { opacity: 0.4; } 50% { opacity: 1; } }
</style>
</head>
<body>
<div class="card">
<div class="emoji">🍫</div>
<h1>Технические шоколадки</h1>
<p>Сайт временно недоступен — мы уже чиним. Загляните через минутку.</p>
<div class="hint"><span class="dot"></span>Страница обновится сама, как только всё заработает</div>
</div>
</body>
</html>