-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.py
More file actions
166 lines (134 loc) · 4.74 KB
/
Copy pathserver.py
File metadata and controls
166 lines (134 loc) · 4.74 KB
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
import asyncio
import os
import uuid
import time
import glob
from typing import Dict, List
from fastapi import FastAPI, UploadFile, File, BackgroundTasks
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
from pydantic import BaseModel
from pipeline import Pipeline
from utils import GoogleSheetHandler, load_domains
from dotenv import load_dotenv
load_dotenv()
def cleanup_temp_files():
"""Cleanup any leftover temp files from previous runs."""
for f in glob.glob("temp_*.txt"):
try:
os.remove(f)
except:
pass
cleanup_temp_files()
app = FastAPI()
# Enable CORS for React frontend
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# In-memory storage for jobs status
jobs: Dict[str, Dict] = {}
class JobStatus(BaseModel):
job_id: str
status: str
processed: int
total: int
percentage: float
results_count: int
async def run_pipeline_task(job_id: str, domains: List[str], tab_name: str):
jobs[job_id]["status"] = "processing"
jobs[job_id]["total"] = len(domains)
# Initialize Sheet Handler
sheet_handler = None
json_keyfile = os.getenv("GOOGLE_SHEETS_JSON")
sheet_id = os.getenv("GOOGLE_SHEET_ID")
if json_keyfile and sheet_id:
try:
sheet_handler = GoogleSheetHandler(json_keyfile, sheet_id)
sheet_handler.create_tab(tab_name)
except Exception as e:
print(f"Failed to initialize Google Sheets: {e}")
pipeline = Pipeline(concurrency=50, sheet_handler=sheet_handler)
async def progress_callback(processed, total):
jobs[job_id]["processed"] = processed
jobs[job_id]["results_count"] = len(pipeline.results)
pipeline.set_progress_callback(progress_callback)
try:
await pipeline.run(domains)
jobs[job_id]["status"] = "completed"
except Exception as e:
jobs[job_id]["status"] = f"error: {str(e)}"
@app.post("/api/upload")
async def upload_file(background_tasks: BackgroundTasks, file: UploadFile = File(...)):
job_id = str(uuid.uuid4())
content = await file.read()
# Save temp file
temp_path = f"temp_{job_id}.txt"
try:
with open(temp_path, "wb") as f:
f.write(content)
domains = load_domains(temp_path)
finally:
if os.path.exists(temp_path):
os.remove(temp_path)
if not domains:
return {"error": "No valid domains found in file"}
# Dynamic tab name: Filename + Timestamp
timestamp = time.strftime("%Y%m%d_%H%M%S")
clean_filename = "".join(x for x in file.filename if x.isalnum() or x in "._-")
tab_name = f"{clean_filename}_{timestamp}"[:30] # Limit length
jobs[job_id] = {
"job_id": job_id,
"status": "starting",
"processed": 0,
"total": len(domains),
"results_count": 0,
"tab_name": tab_name
}
background_tasks.add_task(run_pipeline_task, job_id, domains, tab_name)
return {"job_id": job_id, "tab_name": tab_name}
@app.get("/api/status/{job_id}")
async def get_status(job_id: str):
if job_id not in jobs:
return {"error": "Job not found"}
job = jobs[job_id]
percentage = (job["processed"] / job["total"] * 100) if job["total"] > 0 else 0
return {
"job_id": job_id,
"status": job["status"],
"processed": job["processed"],
"total": job["total"],
"percentage": round(percentage, 2),
"results_count": job["results_count"],
"tab_name": job.get("tab_name")
}
from pathlib import Path
# Paths for static files
BASE_DIR = Path(__file__).parent.resolve()
FRONTEND_DIST = BASE_DIR / "frontend" / "dist"
# Serve Static Files (Built Frontend)
if FRONTEND_DIST.exists():
app.mount("/", StaticFiles(directory=str(FRONTEND_DIST), html=True), name="static")
@app.get("/{full_path:path}")
async def serve_frontend(full_path: str):
# Fallback to index.html for SPA routing
index_path = FRONTEND_DIST / "index.html"
if index_path.exists():
return FileResponse(str(index_path))
return {"error": "Frontend build files missing. Please run the build script."}
else:
@app.get("/")
async def root():
return {"message": "WP Plugin Hunter API is running. Frontend build missing at 'frontend/dist'."}
if __name__ == "__main__":
import uvicorn
import webbrowser
from threading import Timer
def open_browser():
webbrowser.open("http://localhost:8000/")
# Open browser after 1.5 seconds to give server time to start
Timer(1.5, open_browser).start()
uvicorn.run(app, host="0.0.0.0", port=8000)