mirror of
https://github.com/System-End/hackatime.git
synced 2026-04-19 22:15:14 +00:00
andrew rotate keys (#624)
This commit is contained in:
parent
4466d8d820
commit
0250448b5a
4 changed files with 132 additions and 0 deletions
|
|
@ -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")
|
||||
|
|
|
|||
98
app/javascript/controllers/api_key_rotation_controller.js
Normal file
98
app/javascript/controllers/api_key_rotation_controller.js
Normal file
|
|
@ -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 = `
|
||||
<div id="api-key-modal" class="fixed inset-0 flex items-center justify-center z-[9999]" style="background-color: rgba(0, 0, 0, 0.5);backdrop-filter: blur(4px);">
|
||||
<div class="bg-darker rounded-lg p-6 max-w-md w-full mx-4 border border-gray-600">
|
||||
<h3 class="text-xl font-bold text-white mb-4">🔑 New API Key Generated</h3>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<p class="text-sm text-gray-300 mb-3">
|
||||
We have gone ahead and invalidated your old API key, here is your new API key. Update your editor configuration with this new key.
|
||||
</p>
|
||||
<div class="bg-gray-800 border border-gray-600 rounded p-3">
|
||||
<code id="new-api-key" class="text-sm text-white break-all">${token}</code>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-3 pt-2">
|
||||
<button type="button" id="copy-api-key"
|
||||
class="flex-1 bg-primary hover:bg-red text-white px-4 py-2 rounded-lg transition-colors">
|
||||
Copy Key
|
||||
</button>
|
||||
<button type="button" id="close-modal"
|
||||
class="flex-1 bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded-lg transition-colors">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
document.body.insertAdjacentHTML("beforeend", c)
|
||||
|
||||
document.getElementById("copy-api-key").addEventListener("click", () => {
|
||||
navigator.clipboard.writeText(token).then(() => {
|
||||
const button = document.getElementById("copy-api-key")
|
||||
const og = button.textContent
|
||||
button.textContent = "Copied!"
|
||||
setTimeout(() => {
|
||||
button.textContent = og
|
||||
}, 2000)
|
||||
})
|
||||
})
|
||||
|
||||
document.getElementById("close-modal").addEventListener("click", this.closeModal)
|
||||
|
||||
document.getElementById("api-key-modal").addEventListener("click", (event) => {
|
||||
if (event.target.id === "api-key-modal") {
|
||||
this.closeModal()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
closeModal() {
|
||||
const modal = document.getElementById("api-key-modal")
|
||||
if (modal) {
|
||||
modal.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -361,6 +361,28 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border border-primary rounded-xl p-6 bg-dark transition-all duration-200">
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<div class="p-2 bg-red-600/10 rounded">
|
||||
<span class="text-2xl">🔑</span>
|
||||
</div>
|
||||
<h2 class="text-xl font-semibold text-white" id="user_api_key">API Key</h2>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<p class="text-gray-300 text-sm">
|
||||
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.
|
||||
</p>
|
||||
|
||||
<button type="button"
|
||||
data-controller="api-key-rotation"
|
||||
data-action="click->api-key-rotation#rotateKey"
|
||||
class="w-full px-4 py-2 bg-primary hover:bg-red text-white font-medium rounded transition-colors duration-200 cursor-pointer">
|
||||
Rotate API Key
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%# This is copied from the github thingie blog, Im not good at UI so I copied :) %>
|
||||
|
||||
<div class="border border-primary rounded-xl p-6 bg-dark transition-all duration-200 md:col-span-2">
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ Rails.application.routes.draw do
|
|||
get "my/settings", to: "users#edit", as: :my_settings
|
||||
patch "my/settings", to: "users#update"
|
||||
post "my/settings/migrate_heartbeats", to: "users#migrate_heartbeats", as: :my_settings_migrate_heartbeats
|
||||
post "my/settings/rotate_api_key", to: "users#rotate_api_key", as: :my_settings_rotate_api_key
|
||||
|
||||
namespace :my do
|
||||
resources :project_repo_mappings, param: :project_name, only: [ :edit, :update ], constraints: { project_name: /.+/ }
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue