""" Schedule 1 — 租戶檢查(每 3 分鐘) 檢查每個 active 租戶的: Traefik路由 / SSO Realm / Mailbox Domain / NC容器 / OO容器 / Quota """ import logging from datetime import datetime from sqlalchemy.orm import Session from app.models.tenant import Tenant from app.models.result import TenantScheduleResult logger = logging.getLogger(__name__) def run_tenant_check(schedule_log_id: int, db: Session): from app.services.keycloak_client import KeycloakClient from app.services.mail_client import MailClient from app.services.docker_client import DockerClient from app.services.nextcloud_client import NextcloudClient tenants = db.query(Tenant).filter(Tenant.is_active == True).all() kc = KeycloakClient() mail = MailClient() docker = DockerClient() for tenant in tenants: realm = tenant.keycloak_realm or tenant.code result = TenantScheduleResult( schedule_log_id=schedule_log_id, tenant_id=tenant.id, recorded_at=datetime.utcnow(), ) fail_reasons = [] # [1] Traefik try: result.traefik_status = docker.check_traefik_route(tenant.domain) result.traefik_done_at = datetime.utcnow() except Exception as e: result.traefik_status = False result.traefik_done_at = datetime.utcnow() fail_reasons.append(f"traefik: {e}") # [2] SSO try: exists = kc.realm_exists(realm) if not exists: kc.create_realm(realm, tenant.name) result.sso_result = True result.sso_done_at = datetime.utcnow() except Exception as e: result.sso_result = False result.sso_done_at = datetime.utcnow() fail_reasons.append(f"sso: {e}") # [3] Mailbox Domain (with DNS check for active tenants) try: if tenant.status == "active": dns_ok = mail.check_mx_dns(tenant.domain) if not dns_ok: result.mailbox_result = False result.mailbox_done_at = datetime.utcnow() fail_reasons.append("mailbox: MX record not configured") db.add(result) db.commit() continue domain_exists = mail.domain_exists(tenant.domain) if not domain_exists: mail.create_domain(tenant.domain) result.mailbox_result = True result.mailbox_done_at = datetime.utcnow() except Exception as e: result.mailbox_result = False result.mailbox_done_at = datetime.utcnow() fail_reasons.append(f"mailbox: {e}") # [4] Nextcloud container try: nc_name = f"nc-{realm}" result.nc_result = docker.ensure_container_running(nc_name, tenant.code, realm) result.nc_done_at = datetime.utcnow() except Exception as e: result.nc_result = False result.nc_done_at = datetime.utcnow() fail_reasons.append(f"nc: {e}") # [5] OnlyOffice container try: oo_name = f"oo-{realm}" result.office_result = docker.ensure_container_running(oo_name, tenant.code, realm) result.office_done_at = datetime.utcnow() except Exception as e: result.office_result = False result.office_done_at = datetime.utcnow() fail_reasons.append(f"office: {e}") # [6] Quota try: nc = NextcloudClient(tenant.domain) result.quota_usage = nc.get_total_quota_used_gb() except Exception as e: logger.warning(f"Quota check failed for {tenant.code}: {e}") if fail_reasons: result.fail_reason = "; ".join(fail_reasons) db.add(result) db.commit() logger.info(f"Tenant check done: {len(tenants)} tenants processed")