diff --git a/ADMIN_INTERFACE.md b/ADMIN_INTERFACE.md index e8bea5d..e9f2b50 100644 --- a/ADMIN_INTERFACE.md +++ b/ADMIN_INTERFACE.md @@ -46,6 +46,17 @@ A web-based admin interface has been added to MySMSAPio for managing API keys an - **Activate/Deactivate**: Toggle gateway active status - **Recent Messages**: View messages processed by each gateway +### 5. Push Notifications (ntfy) +- **Per-admin configuration**: Each admin user sets their own ntfy topic, access token, and server URL +- **Test button**: Send a test push notification to verify connectivity +- **Real-time alerts** for: + - Gateway went offline (high priority) + - Gateway came back online + - SMS delivery confirmed (low priority) + - SMS failed (urgent priority) + - API key revoked +- See [Push Notifications (ntfy)](#push-notifications-ntfy) section below for setup details + ## Access ### Default Login Credentials diff --git a/README.md b/README.md index 2058388..4b5b3c4 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ A Ruby on Rails REST API and WebSocket server for managing SMS messaging through - **OTP Management**: Generate, send, and verify one-time passwords - **WebSocket Communication**: Real-time bidirectional communication with gateway devices - **Webhook Support**: Trigger webhooks for SMS events +- **Push Notifications (ntfy)**: Self-hosted ntfy integration for real-time alerts on gateway status, SMS delivery, and API key changes +- **Admin Interface**: Web-based dashboard for managing API keys, SMS logs, gateways, and notification settings - **Rate Limiting**: Protect API endpoints from abuse - **Load Balancing**: Automatically distribute messages across multiple gateways - **Auto Failover**: Retry failed messages and handle gateway offline scenarios @@ -23,6 +25,8 @@ A Ruby on Rails REST API and WebSocket server for managing SMS messaging through - **Background Jobs**: Sidekiq 7 - **WebSocket**: Action Cable (Redis adapter) - **API Authentication**: JWT + API Keys +- **Push Notifications**: ntfy (self-hosted, via Kamal accessory) +- **Admin UI**: Tailwind CSS v4 + Font Awesome ## Prerequisites @@ -66,6 +70,9 @@ DEFAULT_COUNTRY_CODE=US # Rails SECRET_KEY_BASE=your_secret_key_here RAILS_ENV=development + +# ntfy Push Notifications (optional — defaults to https://ntfy.sh) +# NTFY_SERVER_URL=https://ntfy.yourdomain.com ``` ### 4. Create and set up the database @@ -543,6 +550,7 @@ The system uses Sidekiq for background processing: - **ProcessInboundSmsJob**: Processes received SMS and triggers webhooks - **RetryFailedSmsJob**: Retries failed messages with exponential backoff - **TriggerWebhookJob**: Executes webhook HTTP calls +- **SendNtfyNotificationJob**: Sends push notifications via ntfy (async, retries 3×) --- @@ -575,6 +583,58 @@ If a `secret_key` is configured, webhooks include an HMAC-SHA256 signature in th --- +## Push Notifications (ntfy) + +The system integrates with [ntfy](https://ntfy.sh) for real-time push notifications to your phone or desktop. ntfy is a simple HTTP-based pub/sub notification service — no signup required. + +### How It Works + +1. A self-hosted ntfy server runs as a Docker container alongside the Rails app (configured as a Kamal accessory in `config/deploy.yml`). +2. Each admin user configures their own ntfy topic + access token in the admin UI at `/admin/notifications`. +3. When a monitored event occurs, `SendNtfyNotificationJob` fires asynchronously, POSTing a JSON payload to the ntfy server with title, message, priority, tags, and optional click URL. +4. The ntfy app on your phone (Android/iOS) or desktop receives the push notification. + +### Notification Events + +| Event | Priority | Tags | Trigger | +|---|---|---|---| +| `gateway_online` | default (3) | white_check_mark | Gateway transitions from offline → online | +| `gateway_offline` | high (4) | rotating_light | Gateway goes offline (WebSocket disconnect or stale heartbeat) | +| `sms_delivered` | low (2) | white_check_mark | Delivery report confirms SMS reached handset | +| `sms_failed` | urgent (5) | x, rotating_light | SMS fails after retries exhausted | +| `api_key_revoked` | default (3) | key, no_entry | API key revoked by an admin | + +### Setup + +1. **Install the ntfy app** on your phone ([Android](https://play.google.com/store/apps/details?id=io.heckel.ntfy) / [iOS](https://apps.apple.com/us/app/ntfy/id1625396347)) or use the [web app](https://ntfy.sh/app). +2. **Log in** to the admin interface at `/admin/login`. +3. **Navigate** to **Notifications** in the sidebar. +4. **Enter your ntfy topic** (treat it like a password — pick something hard to guess), access token (if your server has auth), and server URL (leave blank to use the default from `NTFY_SERVER_URL` env var). +5. **Click "Send Test Notification"** to verify connectivity. + +### Configuration + +- **Admin UI**: `/admin/notifications` — per-admin ntfy settings + test button +- **Environment variable**: `NTFY_SERVER_URL` — default ntfy server URL (used when an admin's `ntfy_server_url` field is blank). Defaults to `https://ntfy.sh`. +- **Kamal accessory**: ntfy runs as an accessory container in production (see `config/deploy.yml`). Requires `NTFY_AUTH_FILE` secret for access control. +- **Model**: `AdminUser` ntfy fields — `ntfy_topic`, `ntfy_token`, `ntfy_enabled`, `ntfy_server_url` + +### Architecture + +``` +Event (gateway offline, SMS failed, etc.) + ↓ +NtfyDispatchable concern → dispatch_ntfy(event, payload) + ↓ +SendNtfyNotificationJob (async, :notifications queue, retries 3×) + ↓ +Ntfy::Publisher → HTTParty.post(ntfy_server_url/topic, JSON + Bearer auth) + ↓ +ntfy server → push to phone/desktop app +``` + +--- + ## Error Responses All errors follow this format: @@ -658,8 +718,14 @@ SECRET_KEY_BASE=your_production_secret RAILS_ENV=production ALLOWED_ORIGINS=https://yourdomain.com DEFAULT_COUNTRY_CODE=US + +# ntfy Push Notifications +NTFY_SERVER_URL=https://ntfy.yourdomain.com +NTFY_AUTH_FILE=/var/lib/ntfy/user.db ``` +ntfy runs as a Kamal accessory container. It listens on port 80 (mapped to `127.0.0.1:8090` on the host) with a persistent volume at `ntfy_data:/var/lib/ntfy`. The `NTFY_AUTH_FILE` secret points to the ntfy access control database. See `config/deploy.yml` for the full accessory configuration. + --- ## Monitoring @@ -714,6 +780,13 @@ mount Sidekiq::Web => '/sidekiq' │ (Background │ │ Jobs) │ │ │ + └────────┬─────────┘ + │ + ┌────────┴─────────┐ + │ │ + │ ntfy Server │ + │ (Push Notifs) │ + │ │ └──────────────────┘ ```