diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 160e338..db4ba7a 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -172,6 +172,40 @@ class SessionsController < ApplicationController redirect_to my_settings_path, alert: "Failed to add email: #{e.record.errors.full_messages.join(', ')}" end + def unlink_email + unless current_user + redirect_to root_path, alert: "Please sign in first to unlink an email" + return + end + + email = params[:email].downcase + + email_record = current_user.email_addresses.find_by( + email: email + ) + + unless email_record + redirect_to my_settings_path, alert: "Email must exist to be unlinked" + return + end + + unless current_user.can_delete_email_address?(email_record) + redirect_to my_settings_path, alert: "Email must be registered for signing in to unlink" + return + end + + email_verification_request = current_user.email_verification_requests.find_by( + email: email + ) + + email_record.destroy! + email_verification_request.destroy + + redirect_to my_settings_path, notice: "Email unlinked!" + rescue ActiveRecord::RecordNotDestroyed => e + redirect_to my_settings_path, alert: "Failed to unlink email: #{e.record.errors.full_messages.join(', ')}" + end + def token verification_request = EmailVerificationRequest.valid.find_by(token: params[:token]) diff --git a/app/models/email_address.rb b/app/models/email_address.rb index ea82c15..501263d 100644 --- a/app/models/email_address.rb +++ b/app/models/email_address.rb @@ -15,6 +15,11 @@ class EmailAddress < ApplicationRecord before_validation :downcase_email + def can_unlink? + # only allow unlinking if signin email + self.source_signing_in? + end + private def downcase_email diff --git a/app/models/user.rb b/app/models/user.rb index 298e27f..33547c3 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -153,6 +153,15 @@ class User < ApplicationRecord last_audit.created_at <= 365.days.ago end + def can_delete_emails? + # don't let user delete emails if email count is <= 1 + email_addresses.size > 1 + end + + def can_delete_email_address?(email) + email.can_unlink? && can_delete_emails? + end + if Rails.env.development? def self.slow_find_by_email(email) # This is an n+1 query, but provided for developer convenience diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb index 6c7ef84..3736aa5 100644 --- a/app/views/users/edit.html.erb +++ b/app/views/users/edit.html.erb @@ -245,11 +245,21 @@ <% if @user.email_addresses.any? %>
<% @user.email_addresses.each do |email| %> -
- <%= email.email %> - - <%= email.source&.humanize || "Unknown" %> - +
+
+ <%= email.email %> + + <%= email.source&.humanize || "Unknown" %> + +
+ <% if @user.can_delete_email_address?(email) %> + <%= form_with url: unlink_email_auth_path, + method: :delete, + class: "space-y-4" do |f| %> + <%= f.hidden_field :email, value: email.email %> + <%= f.submit "Unlink!", class: "w-full px-4 py-2 bg-primary text-white font-medium rounded transition-colors duration-200 cursor-pointer" %> + <% end %> + <% end %>
<% end %>
diff --git a/config/routes.rb b/config/routes.rb index ff4e797..439221d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -89,6 +89,7 @@ Rails.application.routes.draw do delete "/auth/github/unlink", to: "sessions#github_unlink", as: :github_unlink post "/auth/email", to: "sessions#email", as: :email_auth post "/auth/email/add", to: "sessions#add_email", as: :add_email_auth + delete "/auth/email/unlink", to: "sessions#unlink_email", as: :unlink_email_auth get "/auth/token/:token", to: "sessions#token", as: :auth_token get "/auth/close_window", to: "sessions#close_window", as: :close_window delete "signout", to: "sessions#destroy", as: "signout"