From 0c7a7b808296feadd555c9ef28df75286af15d5a Mon Sep 17 00:00:00 2001 From: Vadym Samoilenko Date: Thu, 26 Mar 2026 14:05:14 +0000 Subject: [PATCH] feat: logout button, revoke/delete keys, setup script download - Sidebar: Add Sign Out button below user info - Keys API: split revoke (PATCH /{id}/revoke) and delete (DELETE /{id}) - Keys page: Revoke + Delete buttons per key; delete removes from DB - New key flow: after creation show download setup script step - Script embeds API key, asks for projects root folder - Downloads cc-collector.py, merges Claude Code hook into settings.json - Tests connection and reports result Co-Authored-By: Claude Sonnet 4.6 --- src/routers/keys.py | 11 +- src/static/js/app.js | 4 + src/static/js/pages/keys.js | 272 ++++++++++++++++++++++++++++-------- 3 files changed, 226 insertions(+), 61 deletions(-) diff --git a/src/routers/keys.py b/src/routers/keys.py index 4bc86fa..ba3feb5 100644 --- a/src/routers/keys.py +++ b/src/routers/keys.py @@ -32,10 +32,19 @@ async def create_key(body: ApiKeyCreate, user: CurrentUser, db: AsyncSession = D ) -@router.delete("/{key_id}", status_code=status.HTTP_204_NO_CONTENT) +@router.patch("/{key_id}/revoke", status_code=status.HTTP_204_NO_CONTENT) async def revoke_key(key_id: str, user: CurrentUser, db: AsyncSession = Depends(get_db)): key = await db.get(ApiKey, key_id) if not key or key.user_id != user.id: raise HTTPException(status_code=404, detail="Key not found") key.is_active = False await db.commit() + + +@router.delete("/{key_id}", status_code=status.HTTP_204_NO_CONTENT) +async def delete_key(key_id: str, user: CurrentUser, db: AsyncSession = Depends(get_db)): + key = await db.get(ApiKey, key_id) + if not key or key.user_id != user.id: + raise HTTPException(status_code=404, detail="Key not found") + await db.delete(key) + await db.commit() diff --git a/src/static/js/app.js b/src/static/js/app.js index 63a9338..684f082 100644 --- a/src/static/js/app.js +++ b/src/static/js/app.js @@ -97,6 +97,10 @@ const App = (() => { ${_currentUser?.username || ''} ${_currentUser?.email || ''} +
diff --git a/src/static/js/pages/keys.js b/src/static/js/pages/keys.js index 46a9205..440a3df 100644 --- a/src/static/js/pages/keys.js +++ b/src/static/js/pages/keys.js @@ -18,102 +18,153 @@ const KeysPage = (() => {
- + + + + + + + + + ${_keys.length ? _keys.map(k => ` - - + + `).join('') : ''}
LabelPrefixLast UsedStatus
LabelPrefixLast UsedStatus
${k.label} ${k.key_prefix}… ${k.last_used_at ? new Date(k.last_used_at).toLocaleString() : 'Never'}${k.is_active ? 'Active' : 'Revoked'}${k.is_active ? `` : ''}${k.is_active + ? 'Active' + : 'Revoked' + } + ${k.is_active + ? `` + : '' + } + +
No API keys yet
- -
-

Hook Setup Instructions

-

- 1. Create an API key above.
- 2. Download cc-collector.py and save to ~/.claude/cc-collector.py
- 3. Add the hook to your Claude Code settings: -

-
-
${_buildHookSnippet('')}
- -
-

- Replace YOUR_API_KEY with your key after creating it. -

- ⬇ Download cc-collector.py -