Deploy: 2025-12-19 12:34
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:
@ -735,6 +735,15 @@ def setup_scheduler():
|
||||
replace_existing=True,
|
||||
)
|
||||
|
||||
# Drops RDAP status update (every 15 minutes - check real status)
|
||||
scheduler.add_job(
|
||||
update_drops_status,
|
||||
CronTrigger(minute='*/15'), # Every 15 minutes
|
||||
id="drops_status_update",
|
||||
name="Drops Status Update (15-min)",
|
||||
replace_existing=True,
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"Scheduler configured:"
|
||||
f"\n - Scout domain check at {settings.check_hour:02d}:{settings.check_minute:02d} (daily)"
|
||||
@ -1033,6 +1042,91 @@ async def verify_drops():
|
||||
logger.exception(f"Drops verification failed: {e}")
|
||||
|
||||
|
||||
async def update_drops_status():
|
||||
"""
|
||||
Update RDAP status for dropped domains.
|
||||
|
||||
This job runs every 30 minutes to check the real status of drops
|
||||
(available, dropping_soon, taken) and store it in the database.
|
||||
This way users see the status instantly without needing to check manually.
|
||||
"""
|
||||
logger.info("Starting drops status update...")
|
||||
|
||||
try:
|
||||
from app.services.drop_status_checker import batch_check_drops
|
||||
from app.models.zone_file import DroppedDomain
|
||||
from sqlalchemy import select, update
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
async with AsyncSessionLocal() as db:
|
||||
# Get drops that haven't been status-checked in the last 30 minutes
|
||||
# Or have never been checked
|
||||
thirty_min_ago = datetime.utcnow() - timedelta(minutes=30)
|
||||
|
||||
query = (
|
||||
select(DroppedDomain)
|
||||
.where(
|
||||
(DroppedDomain.availability_status == 'unknown') |
|
||||
(DroppedDomain.last_status_check == None) |
|
||||
(DroppedDomain.last_status_check < thirty_min_ago)
|
||||
)
|
||||
.order_by(DroppedDomain.length.asc()) # Short domains first
|
||||
.limit(200) # Process 200 per run
|
||||
)
|
||||
|
||||
result = await db.execute(query)
|
||||
drops = result.scalars().all()
|
||||
|
||||
if not drops:
|
||||
logger.info("All drops have been status-checked recently")
|
||||
return
|
||||
|
||||
logger.info(f"Checking status for {len(drops)} drops...")
|
||||
|
||||
# Prepare domain list
|
||||
domains_to_check = [(d.id, f"{d.domain}.{d.tld}") for d in drops]
|
||||
|
||||
# Batch check with rate limiting
|
||||
results = await batch_check_drops(domains_to_check)
|
||||
|
||||
# Update database
|
||||
available_count = 0
|
||||
dropping_soon_count = 0
|
||||
taken_count = 0
|
||||
|
||||
for drop_id, status in results:
|
||||
await db.execute(
|
||||
update(DroppedDomain)
|
||||
.where(DroppedDomain.id == drop_id)
|
||||
.values(
|
||||
availability_status=status.status,
|
||||
rdap_status=str(status.rdap_status) if status.rdap_status else None,
|
||||
last_status_check=datetime.utcnow(),
|
||||
deletion_date=status.deletion_date,
|
||||
)
|
||||
)
|
||||
|
||||
if status.status == 'available':
|
||||
available_count += 1
|
||||
elif status.status == 'dropping_soon':
|
||||
dropping_soon_count += 1
|
||||
elif status.status == 'taken':
|
||||
taken_count += 1
|
||||
|
||||
await db.commit()
|
||||
|
||||
logger.info(
|
||||
f"Drops status update complete: "
|
||||
f"{len(results)} checked, "
|
||||
f"{available_count} available, "
|
||||
f"{dropping_soon_count} dropping soon, "
|
||||
f"{taken_count} taken"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"Drops status update failed: {e}")
|
||||
|
||||
|
||||
async def sync_zone_files():
|
||||
"""Sync zone files from Switch.ch (.ch, .li) and ICANN CZDS (gTLDs)."""
|
||||
logger.info("Starting zone file sync...")
|
||||
|
||||
@ -156,29 +156,6 @@ export function DropsTab({ showToast }: DropsTabProps) {
|
||||
loadDrops(1)
|
||||
}, [loadDrops])
|
||||
|
||||
// Auto batch-check status when drops are loaded
|
||||
const [batchChecking, setBatchChecking] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
// Check if we have unknown status drops
|
||||
const unknownCount = items.filter(i => i.availability_status === 'unknown').length
|
||||
|
||||
if (unknownCount > 0 && !batchChecking && items.length > 0) {
|
||||
setBatchChecking(true)
|
||||
|
||||
// Trigger batch check in background
|
||||
api.batchCheckDrops(50).then(() => {
|
||||
// Reload drops after a short delay to get updated status
|
||||
setTimeout(() => {
|
||||
loadDrops(page, false)
|
||||
setBatchChecking(false)
|
||||
}, 3000)
|
||||
}).catch(() => {
|
||||
setBatchChecking(false)
|
||||
})
|
||||
}
|
||||
}, [items, batchChecking, loadDrops, page])
|
||||
|
||||
const handlePageChange = useCallback((newPage: number) => {
|
||||
setPage(newPage)
|
||||
loadDrops(newPage)
|
||||
@ -187,8 +164,6 @@ export function DropsTab({ showToast }: DropsTabProps) {
|
||||
const handleRefresh = useCallback(async () => {
|
||||
await loadDrops(page, true)
|
||||
await loadStats()
|
||||
// Also trigger batch check
|
||||
api.batchCheckDrops(50).catch(() => {})
|
||||
}, [loadDrops, loadStats, page])
|
||||
|
||||
// Check real-time status of a drop
|
||||
@ -523,12 +498,6 @@ export function DropsTab({ showToast }: DropsTabProps) {
|
||||
<span>{availableCount} available now</span>
|
||||
</div>
|
||||
)}
|
||||
{batchChecking && (
|
||||
<div className="flex items-center gap-2 text-amber-400">
|
||||
<Loader2 className="w-3 h-3 animate-spin" />
|
||||
<span>Checking status...</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{totalPages > 1 && !showOnlyAvailable && (
|
||||
<span className="text-[11px] font-mono text-white/30 uppercase tracking-widest">
|
||||
@ -590,9 +559,9 @@ export function DropsTab({ showToast }: DropsTabProps) {
|
||||
// Simplified status display config
|
||||
const statusConfig = {
|
||||
available: { label: 'Available', color: 'text-accent', bg: 'bg-accent/10', border: 'border-accent/30', icon: CheckCircle2 },
|
||||
dropping_soon: { label: countdown || 'Soon', color: 'text-amber-400', bg: 'bg-amber-400/10', border: 'border-amber-400/30', icon: Clock },
|
||||
dropping_soon: { label: countdown || 'Dropping', color: 'text-amber-400', bg: 'bg-amber-400/10', border: 'border-amber-400/30', icon: Clock },
|
||||
taken: { label: 'Taken', color: 'text-rose-400', bg: 'bg-rose-400/10', border: 'border-rose-400/30', icon: Ban },
|
||||
unknown: { label: batchChecking ? '...' : 'Check', color: 'text-white/50', bg: 'bg-white/5', border: 'border-white/20', icon: batchChecking ? Loader2 : Search },
|
||||
unknown: { label: 'Pending', color: 'text-white/50', bg: 'bg-white/5', border: 'border-white/20', icon: Clock },
|
||||
}[status]
|
||||
|
||||
const StatusIcon = statusConfig.icon
|
||||
|
||||
Reference in New Issue
Block a user