diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index acccf85..0a6bb95 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -36,6 +36,17 @@ class UsersController < ApplicationController notice: "Heartbeats & api keys migration started" end + def rotate_api_key + @user.api_keys.destroy_all + + new_api_key = @user.api_keys.create!(name: "Hackatime key") + + render json: { token: new_api_key.token }, status: :ok + rescue => e + Rails.logger.error("error rotate #{e.class.name} #{e.message}") + render json: { error: "cant rotate" }, status: :unprocessable_entity + end + def wakatime_setup api_key = current_user&.api_keys&.last api_key ||= current_user.api_keys.create!(name: "Wakatime API Key") diff --git a/app/javascript/controllers/api_key_rotation_controller.js b/app/javascript/controllers/api_key_rotation_controller.js new file mode 100644 index 0000000..30c69ce --- /dev/null +++ b/app/javascript/controllers/api_key_rotation_controller.js @@ -0,0 +1,98 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + async rotateKey(event) { + event.preventDefault() + + if (!confirm("Are you sure you want to rotate your API key? Your old key will be immediately invalidated and you'll need to update it in all your applications.")) { + return + } + + const button = event.currentTarget + const og = button.textContent + button.textContent = "Rotating..." + button.disabled = true + + try { + const r = await fetch("/my/settings/rotate_api_key", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-CSRF-Token": document.querySelector("[name='csrf-token']").content + } + }) + + const d = await r.json() + + if (r.ok && d.token) { + this.m(d.token) + } else { + alert("Failed to rotate API key: " + (d.error || "Unknown error")) + } + } catch (error) { + console.error("Error rotating API key:", error) + alert("Failed to rotate API key. Please try again.") + } finally { + button.textContent = og + button.disabled = false + } + } + + m(token) { + const c = ` +
+ We have gone ahead and invalidated your old API key, here is your new API key. Update your editor configuration with this new key. +
+${token}
+ + Your API key is used to authenticate requests from your code editor. If your key has been compromised, you can rotate it to generate a new one. Rotating your API key will immediately invalidate your old key. You'll need to update the key in all of your code editors and IDEs. +
+ + +