# `PhoenixKit.Modules.Maintenance`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.165/lib/modules/maintenance/maintenance.ex#L1)

Maintenance Mode module for PhoenixKit.

This module provides a system-wide maintenance mode that shows a
maintenance page to all non-admin users while allowing
admins and owners to access the site normally.

Maintenance can be activated in two ways:
- **Manual toggle**: Immediately enables/disables maintenance mode
- **Scheduled window**: Set a start and end time for automatic activation

`active?/0` returns true when either the manual toggle is on OR the current
time falls within a scheduled window.

## Settings

The module uses the following settings stored in the database:
- `maintenance_module_enabled` - Boolean to enable/disable the module settings page (default: false)
- `maintenance_enabled` - Boolean to enable/disable maintenance mode manually (default: false)
- `maintenance_header` - Main heading text (default: "Maintenance Mode")
- `maintenance_subtext` - Descriptive subtext (default: "We'll be back soon")
- `maintenance_scheduled_start` - ISO 8601 UTC datetime for scheduled start (default: nil)
- `maintenance_scheduled_end` - ISO 8601 UTC datetime for scheduled end (default: nil)

## Usage

    # Check if maintenance mode is currently active (manual OR scheduled)
    if PhoenixKit.Modules.Maintenance.active?() do
      # Show maintenance page to non-admin users
    end

    # Enable/disable manually
    PhoenixKit.Modules.Maintenance.enable_system()
    PhoenixKit.Modules.Maintenance.disable_system()

    # Schedule a maintenance window
    PhoenixKit.Modules.Maintenance.update_schedule(
      ~U[2026-04-14 17:00:00Z],
      ~U[2026-04-14 18:00:00Z]
    )

    # Get module configuration
    config = PhoenixKit.Modules.Maintenance.get_config()

# `active?`

Returns true if maintenance mode is currently active.

The main function used to check maintenance status. Logic:

1. If a scheduled end time is set and has passed → **off** (auto-turn-off)
2. If the manual toggle is on → **on**
3. If a scheduled start time is set and has passed → **on** (auto-turn-on)
4. Otherwise → **off**

Schedule configurations:
- **Start only**: activates at start time, stays on until manually disabled
- **End only**: manual toggle works, but auto-disables at end time
- **Start + End**: active during the window
- **Neither**: just the manual toggle

## Examples

    iex> PhoenixKit.Modules.Maintenance.active?()
    false

# `broadcast_status_change`

Broadcasts the current maintenance status.

Sends `{:maintenance_status_changed, %{active: boolean}}` to all subscribers.

# `cleanup_expired_schedule`

Cleans up stale state when the scheduled end time has passed.

Disables the manual toggle and clears the schedule, then broadcasts
so any connected users get their layout restored.

Returns `true` if cleanup was performed, `false` if nothing needed cleaning.
Safe to call repeatedly — it's a no-op when there's nothing stale.

# `clear_schedule`

Clears the scheduled maintenance window.
Broadcasts a PubSub event.

# `disable_module`

Disables the Maintenance module (hides settings page).

Also automatically disables maintenance mode and clears any schedule
to prevent users from being locked out.

# `disable_system`

Disables maintenance mode manually.

When disabled, all users can access the site normally.
Also clears both scheduled start and end times so stale schedule values
don't re-activate maintenance or leave surprise auto-off signals for
later re-enables.
Broadcasts a PubSub event so the maintenance layout is removed.

# `enable_module`

Enables the Maintenance module (makes settings page accessible).

# `enable_system`

Enables maintenance mode manually.

When enabled, all non-admin users will see the maintenance page.
Broadcasts a PubSub event so LiveViews can react in real time.

# `enabled?`

Checks if the Maintenance module is enabled (PhoenixKit.Module callback).

# `get_config`

Gets the full configuration for the Maintenance module.

# `get_header`

Gets the header text for the maintenance page.

# `get_scheduled_end`

Returns the scheduled end time as a DateTime, or nil.

# `get_scheduled_start`

Returns the scheduled start time as a DateTime, or nil.

# `get_subtext`

Gets the subtext for the maintenance page.

# `manually_enabled?`

Returns whether the manual maintenance toggle is on.

# `module_enabled?`

Checks if the Maintenance module is enabled (settings page accessible).

# `past_scheduled_end?`

Returns true if the current time is past the scheduled end time.

When true, maintenance is forced off regardless of other settings.
Used as an auto-turn-off mechanism.

# `past_scheduled_start?`

Returns true if the current time is past the scheduled start time.

Used for start-only schedules (no end time) or as the "on" condition
in a start+end window.

# `pubsub_topic`

Returns the PubSub topic for maintenance status changes.

# `seconds_until_end`

Returns the number of seconds until maintenance ends, or nil if unknown.

Used for the Retry-After HTTP header and countdown timer.
Returns nil if maintenance is manually enabled without a scheduled end,
or if maintenance is not active.

# `subscribe`

Subscribes the calling process to maintenance status change events.

# `update_header`

Updates the header text for the maintenance page.

# `update_schedule`

Sets a scheduled maintenance window.

Either or both times can be provided:
- **Start only**: maintenance activates at start, stays on until manually disabled
- **End only**: maintenance auto-disables at end time
- **Both**: maintenance is active between start and end

Validates the schedule via `validate_schedule/2` before writing.
Times are stored as ISO 8601 UTC strings. Pass `nil` to clear a field.
Broadcasts a PubSub event on success.

Returns `:ok` on success or `{:error, atom}` on validation/DB failure.

# `update_subtext`

Updates the subtext for the maintenance page.

# `validate_schedule`

Validates a proposed maintenance schedule.

Rules:
- At least one of start or end must be provided
- Start (if set) must be in the future
- End (if set) must be in the future
- If both are set, end must be strictly after start

A small tolerance (60 seconds) is applied to "in the future" checks
to handle datetime-local inputs which only have minute precision and
minor clock drift between client and server.

Returns `:ok` or `{:error, atom}` where atom is one of:
- `:empty` — neither start nor end provided
- `:start_in_past`
- `:end_in_past`
- `:end_before_start` — end is before or equal to start
- `:too_far_future` — date is more than one year in the future

# `within_scheduled_window?`

Returns true if a scheduled maintenance window is currently active.

Handles three schedule configurations:
- **Start + End**: active between start and end times
- **Start only**: active once past start, stays on indefinitely
- **End only**: returns false (end-only acts as auto-off for the manual toggle)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
