File size: 3,960 Bytes
494c89b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374416b
494c89b
 
 
374416b
494c89b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
"""
Kiro Account Manager - Main Application
FastAPI server with WebSocket support for real-time updates
"""

import os
import sys
import webbrowser
import asyncio
from pathlib import Path
from contextlib import asynccontextmanager

# Add parent to path for imports
sys.path.insert(0, str(Path(__file__).parent.parent))

from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse, JSONResponse
from fastapi.middleware.cors import CORSMiddleware
import uvicorn

from app.api import accounts, quota, autoreg, patch, system
from app.websocket import get_manager
from version import __version__, __app_name__

# WebSocket manager for real-time logs
ws_manager = get_manager()


@asynccontextmanager
async def lifespan(app: FastAPI):
    """Startup and shutdown events"""
    print("\n" + "=" * 50)
    print(f"🚀 {__app_name__} v{__version__}")
    print("=" * 50)
    yield
    print("\n👋 Shutting down...")


# Create FastAPI app
app = FastAPI(
    title=__app_name__,
    description="Manage Kiro accounts, quotas, and auto-registration",
    version=__version__,
    lifespan=lifespan
)

# CORS for development
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Include API routers
app.include_router(accounts.router, prefix="/api/accounts", tags=["Accounts"])
app.include_router(quota.router, prefix="/api/quota", tags=["Quota"])
app.include_router(autoreg.router, prefix="/api/autoreg", tags=["Auto-Registration"])
app.include_router(patch.router, prefix="/api/patch", tags=["Kiro Patch"])
app.include_router(system.router, prefix="/api/system", tags=["System"])

from app.utils import get_static_dir

# Static files
static_dir = get_static_dir()
if static_dir.exists():
    app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")


@app.get("/")
async def root():
    """Serve main UI with no-cache headers"""
    from fastapi.responses import HTMLResponse
    index_file = get_static_dir() / "index.html"
    content = index_file.read_text(encoding='utf-8')
    return HTMLResponse(
        content=content,
        headers={
            "Cache-Control": "no-cache, no-store, must-revalidate",
            "Pragma": "no-cache",
            "Expires": "0"
        }
    )


@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    """WebSocket for real-time logs and status updates"""
    from app.websocket import handle_command
    import json
    
    await ws_manager.connect(websocket)
    try:
        while True:
            # Receive and handle commands from frontend
            data = await websocket.receive_text()
            try:
                msg = json.loads(data)
                command = msg.get("command", "")
                await handle_command(command, msg, websocket)
            except json.JSONDecodeError:
                # Legacy ping support
                if data == "ping":
                    await websocket.send_text("pong")
    except WebSocketDisconnect:
        ws_manager.disconnect(websocket)


@app.get("/health")
async def health():
    """Health check endpoint"""
    return {"status": "ok", "version": __version__}


def open_browser(port: int):
    """Open browser after short delay"""
    import time
    time.sleep(1.5)
    webbrowser.open(f"http://127.0.0.1:{port}")


def run(host: str = "127.0.0.1", port: int = 8420, open_browser_flag: bool = True):
    """Run the application"""
    if open_browser_flag:
        import threading
        threading.Thread(target=open_browser, args=(port,), daemon=True).start()
    
    print(f"\n📡 Server: http://{host}:{port}")
    print("Press Ctrl+C to stop\n")
    
    uvicorn.run(
        app,
        host=host,
        port=port,
        log_level="warning",
        access_log=False
    )


if __name__ == "__main__":
    run()