Packages
phoenix_kit
1.7.201
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/modules/maintenance/README.md
# Maintenance Mode Module
System-wide maintenance mode that redirects non-admin users to a dedicated `/maintenance` LiveView page. Admins and owners bypass maintenance and see the real site.
## Features
- **Redirect-based** -- plug redirects to `/maintenance` LiveView, on_mount hooks redirect LiveView routes
- **LiveView maintenance page** -- supports countdown timers, PubSub auto-redirect, admin preview, extensible with custom components (live chat, status updates, etc.)
- **Scheduled maintenance** -- set a start/end UTC window; `active?/0` checks manual toggle OR scheduled window, no Oban job needed
- **Role-based bypass** -- admins and owners access the site normally during maintenance
- **PubSub real-time updates** -- when maintenance ends, all connected users auto-redirect back (no manual refresh)
- **Retry-After header** -- HTTP header sent with redirect when a scheduled end time is known
- **Customizable content** -- header and subtext configurable via admin settings with live preview
- **Activity logging** -- all admin actions (toggle, content changes, schedule) are logged
## Module Structure
```
lib/modules/maintenance/
├── README.md # This documentation
├── maintenance.ex # Context module (active?, schedule, PubSub)
├── settings.ex # Admin settings LiveView
└── web/
├── plugs/
│ └── maintenance_mode.ex # HTTP plug — redirects non-admins to /maintenance
├── components/
│ └── maintenance_page.ex # Reusable card component
└── maintenance_page_live.ex # Public /maintenance LiveView page
```
## How It Works
### Request Flow
1. **HTTP request** hits `PhoenixKitWeb.Plugs.Integration` (in browser pipeline)
2. `MaintenanceMode` plug checks `Maintenance.active?()`
3. If active:
- Static assets and auth routes pass through (login, reset-password, etc.)
- Admin/owner users pass through (checked via session token + Scope)
- The `/maintenance` path itself passes through (prevents redirect loop)
- Everyone else gets a **302 redirect to `/maintenance`** with optional `Retry-After` header
4. **LiveView routes** are also protected via `check_maintenance_mode/1` in `auth.ex` on_mount hooks
### Maintenance Page LiveView
The `/maintenance` route renders a full-screen LiveView (`PhoenixKitWeb.Live.Modules.Maintenance.Page`) that:
- Shows the customizable header + subtext with a construction emoji
- Displays a live countdown timer if a scheduled end time is set (JS hook)
- Subscribes to PubSub and auto-redirects users to `/` when maintenance ends
- Shows an admin preview banner with link to settings if an admin visits directly
- Redirects to `/` if maintenance is not active (prevents stale bookmarks)
### active?/0
The main check used by the plug and on_mount:
```elixir
def active? do
manually_enabled?() or within_scheduled_window?()
end
```
Returns true when either:
- The manual toggle (`maintenance_enabled` setting) is on, OR
- The current UTC time falls between `maintenance_scheduled_start` and `maintenance_scheduled_end`
## Context API
### `PhoenixKit.Modules.Maintenance`
**Status checks:**
| Function | Returns | Description |
|----------|---------|-------------|
| `active?/0` | `boolean` | Main check: `true` if maintenance is currently active (manual toggle OR schedule), auto-disables if past scheduled end |
| `manually_enabled?/0` | `boolean` | Just the manual toggle state |
| `past_scheduled_start?/0` | `boolean` | `true` if now >= scheduled start |
| `past_scheduled_end?/0` | `boolean` | `true` if now >= scheduled end |
| `within_scheduled_window?/0` | `boolean` | `true` if inside an active scheduled window |
| `seconds_until_end/0` | `integer \| nil` | Seconds until maintenance ends (for Retry-After + countdown) |
**Mutations:**
| Function | Returns | Description |
|----------|---------|-------------|
| `enable_system/0` | `{:ok, _} \| {:error, _}` | Turn on manual toggle, clears expired schedule |
| `disable_system/0` | `{:ok, _} \| {:error, _}` | Turn off manual toggle, clears scheduled start |
| `update_schedule/2` | `:ok \| {:error, atom}` | Set scheduled start/end DateTimes (either may be `nil`). Validates before saving |
| `clear_schedule/0` | `:ok` | Remove scheduled window |
| `cleanup_expired_schedule/0` | `boolean` | Clean up stale state if end time has passed. Returns `true` if cleanup was performed |
| `validate_schedule/2` | `:ok \| {:error, atom}` | Validate a proposed schedule without saving. Error atoms: `:empty`, `:start_in_past`, `:end_in_past`, `:end_before_start`, `:too_far_future` |
**Content:**
| Function | Returns | Description |
|----------|---------|-------------|
| `get_header/0` | `String.t()` | Maintenance page header text |
| `get_subtext/0` | `String.t()` | Maintenance page subtext |
| `update_header/1` | `{:ok, _} \| {:error, _}` | Update header text |
| `update_subtext/1` | `{:ok, _} \| {:error, _}` | Update subtext |
| `get_scheduled_start/0` | `DateTime.t() \| nil` | Scheduled start time in UTC |
| `get_scheduled_end/0` | `DateTime.t() \| nil` | Scheduled end time in UTC |
| `get_config/0` | `map` | Full configuration map with all settings + computed status |
**Module lifecycle:**
| Function | Returns | Description |
|----------|---------|-------------|
| `module_enabled?/0` | `boolean` | Is the module's settings page enabled? |
| `enabled?/0` | `boolean` | Same as `module_enabled?/0` (PhoenixKit.Module callback) |
| `enable_module/0` | `{:ok, _} \| {:error, _}` | Enable the settings page |
| `disable_module/0` | `{:ok, _} \| {:error, _}` | Disable the settings page. Also disables maintenance and clears schedule to prevent lockouts |
**PubSub:**
| Function | Returns | Description |
|----------|---------|-------------|
| `pubsub_topic/0` | `String.t()` | Returns `"phoenix_kit:maintenance"` |
| `subscribe/0` | `:ok` | Subscribe calling process to status change events |
| `broadcast_status_change/0` | `:ok` | Broadcast current status to subscribers. Message: `{:maintenance_status_changed, %{active: boolean}}` |
### PubSub
Topic: `"phoenix_kit:maintenance"`
Message: `{:maintenance_status_changed, %{active: boolean}}`
Subscribe with `Maintenance.subscribe/0`, uses `PhoenixKit.PubSub.Manager`.
## Settings Storage
All settings stored in `phoenix_kit_settings` table:
| Key | Type | Default |
|-----|------|---------|
| `maintenance_module_enabled` | boolean | `false` |
| `maintenance_enabled` | boolean | `false` |
| `maintenance_header` | string | `"Maintenance Mode"` |
| `maintenance_subtext` | string | `"We'll be back soon..."` |
| `maintenance_scheduled_start` | ISO 8601 string | `nil` |
| `maintenance_scheduled_end` | ISO 8601 string | `nil` |
## Admin Settings UI
Located at `/admin/settings/maintenance`:
- **Manual toggle** -- immediately enable/disable
- **Scheduled maintenance** -- UTC datetime pickers for start/end with current server time display
- **Content editor** -- header + subtext with live preview
- **Status banner** -- shows why maintenance is active (manual, scheduled, or both)
- **Preview link** -- navigate to `/maintenance` to see the user-facing page
## Component
`PhoenixKitWeb.Components.Core.MaintenancePage.maintenance_card/1` renders the maintenance card (emoji + header + subtext). Used by the LiveView. Also available as `maintenance_page/1` for backwards compatibility.
## Troubleshooting
### Users still see normal content after enabling
- Existing LiveView WebSocket connections are not affected until next page navigation
- Verify `Maintenance.active?()` returns true in IEx
### Admins see maintenance page
- Verify user has Admin or Owner role
- Check `Scope.admin?(scope)` or `Scope.owner?(scope)` returns true
### Scheduled maintenance not activating
- Times are stored and compared in UTC
- Check `Maintenance.get_scheduled_start()` and `Maintenance.get_scheduled_end()` return expected DateTimes
- Verify current server time with `DateTime.utc_now()`