Files
consentos/apps/api/src/celery_app.py
James Cottrill fbf26453f2 feat: initial public release
ConsentOS — a privacy-first cookie consent management platform.

Self-hosted, source-available alternative to OneTrust, Cookiebot, and
CookieYes. Full standards coverage (IAB TCF v2.2, GPP v1, Google
Consent Mode v2, GPC, Shopify Customer Privacy API), multi-tenant
architecture with role-based access, configuration cascade
(system → org → group → site → region), dark-pattern detection in
the scanner, and a tamper-evident consent record audit trail.

This is the initial public release. Prior development history is
retained internally.

See README.md for the feature list, architecture overview, and
quick-start instructions. Licensed under the Elastic Licence 2.0 —
self-host freely; do not resell as a managed service.
2026-04-14 09:18:18 +00:00

90 lines
3.0 KiB
Python

"""Celery application and task definitions for the CMP API.
Provides async-compatible scan scheduling via Celery with Redis as the
broker and result backend.
"""
import ssl
from celery import Celery
from celery.schedules import crontab
from src.config.settings import get_settings
settings = get_settings()
# Named `app` by Celery convention — the CLI finds it via -A src.celery_app
app = Celery(
"cmp",
broker=settings.redis_url,
backend=settings.redis_url,
)
# When using rediss:// (TLS) — e.g. Upstash — Celery requires explicit
# SSL certificate verification settings for both broker and backend.
_conf: dict = {
"task_serializer": "json",
"accept_content": ["json"],
"result_serializer": "json",
"timezone": "UTC",
"enable_utc": True,
"task_track_started": True,
"task_acks_late": True,
"worker_prefetch_multiplier": 1,
}
if settings.redis_url.startswith("rediss://"):
_conf["broker_use_ssl"] = {"ssl_cert_reqs": ssl.CERT_NONE}
_conf["redis_backend_use_ssl"] = {"ssl_cert_reqs": ssl.CERT_NONE}
app.conf.update(**_conf)
# ── Beat schedule (periodic tasks) ──────────────────────────────────
app.conf.beat_schedule = {
"check-scheduled-scans": {
"task": "src.tasks.scanner.check_scheduled_scans",
"schedule": crontab(minute="*/15"), # Every 15 minutes
},
"recover-stale-scans": {
"task": "src.tasks.scanner.recover_stale_scans",
"schedule": crontab(minute="*/5"), # Every 5 minutes
},
"purge-expired-consent-records": {
"task": "src.tasks.retention.purge_expired_consent_records",
"schedule": crontab(hour="1", minute="0"), # Daily at 01:00 UTC
},
}
# ── Explicit task imports ───────────────────────────────────────────
# Must be at the bottom to avoid circular imports. These ensure the
# worker process registers all @app.task definitions on startup.
import src.tasks.retention # noqa: E402
import src.tasks.scanner # noqa: E402, F401
# EE tasks are registered conditionally — they only exist in EE mode.
try:
import ee.api.src.tasks.compliance_scanner
import ee.api.src.tasks.compliance_scoring
import ee.api.src.tasks.retention # noqa: F401
app.conf.beat_schedule.update(
{
"check-scheduled-compliance-scans": {
"task": "src.tasks.compliance_scanner.check_scheduled_compliance_scans",
"schedule": crontab(hour="3", minute="0"),
},
"compute-daily-compliance-scores": {
"task": "src.tasks.compliance_scoring.compute_daily_scores",
"schedule": crontab(hour="4", minute="0"),
},
"run-retention-purge": {
"task": "src.tasks.retention.run_retention_purge",
"schedule": crontab(hour="2", minute="0"),
},
}
)
except ImportError:
pass