from typing import Dict, Any, List from sqlalchemy import select from app.db import SessionLocal from app.models import Task from app.workers.base_worker import BaseWorker from app.job_registry import routing_key from app.workers.utils import resolve_mx, smtp_starttls_probe, parse_cert_time from datetime import datetime, timezone class CertValidityWorker(BaseWorker): async def process(self, payload: dict, **meta) -> dict: domain = payload.get("domain") job_id = meta.get('job_id') if not domain: raise ValueError("payload.domain is required") # Hol MX aus MXValidation mx_hosts: List[str] = [] if job_id: async with SessionLocal() as session: res = await session.execute(select(Task).where(Task.job_id == job_id, Task.name == 'MXValidation')) mx_task = res.scalar_one_or_none() if mx_task and mx_task.result and 'mx_records' in mx_task.result: mx_hosts = [r['exchange'] for r in mx_task.result.get('mx_records', [])] if not mx_hosts: mx = await resolve_mx(domain) mx_hosts = [m.exchange for m in mx] results: List[Dict[str, Any]] = [] now = datetime.now(timezone.utc) for host in mx_hosts: probe = await smtp_starttls_probe(host, 25) cert = probe.get('cert') entry: Dict[str, Any] = {'host': host, 'tls_established': probe.get('tls_established', False)} if cert: nb = parse_cert_time(cert.get('notBefore')) if cert.get('notBefore') else None na = parse_cert_time(cert.get('notAfter')) if cert.get('notAfter') else None is_valid = None days_remaining = None if nb and na: # Hinweis: parse_cert_time gibt naive UTC basierend auf 'GMT'; wir machen sie timezone-aware UTC nb = nb.replace(tzinfo=timezone.utc) na = na.replace(tzinfo=timezone.utc) is_valid = (nb <= now <= na) days_remaining = int((na - now).total_seconds() // 86400) entry.update({ 'not_before': nb.isoformat() if nb else None, 'not_after': na.isoformat() if na else None, 'valid_now': is_valid, 'days_remaining': days_remaining, 'subject': cert.get('subject'), 'issuer': cert.get('issuer'), }) else: entry['error'] = probe.get('error') or ('no cert (starttls_advertised=' + str(probe.get('starttls_advertised')) + ')') results.append(entry) return { 'certs': results, 'summary': { 'total_hosts': len(results), 'valid_now': sum(1 for r in results if r.get('valid_now')), 'expiring_soon_14d': sum(1 for r in results if isinstance(r.get('days_remaining'), int) and r['days_remaining'] <= 14), } } async def main(): worker = CertValidityWorker(routing_key("MailCheck", "CertValidity"), worker_id="cert-worker-1") await worker.run() if __name__ == "__main__": import asyncio as _a _a.run(main())