File size: 9,035 Bytes
494c89b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fb7f347
 
 
 
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
"""
Auto-Registration API - Start/stop registration, SSO import
"""

from fastapi import APIRouter, HTTPException, BackgroundTasks
from pydantic import BaseModel
from typing import Optional, List
import asyncio
import subprocess
import sys
import os
from pathlib import Path

from app.websocket import get_manager

router = APIRouter()

# Track running process
_autoreg_process: Optional[subprocess.Popen] = None


async def _broadcast_accounts_update(ws):
    """Broadcast updated accounts list to all clients"""
    try:
        from services.token_service import TokenService
        token_service = TokenService()
        tokens = token_service.list_tokens()
        current = token_service.get_current_token()
        current_refresh = current.raw_data.get('refreshToken') if current else None
        
        accounts = []
        for t in tokens:
            accounts.append({
                "filename": t.path.name,
                "isActive": t.raw_data.get('refreshToken') == current_refresh if current_refresh else False,
                "isExpired": t.is_expired,
                "needsRefresh": t.needs_refresh,
                "tokenData": {
                    "accountName": t.account_name,
                    "email": t.email,
                    "expiresAt": t.expires_at.isoformat() if t.expires_at else None,
                    "refreshToken": t.raw_data.get('refreshToken') if t.raw_data else None,
                    "clientId": t.raw_data.get('_clientId') if t.raw_data else None,
                    "clientSecret": t.raw_data.get('_clientSecret') if t.raw_data else None
                },
                "usage": {
                    "currentUsage": -1,
                    "usageLimit": 500,
                    "percentageUsed": 0
                }
            })
        
        await ws.broadcast({"type": "accountsLoaded", "accounts": accounts})
    except Exception as e:
        await ws.broadcast_log(f"⚠️ Failed to refresh accounts: {e}", "warning")


class AutoRegConfig(BaseModel):
    headless: bool = False
    spoofing: bool = True
    imapServer: Optional[str] = None
    imapUser: Optional[str] = None
    imapPassword: Optional[str] = None
    emailDomain: Optional[str] = None
    emailStrategy: str = "single"


class SsoImportRequest(BaseModel):
    token: str
    region: str = "us-east-1"
    activate: bool = False


class AutoRegStatus(BaseModel):
    running: bool
    step: Optional[int] = None
    totalSteps: Optional[int] = None
    stepName: Optional[str] = None
    detail: Optional[str] = None


@router.get("/status", response_model=AutoRegStatus)
async def get_status():
    """Get auto-registration status"""
    global _autoreg_process
    
    running = _autoreg_process is not None and _autoreg_process.poll() is None
    
    return AutoRegStatus(running=running)


@router.post("/start")
async def start_autoreg(config: AutoRegConfig, background_tasks: BackgroundTasks):
    """Start auto-registration process"""
    global _autoreg_process
    
    # Check if already running
    if _autoreg_process is not None and _autoreg_process.poll() is None:
        raise HTTPException(status_code=400, detail="Auto-registration already running")
    
    # Validate IMAP settings
    if not config.imapServer or not config.imapUser or not config.imapPassword:
        raise HTTPException(status_code=400, detail="IMAP settings required")
    
    # Start in background
    background_tasks.add_task(run_autoreg, config)
    
    return {"success": True, "message": "Auto-registration started"}


async def run_autoreg(config: AutoRegConfig):
    """Run auto-registration in background"""
    global _autoreg_process
    ws = get_manager()
    
    try:
        await ws.broadcast_log("πŸš€ Starting auto-registration...", "info")
        
        # Build environment
        env = os.environ.copy()
        env.update({
            'PYTHONUNBUFFERED': '1',
            'PYTHONIOENCODING': 'utf-8',
            'IMAP_SERVER': config.imapServer or '',
            'IMAP_USER': config.imapUser or '',
            'IMAP_PASSWORD': config.imapPassword or '',
            'EMAIL_DOMAIN': config.emailDomain or '',
            'EMAIL_STRATEGY': config.emailStrategy,
            'SPOOFING_ENABLED': '1' if config.spoofing else '0'
        })
        
        # Build command
        autoreg_dir = Path(__file__).parent.parent.parent
        args = [sys.executable, '-u', '-m', 'registration.register_auto']
        if config.headless:
            args.append('--headless')
        
        await ws.broadcast_log(f"πŸ“ Working dir: {autoreg_dir}", "info")
        await ws.broadcast_log(f"βš™οΈ Strategy: {config.emailStrategy}", "info")
        await ws.broadcast_log(f"πŸ”§ Headless: {config.headless}", "info")
        
        # Start process
        _autoreg_process = subprocess.Popen(
            args,
            cwd=str(autoreg_dir),
            env=env,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
            bufsize=1
        )
        
        # Read output in real-time
        while True:
            line = _autoreg_process.stdout.readline()
            if not line and _autoreg_process.poll() is not None:
                break
            
            if line:
                line = line.strip()
                await ws.broadcast_log(line, get_log_level(line))
                
                # Parse progress
                if line.startswith('PROGRESS:'):
                    try:
                        import json
                        progress = json.loads(line[9:])
                        await ws.broadcast_progress(
                            progress.get('step', 0),
                            progress.get('totalSteps', 8),
                            progress.get('stepName', ''),
                            progress.get('detail', '')
                        )
                    except Exception:
                        pass
        
        # Check exit code
        exit_code = _autoreg_process.returncode
        if exit_code == 0:
            await ws.broadcast_log("βœ… Registration complete!", "success")
            # Refresh accounts list after successful registration
            await _broadcast_accounts_update(ws)
        else:
            stderr = _autoreg_process.stderr.read()
            if stderr:
                await ws.broadcast_log(f"❌ Error: {stderr}", "error")
            await ws.broadcast_log(f"❌ Process exited with code {exit_code}", "error")
        
    except Exception as e:
        await ws.broadcast_log(f"❌ Error: {str(e)}", "error")
    finally:
        _autoreg_process = None
        await ws.broadcast_status({"running": False})


def get_log_level(line: str) -> str:
    """Determine log level from line content"""
    if 'βœ“' in line or 'SUCCESS' in line or 'βœ…' in line or '[OK]' in line:
        return "success"
    if 'βœ—' in line or 'ERROR' in line or '❌' in line or '[X]' in line:
        return "error"
    if '⚠' in line or 'WARN' in line:
        return "warning"
    return "info"


@router.post("/stop")
async def stop_autoreg():
    """Stop auto-registration process"""
    global _autoreg_process
    
    if _autoreg_process is None or _autoreg_process.poll() is not None:
        return {"success": True, "message": "Not running"}
    
    _autoreg_process.terminate()
    try:
        _autoreg_process.wait(timeout=5)
    except subprocess.TimeoutExpired:
        _autoreg_process.kill()
    
    _autoreg_process = None
    
    ws = get_manager()
    await ws.broadcast_log("⏹ Auto-registration stopped", "warning")
    
    return {"success": True, "message": "Stopped"}


@router.post("/sso-import")
async def sso_import(request: SsoImportRequest):
    """Import account from SSO cookie"""
    ws = get_manager()
    
    try:
        await ws.broadcast_log("🌐 Starting SSO import...", "info")
        
        from services.sso_import_service import SsoImportService
        service = SsoImportService()
        
        if request.activate:
            result = service.import_and_activate(request.token, request.region)
        else:
            result = service.import_and_save(request.token, request.region)
        
        if result.success:
            await ws.broadcast_log(f"βœ… Imported: {result.email}", "success")
            # Refresh accounts list after successful import
            await _broadcast_accounts_update(ws)
            return {
                "success": True,
                "email": result.email,
                "clientId": result.client_id[:30] + "..." if result.client_id else None
            }
        else:
            await ws.broadcast_log(f"❌ Import failed: {result.error}", "error")
            raise HTTPException(status_code=400, detail=result.error)
    
    except HTTPException:
        raise
    except Exception as e:
        await ws.broadcast_log(f"❌ Error: {str(e)}", "error")
        raise HTTPException(status_code=500, detail=str(e))