#!/bin/sh # Daily reclamation pass (Render cron mapsurvey-preview-media-reclaim runs this). # A shell script, not an inline command list: Render's dockerCommand does not go # through a shell, so `a && b` there becomes arguments to `a` (recorded lesson). # # Order matters loosely: preview media first (bigger objects), then orphaned # respondent uploads, then lapsed-Pro mail, then the PostHog person sync. Each # command is independently safe to rerun and exits cleanly when it has nothing # to do. set -e python manage.py reclaim_preview_media --delete python manage.py reclaim_orphan_uploads --delete # Personal API tokens expired or revoked more than 30 days ago (change auth-core). # They stop working the moment they end; the rows stay a month for the audit trail. python manage.py purge_api_tokens # OAuth client registrations (DCR/CIMD) older than 30 days that nobody ever # authorised (change oauth-server). First-party clients are never touched. python manage.py purge_oauth_clients # Paper-photo batches nobody touched for 30 days: closed, page images deleted, # rows kept (change paper-photo-entry, #495). python manage.py sweep_paper_batches --delete # Pro subscriptions whose paid period ended since the last run: one # plan_lapsed event and one email per owner (billing app). python manage.py notify_lapsed # Pro trials that end within a week: one mail per trial to the owner it belongs # to (change pro-trial-auto). Idempotent through ending_notified_at. python manage.py notify_trials_ending # Creator labels (segment, plan, domain, language) to PostHog person properties. # Last on purpose: set -e stops the script at the first failure, and a PostHog # outage or a missing key must never block reclamation or lapse mail. Labels are # also written by raw SQL during the GTM review, which is why this is a daily # pass and not a save signal (openspec: posthog-person-sync-daily, #371). python manage.py sync_posthog_person_properties