Spaces:
Sleeping
Sleeping
| """ | |
| Accounts API - List, switch, delete accounts | |
| """ | |
| from fastapi import APIRouter, HTTPException | |
| from pydantic import BaseModel | |
| from typing import List, Optional | |
| from datetime import datetime | |
| from services.token_service import TokenService | |
| from services.kiro_service import KiroService | |
| router = APIRouter() | |
| token_service = TokenService() | |
| kiro_service = KiroService() | |
| class AccountResponse(BaseModel): | |
| filename: str | |
| accountName: str | |
| email: Optional[str] = None | |
| provider: str | |
| authMethod: str | |
| region: str | |
| isActive: bool | |
| isExpired: bool | |
| expiresAt: Optional[str] = None | |
| expiresIn: Optional[str] = None | |
| class AccountListResponse(BaseModel): | |
| accounts: List[AccountResponse] | |
| total: int | |
| valid: int | |
| expired: int | |
| activeAccount: Optional[str] = None | |
| async def list_accounts(): | |
| """Get all accounts""" | |
| tokens = token_service.list_tokens() | |
| current = token_service.get_current_token() | |
| current_refresh = current.raw_data.get('refreshToken') if current else None | |
| accounts = [] | |
| valid_count = 0 | |
| expired_count = 0 | |
| active_account = None | |
| for token in tokens: | |
| is_active = (current_refresh and | |
| token.raw_data.get('refreshToken') == current_refresh) | |
| if is_active: | |
| active_account = token.account_name | |
| if token.is_expired: | |
| expired_count += 1 | |
| else: | |
| valid_count += 1 | |
| # Calculate expires in | |
| expires_in = None | |
| if token.expires_at: | |
| try: | |
| # Handle timezone-aware and naive datetimes | |
| now = datetime.now(token.expires_at.tzinfo) if token.expires_at.tzinfo else datetime.now() | |
| delta = token.expires_at - now | |
| if delta.total_seconds() > 0: | |
| hours = int(delta.total_seconds() // 3600) | |
| if hours < 24: | |
| expires_in = f"{hours}h" | |
| else: | |
| expires_in = f"{hours // 24}d" | |
| else: | |
| expires_in = "expired" | |
| except Exception: | |
| expires_in = "—" | |
| accounts.append(AccountResponse( | |
| filename=token.path.name, | |
| accountName=token.account_name, | |
| email=token.raw_data.get('email') if token.raw_data else None, | |
| provider=token.provider, | |
| authMethod=token.auth_method, | |
| region=token.region, | |
| isActive=is_active, | |
| isExpired=token.is_expired, | |
| expiresAt=token.expires_at.isoformat() if token.expires_at else None, | |
| expiresIn=expires_in | |
| )) | |
| return AccountListResponse( | |
| accounts=accounts, | |
| total=len(accounts), | |
| valid=valid_count, | |
| expired=expired_count, | |
| activeAccount=active_account | |
| ) | |
| async def switch_account(filename: str): | |
| """Switch to a specific account""" | |
| token = token_service.get_token(filename) | |
| if not token: | |
| raise HTTPException(status_code=404, detail="Account not found") | |
| success = token_service.activate_token(token) | |
| if not success: | |
| raise HTTPException(status_code=500, detail="Failed to switch account") | |
| return {"success": True, "message": f"Switched to {token.account_name}"} | |
| async def delete_account(filename: str): | |
| """Delete an account""" | |
| token = token_service.get_token(filename) | |
| if not token: | |
| raise HTTPException(status_code=404, detail="Account not found") | |
| try: | |
| token_service.delete_token(filename) | |
| return {"success": True, "message": "Account deleted"} | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def refresh_account(filename: str): | |
| """Refresh account token""" | |
| token = token_service.get_token(filename) | |
| if not token: | |
| raise HTTPException(status_code=404, detail="Account not found") | |
| try: | |
| updated = token_service.refresh_and_save(token) | |
| return { | |
| "success": True, | |
| "expiresAt": updated.expires_at.isoformat() if updated.expires_at else None | |
| } | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def delete_expired(): | |
| """Delete all expired accounts""" | |
| tokens = token_service.list_tokens() | |
| deleted = 0 | |
| for token in tokens: | |
| if token.is_expired: | |
| try: | |
| token_service.delete_token(token.path.name) | |
| deleted += 1 | |
| except Exception: | |
| pass | |
| return {"success": True, "deleted": deleted} | |
| async def get_current(): | |
| """Get current active account""" | |
| token = token_service.get_current_token() | |
| if not token: | |
| return {"active": False} | |
| return { | |
| "active": True, | |
| "accountName": token.account_name, | |
| "email": token.raw_data.get('email'), | |
| "provider": token.provider, | |
| "isExpired": token.is_expired | |
| } | |