Skip to content

Commit ef871b7

Browse files
committed
Add a user friendly maintenance page
So that we don't simply display "Internal server error" when migrations are pending.
1 parent 2713e4b commit ef871b7

File tree

3 files changed

+106
-9
lines changed

3 files changed

+106
-9
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# frozen_string_literal: true
2+
3+
class PendingMigrationCatcher
4+
def initialize(app)
5+
@app = app
6+
end
7+
8+
def call(env)
9+
@app.call(env)
10+
rescue ActiveRecord::PendingMigrationError, ActiveRecord::NoDatabaseError => e
11+
render_maintenance_page(e)
12+
end
13+
14+
private
15+
16+
def render_maintenance_page(_error)
17+
html = <<~HTML
18+
<!DOCTYPE html>
19+
<html>
20+
<head>
21+
<title>Upgrade in Progress</title>
22+
<meta name="viewport" content="width=device-width, initial-scale=1">
23+
<meta http-equiv="refresh" content="30">
24+
<style>
25+
* { margin: 0; padding: 0; box-sizing: border-box; }
26+
body {
27+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
28+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
29+
min-height: 100vh;
30+
display: flex;
31+
align-items: center;
32+
justify-content: center;
33+
padding: 20px;
34+
}
35+
.container {
36+
background: white;
37+
border-radius: 16px;
38+
padding: 48px;
39+
max-width: 500px;
40+
text-align: center;
41+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
42+
}
43+
.icon {
44+
font-size: 64px;
45+
margin-bottom: 24px;
46+
}
47+
h1 {
48+
color: #1a202c;
49+
font-size: 28px;
50+
margin-bottom: 16px;
51+
}
52+
p {
53+
color: #4a5568;
54+
font-size: 16px;
55+
line-height: 1.6;
56+
margin-bottom: 24px;
57+
}
58+
.spinner {
59+
width: 40px;
60+
height: 40px;
61+
border: 4px solid #e2e8f0;
62+
border-top-color: #667eea;
63+
border-radius: 50%;
64+
animation: spin 1s linear infinite;
65+
margin: 0 auto;
66+
}
67+
@keyframes spin {
68+
to { transform: rotate(360deg); }
69+
}
70+
.note {
71+
color: #718096;
72+
font-size: 14px;
73+
margin-top: 24px;
74+
}
75+
</style>
76+
</head>
77+
<body>
78+
<div class="container">
79+
<div class="icon">🚀</div>
80+
<h1>Upgrade in Progress</h1>
81+
<p>We're currently upgrading the system to bring you new features and improvements. This should only take a moment.</p>
82+
<div class="spinner"></div>
83+
<p class="note">This page will automatically refresh.</p>
84+
</div>
85+
</body>
86+
</html>
87+
HTML
88+
89+
[
90+
503,
91+
{
92+
"Content-Type" => "text/html; charset=utf-8",
93+
"Retry-After" => "30"
94+
},
95+
[html]
96+
]
97+
end
98+
end

config/application.rb

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,7 @@ class Application < Rails::Application
1616
# Common ones are `templates`, `generators`, or `middleware`, for example.
1717
config.autoload_lib(ignore: %w[assets tasks])
1818

19-
# Configuration for the application, engines, and railties goes here.
20-
#
21-
# These settings can be overridden in specific environments using the files
22-
# in config/environments, which are processed later.
23-
#
24-
# config.time_zone = "Central Time (US & Canada)"
25-
# config.eager_load_paths << Rails.root.join("extras")
19+
require_relative "../app/middleware/pending_migration_catcher"
20+
config.middleware.insert_before ActionDispatch::ShowExceptions, PendingMigrationCatcher
2621
end
2722
end

config/routes.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@
44
end
55
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
66

7-
# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
8-
# Can be used by load balancers and uptime monitors to verify that the app is live.
97
get "up" => "rails/health#show", as: :rails_health_check
8+
9+
if Rails.env.development?
10+
get "preview/maintenance" => ->(env) {
11+
PendingMigrationCatcher.new(nil).send(:render_maintenance_page, nil)
12+
}
13+
end
1014
get "messages/by-id/*message_id", to: "messages#by_message_id", as: :message_by_id, format: false
1115

1216
# Render dynamic PWA files from app/views/pwa/* (remember to link manifest in application.html.erb)

0 commit comments

Comments
 (0)