feat(gateway): add nginx gateway for maintenance mode fallback
Proxy frontend traffic through Nginx to handle service restarts gracefully Serve a static 503 maintenance page when frontend or backend is unavailable Update deployment design docs and docker-compose configuration
This commit is contained in:
224
frontend/gateway/maintenance.html
Normal file
224
frontend/gateway/maintenance.html
Normal file
@@ -0,0 +1,224 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<meta http-equiv="refresh" content="30" />
|
||||
<title>Pokopia Wiki is upgrading</title>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--pokemon-yellow: #ffcb05;
|
||||
--pokemon-yellow-soft: #ffe46b;
|
||||
--pokemon-blue: #2a75bb;
|
||||
--pokemon-blue-deep: #003a70;
|
||||
--pokemon-red: #ee1515;
|
||||
--pokemon-red-deep: #cc0000;
|
||||
--bg: #f2f5fa;
|
||||
--bg-alt: #eaf1fb;
|
||||
--surface: #ffffff;
|
||||
--surface-soft: #f8fafd;
|
||||
--ink: #151923;
|
||||
--ink-soft: #354052;
|
||||
--muted: #687487;
|
||||
--line: #d8deea;
|
||||
--line-strong: #1f2a3b;
|
||||
--shadow-raised: 0 14px 32px rgba(23, 35, 54, .13);
|
||||
--font-sans: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
--font-display: "Arial Rounded MT Bold", "Nunito", "Avenir Next Rounded", var(--font-sans);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
min-width: 320px;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
color: var(--ink);
|
||||
font-family: var(--font-sans);
|
||||
background:
|
||||
linear-gradient(90deg, rgba(42, 117, 187, .08) 1px, transparent 1px) 0 0 / 32px 32px,
|
||||
linear-gradient(rgba(42, 117, 187, .08) 1px, transparent 1px) 0 0 / 32px 32px,
|
||||
linear-gradient(180deg, var(--bg) 0%, var(--bg-alt) 100%);
|
||||
}
|
||||
|
||||
main {
|
||||
min-height: 100vh;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 32px 20px;
|
||||
}
|
||||
|
||||
.maintenance-card {
|
||||
width: min(100%, 560px);
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(31, 42, 59, .14);
|
||||
border-radius: 8px;
|
||||
background: var(--surface);
|
||||
box-shadow: var(--shadow-raised);
|
||||
}
|
||||
|
||||
.status-ribbon {
|
||||
height: 12px;
|
||||
background:
|
||||
linear-gradient(90deg, var(--pokemon-red) 0 28%, var(--line-strong) 28% 34%, var(--surface) 34% 66%, var(--line-strong) 66% 72%, var(--pokemon-blue) 72% 100%);
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: clamp(28px, 6vw, 48px);
|
||||
}
|
||||
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.mark {
|
||||
position: relative;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
flex: 0 0 auto;
|
||||
overflow: hidden;
|
||||
border: 4px solid var(--line-strong);
|
||||
border-radius: 50%;
|
||||
background:
|
||||
linear-gradient(180deg, var(--pokemon-red) 0 45%, var(--line-strong) 45% 55%, var(--surface) 55% 100%);
|
||||
box-shadow: 0 4px 0 rgba(31, 42, 59, .2);
|
||||
}
|
||||
|
||||
.mark::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 13px;
|
||||
border: 4px solid var(--line-strong);
|
||||
border-radius: 50%;
|
||||
background: var(--surface);
|
||||
}
|
||||
|
||||
.brand-name {
|
||||
display: block;
|
||||
color: var(--pokemon-yellow);
|
||||
font-family: var(--font-display);
|
||||
font-size: 2rem;
|
||||
font-weight: 900;
|
||||
line-height: .95;
|
||||
-webkit-text-stroke: 2px var(--pokemon-blue-deep);
|
||||
text-shadow: 2px 3px 0 var(--pokemon-blue);
|
||||
}
|
||||
|
||||
.brand-subtitle {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
color: var(--muted);
|
||||
font-size: .78rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 34px;
|
||||
padding: 7px 10px;
|
||||
border: 1px solid color-mix(in srgb, var(--pokemon-blue) 28%, var(--line));
|
||||
border-radius: 8px;
|
||||
background: color-mix(in srgb, var(--pokemon-blue) 10%, var(--surface));
|
||||
color: var(--pokemon-blue-deep);
|
||||
font-size: .82rem;
|
||||
font-weight: 800;
|
||||
line-height: 1.2;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 20px 0 10px;
|
||||
color: var(--ink);
|
||||
font-size: 3rem;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0;
|
||||
line-height: 1.04;
|
||||
}
|
||||
|
||||
p {
|
||||
max-width: 38rem;
|
||||
margin: 0;
|
||||
color: var(--ink-soft);
|
||||
font-size: 1.12rem;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.meter {
|
||||
height: 12px;
|
||||
margin-top: 30px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--line-strong);
|
||||
border-radius: 999px;
|
||||
background: var(--surface-soft);
|
||||
}
|
||||
|
||||
.meter span {
|
||||
display: block;
|
||||
width: 70%;
|
||||
height: 100%;
|
||||
border-right: 1px solid rgba(31, 42, 59, .28);
|
||||
background: linear-gradient(90deg, var(--pokemon-yellow) 0%, var(--pokemon-yellow-soft) 46%, var(--pokemon-blue) 100%);
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
main {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.maintenance-card {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.brand {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.brand-name {
|
||||
font-size: 1.65rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main aria-labelledby="maintenance-title">
|
||||
<section class="maintenance-card" aria-live="polite">
|
||||
<div class="status-ribbon" aria-hidden="true"></div>
|
||||
<div class="content">
|
||||
<div class="brand">
|
||||
<span class="mark" aria-hidden="true"></span>
|
||||
<div>
|
||||
<span class="brand-name">Pokopia</span>
|
||||
<span class="brand-subtitle">Wiki</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="status">Upgrading</span>
|
||||
<h1 id="maintenance-title">Pokopia Wiki is upgrading</h1>
|
||||
<p>We'll be online within 5 minutes.</p>
|
||||
<div class="meter" aria-hidden="true"><span></span></div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user