diff --git a/app/controllers/admin/notifications_controller.rb b/app/controllers/admin/notifications_controller.rb new file mode 100644 index 0000000..a76af2a --- /dev/null +++ b/app/controllers/admin/notifications_controller.rb @@ -0,0 +1,41 @@ +module Admin + class NotificationsController < BaseController + def show + @admin = current_admin + end + + def update + @admin = current_admin + if @admin.update(admin_params) + redirect_to admin_notifications_path, notice: "Notification settings saved" + else + flash.now[:alert] = "Failed to save settings" + render :show, status: :unprocessable_entity + end + end + + def test + admin = current_admin + + if admin.ntfy_configured? + SendNtfyNotificationJob.perform_now( + admin.id, + "test_notification", + title: "Test notification from MySMSAPio", + message: "If you can read this, your ntfy setup is working! Sent at #{Time.current.strftime('%H:%M:%S')}.", + priority: 3, + tags: [ "tada", "white_check_mark" ] + ) + redirect_to admin_notifications_path, notice: "Test notification sent" + else + redirect_to admin_notifications_path, alert: "Configure ntfy topic and token first" + end + end + + private + + def admin_params + params.require(:admin_user).permit(:ntfy_enabled, :ntfy_topic, :ntfy_token, :ntfy_server_url) + end + end +end diff --git a/app/views/admin/notifications/show.html.erb b/app/views/admin/notifications/show.html.erb new file mode 100644 index 0000000..5a59b32 --- /dev/null +++ b/app/views/admin/notifications/show.html.erb @@ -0,0 +1,89 @@ +
+
+
+

Notification Settings

+

Configure ntfy push notifications for your phone.

+
+
+ +
+
+

+ + ntfy Configuration +

+

+ Install the ntfy app + on your phone, subscribe to your topic, and enter the details below. +

+
+ +
+ <%= form_with model: @admin, url: admin_notifications_path, method: :patch, local: true, class: "space-y-6" do |f| %> +
+ <%= f.check_box :ntfy_enabled, class: "h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500" %> + <%= f.label :ntfy_enabled, "Enable ntfy notifications", class: "text-sm font-medium text-gray-700" %> +
+ +
+ <%= f.label :ntfy_topic, "ntfy Topic", class: "block text-sm font-medium text-gray-700" %> +

The topic name you subscribe to in the ntfy app. Treat this like a password.

+ <%= f.text_field :ntfy_topic, + class: "mt-2 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm py-2.5", + placeholder: "mysmsa-pio-your-secret-topic" %> +
+ +
+ <%= f.label :ntfy_token, "Access Token (optional)", class: "block text-sm font-medium text-gray-700" %> +

Required only if your ntfy server has access control enabled.

+ <%= f.text_field :ntfy_token, + class: "mt-2 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm py-2.5", + placeholder: "tk_..." %> +
+ +
+ <%= f.label :ntfy_server_url, "ntfy Server URL", class: "block text-sm font-medium text-gray-700" %> +

Leave blank to use the default (from NTFY_SERVER_URL env var or ntfy.sh).

+ <%= f.text_field :ntfy_server_url, + class: "mt-2 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm py-2.5", + placeholder: "https://ntfy.yourdomain.com" %> +
+ +
+ <%= f.submit "Save Settings", + class: "inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 transition-all" %> +
+ <% end %> +
+
+ +
+
+

+ + Send Test Notification +

+

Sends a test push to your configured ntfy topic right now.

+
+
+ <%= button_to test_admin_notifications_path, method: :post, + class: "inline-flex items-center gap-2 rounded-lg bg-green-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-green-500 transition-all" do %> + + Send Test Notification + <% end %> +
+
+ +
+

+ + How ntfy works +

+ +
+
diff --git a/app/views/layouts/admin.html.erb b/app/views/layouts/admin.html.erb index 8561e4b..825b5e6 100644 --- a/app/views/layouts/admin.html.erb +++ b/app/views/layouts/admin.html.erb @@ -1,5 +1,5 @@ - + @@ -12,6 +12,101 @@ <%= javascript_importmap_tags %> + + + + + @@ -54,6 +149,11 @@ API Tester <% end %> + + <%= link_to admin_notifications_path, class: "group flex gap-x-3 rounded-md p-3 text-sm leading-6 font-semibold transition-all duration-200 #{current_page?(admin_notifications_path) ? 'bg-gray-700 text-white' : 'text-gray-300 hover:text-white hover:bg-gray-700'}" do %> + + Notifications + <% end %>
  • @@ -115,9 +215,24 @@ <% else %> - -
    - <%= yield %> + + <% end %> diff --git a/config/routes.rb b/config/routes.rb index b704774..5347c9c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -66,6 +66,10 @@ Rails.application.routes.draw do end get "api_tester", to: "api_tester#index" + + resource :notifications, only: [:show, :update] do + post :test + end end # Root route diff --git a/test/integration/admin/notifications_flow_test.rb b/test/integration/admin/notifications_flow_test.rb new file mode 100644 index 0000000..22e80b2 --- /dev/null +++ b/test/integration/admin/notifications_flow_test.rb @@ -0,0 +1,49 @@ +require "test_helper" + +class AdminNotificationsFlowTest < ActionDispatch::IntegrationTest + setup do + @admin = AdminUser.create!(name: "Flow Admin", email: "flow@example.com", password: "password123") + post admin_login_path, params: { email: "flow@example.com", password: "password123" } + end + + test "can view notification settings page" do + get admin_notifications_path + assert_response :success + assert_match "Notification Settings", response.body + assert_match "ntfy Topic", response.body + end + + test "can update ntfy settings" do + patch admin_notifications_path, params: { + admin_user: { + ntfy_enabled: "1", + ntfy_topic: "flow-topic", + ntfy_token: "tk_flow", + ntfy_server_url: "https://ntfy.example.com" + } + } + + assert_redirected_to admin_notifications_path + @admin.reload + assert_equal true, @admin.ntfy_enabled + assert_equal "flow-topic", @admin.ntfy_topic + assert_equal "tk_flow", @admin.ntfy_token + end + + test "test notification redirects with alert when not configured" do + post test_admin_notifications_path + assert_redirected_to admin_notifications_path + follow_redirect! + assert_match "Configure ntfy", response.body + end + + test "test notification sends and redirects with notice when configured" do + @admin.update!(ntfy_enabled: true, ntfy_topic: "t", ntfy_token: "tk", ntfy_server_url: "https://ntfy.example.com") + stub_request(:post, "https://ntfy.example.com/t").to_return(status: 200) + + post test_admin_notifications_path + assert_redirected_to admin_notifications_path + follow_redirect! + assert_match "Test notification sent", response.body + end +end