my own indieAuth provider! indiko.dunkirk.sh/docs
indieauth oauth2-server
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: fix secret regen

+55 -27
+55 -27
src/client/admin-clients.ts
··· 213 213 <div class="detail-title">client secret</div> 214 214 <div class="secret-section"> 215 215 <input type="password" value="••••••••••••••••••••••••" readonly style="background: rgba(0, 0, 0, 0.3); border: 1px solid var(--old-rose); color: var(--lavender); padding: 0.5rem; font-family: monospace; width: 100%; margin-bottom: 0.5rem;" id="secret-${encodeURIComponent(clientId)}" /> 216 - <button class="btn-edit" onclick="event.stopPropagation(); regenerateSecret('${clientId}')">regenerate secret</button> 216 + <button class="btn-edit" onclick="event.stopPropagation(); regenerateSecret('${clientId}', event)">regenerate secret</button> 217 217 </div> 218 218 </div> 219 219 ` : ''} ··· 502 502 } 503 503 }); 504 504 505 - (window as any).regenerateSecret = async function(clientId: string) { 506 - if (!confirm('Are you sure you want to regenerate the client secret? This will invalidate the old secret and any apps using it will need to be updated.')) { 507 - return; 508 - } 505 + (window as any).regenerateSecret = async function(clientId: string, event?: Event) { 506 + const btn = event?.target as HTMLButtonElement | undefined; 507 + 508 + // Double-click confirmation pattern (same as delete) 509 + if (btn?.dataset.confirmState === 'pending') { 510 + // Second click - execute regenerate 511 + delete btn.dataset.confirmState; 512 + btn.disabled = true; 513 + btn.textContent = 'regenerating...'; 514 + 515 + try { 516 + const response = await fetch(`/api/admin/clients/${encodeURIComponent(clientId)}/secret`, { 517 + method: 'POST', 518 + headers: { 519 + 'Authorization': `Bearer ${token}`, 520 + }, 521 + }); 509 522 510 - try { 511 - const response = await fetch(`/api/admin/clients/${encodeURIComponent(clientId)}/secret`, { 512 - method: 'POST', 513 - headers: { 514 - 'Authorization': `Bearer ${token}`, 515 - }, 516 - }); 523 + if (!response.ok) { 524 + throw new Error('Failed to regenerate secret'); 525 + } 517 526 518 - if (!response.ok) { 519 - throw new Error('Failed to regenerate secret'); 527 + const data = await response.json(); 528 + 529 + // Show the secret in modal 530 + const secretModal = document.getElementById('secretModal') as HTMLElement; 531 + const generatedClientId = document.getElementById('generatedClientId') as HTMLElement; 532 + const generatedSecret = document.getElementById('generatedSecret') as HTMLElement; 533 + 534 + if (generatedClientId && generatedSecret && secretModal) { 535 + generatedClientId.textContent = clientId; 536 + generatedSecret.textContent = data.clientSecret; 537 + secretModal.classList.add('active'); 538 + } 539 + 540 + btn.disabled = false; 541 + btn.textContent = 'regenerate secret'; 542 + } catch (error) { 543 + console.error('Failed to regenerate secret:', error); 544 + showToast('Failed to regenerate client secret. Please try again.', 'error'); 545 + btn.disabled = false; 546 + btn.textContent = 'regenerate secret'; 520 547 } 521 - 522 - const data = await response.json(); 523 - 524 - // Show the secret in modal 525 - const secretModal = document.getElementById('secretModal') as HTMLElement; 526 - const generatedSecret = document.getElementById('generatedSecret') as HTMLElement; 527 - 528 - if (generatedSecret && secretModal) { 529 - generatedSecret.textContent = data.clientSecret; 530 - secretModal.classList.add('active'); 548 + } else { 549 + // First click - set pending state 550 + if (btn) { 551 + const originalText = btn.textContent; 552 + btn.dataset.confirmState = 'pending'; 553 + btn.textContent = 'you sure?'; 554 + 555 + // Reset after 3 seconds if not confirmed 556 + setTimeout(() => { 557 + if (btn.dataset.confirmState === 'pending') { 558 + delete btn.dataset.confirmState; 559 + btn.textContent = originalText; 560 + } 561 + }, 3000); 531 562 } 532 - } catch (error) { 533 - console.error('Failed to regenerate secret:', error); 534 - showToast('Failed to regenerate client secret. Please try again.', 'error'); 535 563 } 536 564 }; 537 565