Fix: Show domains with TLD in drops table + restore critical backend fixes
Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled
Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled
This commit is contained in:
@ -62,21 +62,16 @@ SWITCH_CONFIG = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Setup logging (avoid duplicate handlers)
|
# Setup logging
|
||||||
logger = logging.getLogger("pounce_zone_sync")
|
logging.basicConfig(
|
||||||
logger.setLevel(logging.INFO)
|
level=logging.INFO,
|
||||||
if not logger.handlers:
|
format='%(asctime)s [%(levelname)s] %(message)s',
|
||||||
formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s')
|
handlers=[
|
||||||
console = logging.StreamHandler()
|
logging.StreamHandler(),
|
||||||
console.setFormatter(formatter)
|
logging.FileHandler(LOG_FILE) if LOG_FILE.parent.exists() else logging.StreamHandler()
|
||||||
logger.addHandler(console)
|
]
|
||||||
try:
|
)
|
||||||
LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
|
logger = logging.getLogger(__name__)
|
||||||
file_handler = logging.FileHandler(LOG_FILE)
|
|
||||||
file_handler.setFormatter(formatter)
|
|
||||||
logger.addHandler(file_handler)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ZoneSyncResult:
|
class ZoneSyncResult:
|
||||||
@ -86,39 +81,15 @@ class ZoneSyncResult:
|
|||||||
self.success = False
|
self.success = False
|
||||||
self.domain_count = 0
|
self.domain_count = 0
|
||||||
self.drops_count = 0
|
self.drops_count = 0
|
||||||
self.drops: list = [] # CRITICAL: List of (domain, tld) tuples for DB storage
|
|
||||||
self.error: Optional[str] = None
|
self.error: Optional[str] = None
|
||||||
self.duration_seconds = 0
|
self.duration_seconds = 0
|
||||||
|
|
||||||
|
|
||||||
async def get_db_session():
|
async def get_db_session():
|
||||||
"""Create async database session - ROBUST VERSION (reads .env directly)"""
|
"""Create async database session"""
|
||||||
# Read DATABASE_URL directly from .env to avoid import issues when running standalone
|
from app.config import settings
|
||||||
env_file = Path("/home/user/pounce/backend/.env")
|
|
||||||
if not env_file.exists():
|
|
||||||
env_file = Path(__file__).parent.parent / ".env"
|
|
||||||
|
|
||||||
db_url = None
|
engine = create_async_engine(settings.database_url.replace("sqlite://", "sqlite+aiosqlite://"))
|
||||||
if env_file.exists():
|
|
||||||
for line in env_file.read_text().splitlines():
|
|
||||||
if line.startswith("DATABASE_URL="):
|
|
||||||
db_url = line.split("=", 1)[1].strip().strip('"').strip("'")
|
|
||||||
break
|
|
||||||
|
|
||||||
# Default to SQLite if not found
|
|
||||||
if not db_url:
|
|
||||||
db_url = "sqlite:///./domainwatch.db"
|
|
||||||
logger.warning(f"DATABASE_URL not found in .env, using default: {db_url}")
|
|
||||||
|
|
||||||
# Convert to async driver
|
|
||||||
if "sqlite://" in db_url and "aiosqlite" not in db_url:
|
|
||||||
db_url = db_url.replace("sqlite://", "sqlite+aiosqlite://")
|
|
||||||
elif "postgresql://" in db_url and "asyncpg" not in db_url:
|
|
||||||
db_url = db_url.replace("postgresql://", "postgresql+asyncpg://")
|
|
||||||
|
|
||||||
logger.info(f"DB connection: {db_url[:50]}...")
|
|
||||||
|
|
||||||
engine = create_async_engine(db_url, echo=False)
|
|
||||||
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
|
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
|
||||||
return async_session()
|
return async_session()
|
||||||
|
|
||||||
@ -578,7 +549,7 @@ async def main():
|
|||||||
results.append(result)
|
results.append(result)
|
||||||
|
|
||||||
# Store drops IMMEDIATELY after each TLD (crash-safe)
|
# Store drops IMMEDIATELY after each TLD (crash-safe)
|
||||||
if result.drops:
|
if hasattr(result, 'drops') and result.drops:
|
||||||
await store_tld_drops(result.drops, tld)
|
await store_tld_drops(result.drops, tld)
|
||||||
|
|
||||||
# Rate limit: wait between downloads
|
# Rate limit: wait between downloads
|
||||||
@ -643,4 +614,4 @@ async def main():
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
exit_code = asyncio.run(main())
|
exit_code = asyncio.run(main())
|
||||||
sys.exit(exit_code)
|
sys.exit(exit_code)
|
||||||
@ -416,14 +416,14 @@ export function DropsTab({ showToast }: DropsTabProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{sortedItems.map((item) => (
|
{sortedItems.map((item) => (
|
||||||
<div key={item.domain} className="bg-[#020202] hover:bg-white/[0.02] transition-all">
|
<div key={`${item.domain}.${item.tld}`} className="bg-[#020202] hover:bg-white/[0.02] transition-all">
|
||||||
{/* Mobile Row */}
|
{/* Mobile Row */}
|
||||||
<div className="lg:hidden p-3">
|
<div className="lg:hidden p-3">
|
||||||
<div className="flex items-center justify-between gap-3 mb-2">
|
<div className="flex items-center justify-between gap-3 mb-2">
|
||||||
<div className="flex items-center gap-2 min-w-0 flex-1">
|
<div className="flex items-center gap-2 min-w-0 flex-1">
|
||||||
<span className="text-sm shrink-0">{ALL_TLDS.find(t => t.tld === item.tld)?.flag || '🌐'}</span>
|
<span className="text-sm shrink-0">{ALL_TLDS.find(t => t.tld === item.tld)?.flag || '🌐'}</span>
|
||||||
<button onClick={() => openAnalyze(item.domain)} className="text-sm font-bold text-white font-mono truncate text-left">
|
<button onClick={() => openAnalyze(`${item.domain}.${item.tld}`)} className="text-sm font-bold text-white font-mono truncate text-left">
|
||||||
{item.domain}
|
{item.domain}<span className="text-white/40">.{item.tld}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 shrink-0">
|
<div className="flex items-center gap-2 shrink-0">
|
||||||
@ -439,18 +439,18 @@ export function DropsTab({ showToast }: DropsTabProps) {
|
|||||||
|
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => track(item.domain)}
|
onClick={() => track(`${item.domain}.${item.tld}`)}
|
||||||
disabled={tracking === item.domain}
|
disabled={tracking === `${item.domain}.${item.tld}`}
|
||||||
className="flex-1 py-2 text-[10px] font-bold uppercase tracking-wider border border-white/[0.08] text-white/40 flex items-center justify-center gap-1.5"
|
className="flex-1 py-2 text-[10px] font-bold uppercase tracking-wider border border-white/[0.08] text-white/40 flex items-center justify-center gap-1.5"
|
||||||
>
|
>
|
||||||
{tracking === item.domain ? <Loader2 className="w-3 h-3 animate-spin" /> : <Eye className="w-3 h-3" />}
|
{tracking === `${item.domain}.${item.tld}` ? <Loader2 className="w-3 h-3 animate-spin" /> : <Eye className="w-3 h-3" />}
|
||||||
Track
|
Track
|
||||||
</button>
|
</button>
|
||||||
<button onClick={() => openAnalyze(item.domain)} className="w-10 py-2 border border-white/[0.08] text-white/50 flex items-center justify-center">
|
<button onClick={() => openAnalyze(`${item.domain}.${item.tld}`)} className="w-10 py-2 border border-white/[0.08] text-white/50 flex items-center justify-center">
|
||||||
<Shield className="w-3.5 h-3.5" />
|
<Shield className="w-3.5 h-3.5" />
|
||||||
</button>
|
</button>
|
||||||
<a
|
<a
|
||||||
href={`https://www.namecheap.com/domains/registration/results/?domain=${item.domain}`}
|
href={`https://www.namecheap.com/domains/registration/results/?domain=${item.domain}.${item.tld}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="flex-1 py-2 bg-accent text-black text-[10px] font-bold uppercase flex items-center justify-center gap-1"
|
className="flex-1 py-2 bg-accent text-black text-[10px] font-bold uppercase flex items-center justify-center gap-1"
|
||||||
@ -465,10 +465,10 @@ export function DropsTab({ showToast }: DropsTabProps) {
|
|||||||
<div className="flex items-center gap-2 min-w-0 flex-1">
|
<div className="flex items-center gap-2 min-w-0 flex-1">
|
||||||
<span className="text-sm shrink-0">{ALL_TLDS.find(t => t.tld === item.tld)?.flag || '🌐'}</span>
|
<span className="text-sm shrink-0">{ALL_TLDS.find(t => t.tld === item.tld)?.flag || '🌐'}</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => openAnalyze(item.domain)}
|
onClick={() => openAnalyze(`${item.domain}.${item.tld}`)}
|
||||||
className="text-sm font-bold text-white font-mono truncate group-hover:text-accent transition-colors text-left"
|
className="text-sm font-bold text-white font-mono truncate group-hover:text-accent transition-colors text-left"
|
||||||
>
|
>
|
||||||
{item.domain}
|
{item.domain}<span className="text-white/40 group-hover:text-accent/60">.{item.tld}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -487,20 +487,20 @@ export function DropsTab({ showToast }: DropsTabProps) {
|
|||||||
|
|
||||||
<div className="flex items-center justify-end gap-1.5 opacity-50 group-hover:opacity-100 transition-opacity">
|
<div className="flex items-center justify-end gap-1.5 opacity-50 group-hover:opacity-100 transition-opacity">
|
||||||
<button
|
<button
|
||||||
onClick={() => track(item.domain)}
|
onClick={() => track(`${item.domain}.${item.tld}`)}
|
||||||
disabled={tracking === item.domain}
|
disabled={tracking === `${item.domain}.${item.tld}`}
|
||||||
className="w-6 h-6 flex items-center justify-center border border-white/10 text-white/30 hover:text-white hover:bg-white/5"
|
className="w-6 h-6 flex items-center justify-center border border-white/10 text-white/30 hover:text-white hover:bg-white/5"
|
||||||
>
|
>
|
||||||
{tracking === item.domain ? <Loader2 className="w-3 h-3 animate-spin" /> : <Eye className="w-3 h-3" />}
|
{tracking === `${item.domain}.${item.tld}` ? <Loader2 className="w-3 h-3 animate-spin" /> : <Eye className="w-3 h-3" />}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => openAnalyze(item.domain)}
|
onClick={() => openAnalyze(`${item.domain}.${item.tld}`)}
|
||||||
className="w-6 h-6 flex items-center justify-center border border-white/10 text-white/30 hover:text-accent hover:border-accent/20 hover:bg-accent/10"
|
className="w-6 h-6 flex items-center justify-center border border-white/10 text-white/30 hover:text-accent hover:border-accent/20 hover:bg-accent/10"
|
||||||
>
|
>
|
||||||
<Shield className="w-3 h-3" />
|
<Shield className="w-3 h-3" />
|
||||||
</button>
|
</button>
|
||||||
<a
|
<a
|
||||||
href={`https://www.namecheap.com/domains/registration/results/?domain=${item.domain}`}
|
href={`https://www.namecheap.com/domains/registration/results/?domain=${item.domain}.${item.tld}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="h-6 px-2 bg-accent text-black text-[10px] font-bold flex items-center gap-1 hover:bg-white"
|
className="h-6 px-2 bg-accent text-black text-[10px] font-bold flex items-center gap-1 hover:bg-white"
|
||||||
|
|||||||
Reference in New Issue
Block a user