core-framework 3.1.0__tar.gz → 3.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/error-boundary.mdc +1 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/skills/sync-host-rules/SKILL.md +8 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.github/workflows/_deploy.yml +5 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.gitignore +1 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/CHANGELOG.md +32 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/PKG-INFO +2 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/config.toml +8 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/config.toml.template +8 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/bootstrap.py +4 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/comments/aggregation_service.py +7 -3
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/comments/public_service.py +10 -22
- core_framework-3.2.0/core_framework/application/email/__init__.py +29 -0
- core_framework-3.2.0/core_framework/application/email/account_deletion_scheduled.py +77 -0
- core_framework-3.2.0/core_framework/application/email/enqueue.py +36 -0
- core_framework-3.2.0/core_framework/application/email/registry.py +64 -0
- core_framework-3.2.0/core_framework/application/email/send.py +52 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/events/event_token.py +4 -7
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/attachment_service.py +14 -6
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/avatar_service.py +3 -3
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/banner_service.py +3 -3
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/moderation/user_service.py +6 -6
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/notifications/notification_service.py +40 -26
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/polling_service.py +1 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/posts/aggregation_service.py +8 -4
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/posts/public_service.py +10 -23
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/shared/enums.py +2 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/shared/exceptions.py +14 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/users/admin_service.py +8 -8
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/users/aggregation_service.py +1 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/users/authenticated_service.py +24 -9
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/users/public_service.py +13 -3
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/users/user_deletion_service.py +4 -4
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/cache.py +23 -6
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/exception_handlers/application.py +12 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/exception_handlers/common.py +2 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/middleware.py +1 -1
- core_framework-3.2.0/core_framework/core/process.py +89 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/settings.py +55 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/README.md +1 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/repository.py +15 -9
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/service.py +9 -9
- core_framework-3.2.0/core_framework/domains/email/README.md +74 -0
- core_framework-3.2.0/core_framework/domains/email/__init__.py +7 -0
- core_framework-3.2.0/core_framework/domains/email/dependencies.py +91 -0
- core_framework-3.2.0/core_framework/domains/email/exceptions.py +10 -0
- core_framework-3.2.0/core_framework/domains/email/models.py +3 -0
- core_framework-3.2.0/core_framework/domains/email/ports/send.py +29 -0
- core_framework-3.2.0/core_framework/domains/email/service.py +98 -0
- core_framework-3.2.0/core_framework/domains/media/attachment/components/variant_encoder.py +48 -0
- core_framework-3.2.0/core_framework/domains/media/attachment/ports/variant_encoder.py +42 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/service.py +23 -3
- core_framework-3.2.0/core_framework/domains/media/avatar/components/variant_encoder.py +27 -0
- core_framework-3.2.0/core_framework/domains/media/avatar/ports/variant_encoder.py +24 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/service.py +6 -0
- core_framework-3.2.0/core_framework/domains/media/banner/components/variant_encoder.py +27 -0
- core_framework-3.2.0/core_framework/domains/media/banner/ports/variant_encoder.py +24 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/service.py +6 -0
- core_framework-3.2.0/core_framework/domains/media/shared/constants.py +5 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/repository.py +10 -6
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/service.py +6 -6
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/repository.py +16 -9
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/service.py +9 -9
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/README.md +2 -2
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/__init__.py +2 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/outcomes.py +5 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/repository.py +65 -30
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/service.py +23 -5
- core_framework-3.2.0/core_framework/infrastructure/email/resend_email_adapter.py +64 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/pillow_attachment_variant_encoder_adapter.py +82 -20
- core_framework-3.2.0/core_framework/infrastructure/media/pillow_avatar_variant_encoder_adapter.py +74 -0
- core_framework-3.2.0/core_framework/infrastructure/media/pillow_banner_variant_encoder_adapter.py +64 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/main.py +15 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/__init__.py +18 -0
- core_framework-3.2.0/core_framework/worker/tasks/process_account_deletion_scheduled_email.py +17 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_attachment_asset_variants.py +10 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_banner_asset_variants.py +11 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_media_asset_variants.py +11 -0
- core_framework-3.2.0/core_framework/worker/tasks/process_transactional_email.py +21 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/app/docker-compose.yml +3 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/deploy-playbook.md +11 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/README.md +12 -11
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/cache-invalidation-design.md +10 -3
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/create-idempotency-design.md +16 -16
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/event-outbox-design.md +11 -11
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/events-ingest-design.md +1 -1
- core_framework-3.2.0/docs/design/transactional-email-design.md +616 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/auth/registration.md +1 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/cache/admin_cache_invalidation.md +3 -3
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/comments/comment_stats_aggregation.md +1 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/comments/create_comment.md +4 -4
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/events/events.md +1 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/media/attachment_upload.md +3 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/media/avatar-upload-design.md +7 -7
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/media/post-comment-attachments-design.md +35 -35
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/media/upload-pipeline-design.md +7 -7
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/media/upload_pipeline.md +4 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/mentions/mentions_in_content.md +2 -2
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/create_post.md +4 -3
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/post_stats_aggregation.md +1 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/account_deletion.md +20 -3
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/admin_users.md +1 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/check_username_exists.md +1 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/follow-system-design.md +1 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/follow.md +2 -2
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/profile.md +3 -2
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/public_profile.md +10 -8
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/user_stats_aggregation.md +1 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/library/capability-wiring.md +43 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/library/package-api.md +62 -4
- core_framework-3.2.0/docs/library/workers.md +105 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/platform/architecture-decisions.md +19 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/platform/client-error-visibility.md +4 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/platform/conventions.md +3 -2
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/platform/domain-services-and-adapters.md +11 -0
- core_framework-3.2.0/docs/todo.md +5 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/pyproject.toml +2 -1
- {core_framework-3.1.0 → core_framework-3.2.0}/tach.toml +8 -0
- core_framework-3.2.0/tests/integration/api/users/account_deletion_email_test.py +145 -0
- core_framework-3.2.0/tests/unit/application/email/account_deletion_scheduled_test.py +219 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/uv.lock +21 -1
- core_framework-3.1.0/core_framework/domains/media/attachment/components/variant_encoder.py +0 -55
- core_framework-3.1.0/core_framework/domains/media/attachment/ports/variant_encoder.py +0 -36
- core_framework-3.1.0/core_framework/domains/media/avatar/components/variant_encoder.py +0 -41
- core_framework-3.1.0/core_framework/domains/media/avatar/ports/variant_encoder.py +0 -21
- core_framework-3.1.0/core_framework/domains/media/banner/components/variant_encoder.py +0 -41
- core_framework-3.1.0/core_framework/domains/media/banner/ports/variant_encoder.py +0 -21
- core_framework-3.1.0/core_framework/domains/media/shared/constants.py +0 -4
- core_framework-3.1.0/core_framework/infrastructure/media/pillow_avatar_variant_encoder_adapter.py +0 -52
- core_framework-3.1.0/core_framework/infrastructure/media/pillow_banner_variant_encoder_adapter.py +0 -35
- core_framework-3.1.0/docs/library/workers.md +0 -89
- core_framework-3.1.0/docs/todo.md +0 -4
- {core_framework-3.1.0 → core_framework-3.2.0}/.agents/skills/fastapi +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.agents/skills/library-skills/.library-skills.json +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.agents/skills/library-skills/SKILL.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/alembic-migrations.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/api-layer.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/api-reference-docs.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/api-validation.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/application-layer.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/constants-final.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/constitution.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/database-triggers.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/docstrings.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/domain-caller-context.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/domain-imports.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/domain-input-guards.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/domain-repository-exceptions.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/domain-services-and-adapters.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/flow-documentation.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/inline-raise-messages.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/integration-test-strategy.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/layer-boundaries.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/no-code-in-docs.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/postgres-config-conventions.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/repository-schema-isolation.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/strong-reads.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/rules/structured-logging.mdc +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/skills/add-config/SKILL.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/skills/add-domain/SKILL.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/skills/code-review/SKILL.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/skills/fastapi +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/skills/implement-feature/SKILL.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/skills/recommend-features/SKILL.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.cursor/skills/write-flow-doc/SKILL.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.dockerignore +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.github/ci/package-lock.json +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.github/ci/package.json +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.github/workflows/dev-ci-cd.yaml +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.github/workflows/manual-deployment.yaml +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.github/workflows/manual-observability-deployment.yaml +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.github/workflows/publish-pypi.yml +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.github/workflows/test.yaml +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.pre-commit-config.yaml +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/.python-version +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/LICENSE +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/analytics/alembic/README +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/analytics/alembic/env.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/analytics/alembic/script.py.mako +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/analytics/alembic/versions/2d61a5e88d76_analytics_utm_events_add_kind.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/analytics/alembic/versions/585105e1149e_analytics_add_utm_events.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/analytics/alembic/versions/5b9ac98bb150_analytics_daily_fact.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/analytics/alembic/versions/7f2a9c1d4e8b_analytics_daily_fact_day_index.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/analytics/alembic.ini +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic/README +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic/env.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic/script.py.mako +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic/versions/comment_add_comment_attachments.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic/versions/comment_add_comment_create_idempotency.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic/versions/comment_add_comment_create_idempotency_created_at_index.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic/versions/comment_add_comment_mentions.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic/versions/comment_tombstone_status_model.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic/versions/v1_comment_init_baseline.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/comment/alembic.ini +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/extension/alembic/README +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/extension/alembic/env.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/extension/alembic/script.py.mako +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/extension/alembic/versions/v1_ext_init_baseline.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/extension/alembic.ini +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/media/alembic/README +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/media/alembic/env.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/media/alembic/script.py.mako +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/media/alembic/versions/media_add_attachment_staging_registry.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/media/alembic/versions/media_add_banner_staging_and_lease.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/media/alembic/versions/media_attachment_staging_registry_published_at.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/media/alembic/versions/v1_media_init_baseline.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/media/alembic.ini +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/moderation/alembic/README +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/moderation/alembic/env.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/moderation/alembic/script.py.mako +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/moderation/alembic/versions/moderation_add_event_outbox.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/moderation/alembic/versions/moderation_add_event_outbox_dead_indexes.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/moderation/alembic/versions/moderation_add_event_outbox_pending_created_at_index.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/moderation/alembic/versions/v1_mod_init_baseline.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/moderation/alembic.ini +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/notification/alembic/README +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/notification/alembic/env.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/notification/alembic/script.py.mako +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/notification/alembic/versions/2fdd68777706_notification_read_at_retention_index.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/notification/alembic/versions/v1_notif_init_baseline.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/notification/alembic.ini +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/README +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/env.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/script.py.mako +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/versions/post_add_post_attachments.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/versions/post_add_post_create_idempotency.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/versions/post_add_post_create_idempotency_created_at_index.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/versions/post_add_post_mentions.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/versions/post_post_views_retention_high_water_mark.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/versions/post_tombstone_status_model.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic/versions/v1_post_init_baseline.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/post/alembic.ini +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/README +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/env.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/script.py.mako +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/versions/44df70a45ba6_user_add_signup_events.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/versions/user_add_avatar_ingest_sequences.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/versions/user_add_banner_ingest_sequences.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/versions/user_add_date_of_birth_to_profiles.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/versions/user_add_event_outbox.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/versions/user_add_event_outbox_dead_indexes.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/versions/user_add_event_outbox_pending_created_at_index.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/versions/user_reserve_sentinel_user_ids.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic/versions/v1_user_init_baseline.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/alembic/user/alembic.ini +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/analytics/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/analytics/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/analytics/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/cache/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/cache/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/cache/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/comments/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/comments/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/comments/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/moderation/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/moderation/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/moderation/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/outbox/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/outbox/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/outbox/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/posts/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/posts/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/posts/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/users/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/users/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/admin/users/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/attachments/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/attachments/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/attachments/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/auth/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/auth/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/auth/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/authenticated/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/authenticated/mappers.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/authenticated/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/authenticated/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/public/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/public/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/public/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/comments/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/constants.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/dependencies.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/events/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/events/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/events/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/idempotency/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/idempotency/dependencies.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/notifications/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/notifications/authenticated/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/notifications/authenticated/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/notifications/authenticated/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/notifications/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/authenticated/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/authenticated/mappers.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/authenticated/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/authenticated/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/public/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/public/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/public/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/posts/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/system/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/system/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/upload.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/authenticated/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/authenticated/mappers.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/authenticated/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/authenticated/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/mappers.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/public/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/public/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/public/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/router.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/shared/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/api/users/shared/schemas.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/bucket_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/prune_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/read_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/refresh_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/registry.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/sources/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/sources/comment_created.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/sources/post_created.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/sources/post_viewed.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/analytics/sources/user_registered.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/auth/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/auth/access_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/auth/auth_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/auth/models.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/auth/token_verification.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/cache/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/cache/invalidate_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/cache/models.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/comments/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/comments/admin_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/comments/authenticated_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/comments/cleanup_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/events/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/events/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/events/event_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/events/models.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/events/utm_capture_kinds.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/attachment_read.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/ingest_guards.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/read_url_config.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/moderation/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/moderation/appeal_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/moderation/moderator_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/moderation/report_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/moderation/scheduled_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/notifications/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/notifications/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/notifications/enums.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/notifications/inbox_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/notifications/mute_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/notifications/prune_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/block_mirrors.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/events.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/exceptions.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/follow_mirrors.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/handlers.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/ops_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/processing_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/outbox/restriction_mirrors.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/posts/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/posts/admin_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/posts/authenticated_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/posts/cleanup_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/shared/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/shared/request_context.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/shared/worker_jobs.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/users/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/users/deletion_audit.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/users/enums.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/users/scheduled_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/asgi.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/bundled_alembic.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/constants.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/context.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/database.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/exception_handlers/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/exception_handlers/setup.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/exceptions.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/firebase.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/http_client.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/http_probes.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/logging.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/observability.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/pagination.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/redis.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/core/runtime.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/constants.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/dependencies.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/exceptions.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/models.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/ports/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/ports/activity_source.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/repository.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/analytics/service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/auth/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/auth/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/auth/dependencies.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/auth/exceptions.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/auth/models.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/auth/ports/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/auth/ports/auth.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/auth/service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/constants.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/dependencies.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/enums.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/exceptions.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/models.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/comment/outcomes.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/exceptions.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/components/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/components/finals_publisher.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/components/staging.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/constants.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/exceptions.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/models.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/ports/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/ports/finals_publisher.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/attachment/ports/staging.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/components/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/components/finals_publisher.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/components/staging.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/constants.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/exceptions.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/models.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/ports/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/ports/finals_publisher.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/avatar/ports/staging.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/components/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/components/finals_publisher.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/components/staging.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/constants.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/exceptions.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/models.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/ports/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/ports/finals_publisher.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/banner/ports/staging.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/dependencies.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/shared/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/shared/exceptions.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/shared/outcomes.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/shared/repository.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/shared/service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/media/shared/utils.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/dependencies.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/enums.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/exceptions.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/models.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/outbox_repository.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/outbox_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/moderation/outcomes.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/notification/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/notification/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/notification/dependencies.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/notification/enums.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/notification/exceptions.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/notification/models.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/notification/repository.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/notification/service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/dependencies.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/exceptions.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/models.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/ports/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/ports/admin.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/ports/consumer.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/ports/producer.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/outbox/service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/constants.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/dependencies.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/enums.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/exceptions.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/models.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/post/outcomes.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/repository_sql.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/constants.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/dependencies.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/enums.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/exceptions.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/models.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/outbox_repository.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/outbox_service.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/user/utils.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/domains/utils.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/firebase/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/firebase/firebase_auth_adapter.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/attachment_staging_display_geometry.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/local_attachment_staging_adapter.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/local_avatar_staging_adapter.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/local_banner_staging_adapter.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/pillow_staging_image.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/ssh_attachment_finals_publisher_adapter.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/ssh_banner_finals_publisher_adapter.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/ssh_finals_publisher_adapter.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/media/staging_paths.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/outbox/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/outbox/outbox_consumer_adapter.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/infrastructure/outbox/outbox_producer_adapter.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/main.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/migrate_cli.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/py.typed +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/arq.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/auth.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/config.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/containers.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/firebase.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/firebase_identity_toolkit.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/hookspecs.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/httpx_test_client.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/media.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/migrations.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/testing/plugin.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/analytics_refresh_cron.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_aggregate_comment_stats.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_aggregate_post_stats.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_aggregate_user_stats.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_expired_account_deletions.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_expired_mute_lifts.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_poll_outbox.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_prune_analytics_daily_facts.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_prune_post_views.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_prune_read_notifications.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_purge_expired_create_idempotency_keys.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_refresh_analytics.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_sweep_stale_attachment_staging.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_sweep_stale_avatar_staging.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/schedules/schedule_sweep_stale_banner_staging.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/delete_attachment_finals.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/delete_superseded_avatar_finals.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/delete_superseded_banner_finals.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_account_deletion.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_admin_user_deletion.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_aggregate_comment_stats.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_aggregate_post_stats.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_aggregate_user_stats.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_cache_pattern_invalidation.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_destructive_comment_delete.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_destructive_post_delete.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_mute_lift.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_outbox_row.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_refresh_analytics_bucket.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/tasks/process_user_content_deletion.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/core_framework/worker/worker_context.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/app/docker-compose.dev.yaml +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/app/dockerfile +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/.env.example +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/docker-compose.yml +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/grafana/dashboards/application/api-http.json +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/grafana/dashboards/application/worker-jobs.json +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/grafana/dashboards/infrastructure/node-exporter.json +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/grafana/dashboards/infrastructure/postgres-exporter.json +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/grafana/dashboards/infrastructure/redis-exporter.json +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/grafana/provisioning/dashboards/dashboards.yml +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/grafana/provisioning/datasources/datasources.yml +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/loki.yaml +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/otel-collector-config.yaml +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/prometheus.yml +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/targets/node/.gitkeep +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/targets/postgres/.gitkeep +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/targets/redis/.gitkeep +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/targets.example/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/targets.example/node.yml.example +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/targets.example/postgres.yml.example +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/targets.example/redis.yml.example +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/deploy/observability/tempo.yaml +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/e2e-smoke-tests-design.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/edge-rate-limits.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/edge-upload-limits.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/examples/Caddyfile.example +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/examples/Caddyfile.observability.example +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/examples/image-server-known-hosts.example +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/playbook/postgres-backup-playbook.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/playbook/py-spy-playbook.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/app-server-deploy-ssh.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/caddy-setup.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/cloudflare-setup.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/image-server-ssh.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/observability-lgtm-setup.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/postgres-backup-setup.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/postgres-setup.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/py-spy-setup.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/redis-setup.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/guides/setup/ubuntu-server-setup.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/postgres-backup-design.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/deployments/release-playbook.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/analytics-rollup-design.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/media-storage-topology.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/observability-lgtm-design.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/outbox-admin-ops-design.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/design/utm-events-design.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/analytics/admin_analytics_reads.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/auth/access_control.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/comments/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/comments/admin_comments.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/comments/comment_like.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/comments/comment_report.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/comments/delete_comment.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/comments/edit_comment.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/comments/retrieve_comments.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/deletion/comments.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/deletion/overview.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/deletion/posts.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/deletion/users.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/media/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/media/banner-upload-design.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/moderation/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/moderation/appeals.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/moderation/internal_notes.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/moderation/moderator_actions.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/moderation/reports.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/moderation/restrictions.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/notifications/notification_inbox.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/outbox/admin_outbox_ops.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/admin_posts.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/author_context.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/delete_post.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/edit_post.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/hashtag_discovery.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/post_like.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/post_report.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/post_visibility.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/posts/retrieve_posts.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/account.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/avatar.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/banner.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/blocks.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/change_history.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/my_posts_and_comments.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/preferences.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/domains/users/user_removal.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/library/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/library/api.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/library/core-framework-migration.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/library/overview.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/library/testing-plugin-design.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/platform/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/platform/database-triggers.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/platform/layers-and-boundaries.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/platform/patch-update-typed-payload.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/reviews/README.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/reviews/log.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/reviews/review-playbook.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/reviews/slices.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/docs/testing.md +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/firebase_config.example.json +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/makefile +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/conftest.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/_auth.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/_canaries.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/_media.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/_settings.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/_upload_payloads.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/conftest.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/media/pipeline_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/smoke/canary_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/smoke/media_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/smoke/system_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/e2e/smoke/tls_host_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/_http_error_details.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/analytics/_helpers.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/_auth_helpers.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/_destructive_delete_helpers.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/_http_helpers.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/_outbox_helpers.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/_user_deletion_helpers.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/_user_helpers.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/_auth_cases.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/analytics/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/analytics/router_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/cache/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/cache/router_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/comments/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/comments/router_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/moderation/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/moderation/_auth_cases.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/moderation/router_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/outbox/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/outbox/router_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/posts/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/posts/router_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/users/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/admin/users/router_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/attachments/_helpers.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/attachments/attachment_upload_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/auth/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/auth/router_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/comments/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/comments/authenticated/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/comments/authenticated/comment_writes_integration_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/comments/comment_attachments_integration_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/comments/public/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/comments/public/router_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/create_idempotency_integration_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/events/router_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/media/ingest_contract_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/mentions/mentions_integration_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/notifications/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/notifications/router_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/posts/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/posts/authenticated/post_writes_integration_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/posts/comment_count_aggregation_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/posts/followers_visibility_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/posts/post_attachments_integration_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/posts/post_deletion_integration_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/posts/post_stats_dirty_marking_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/posts/public/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/posts/public/router_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/public_read_by_id_unavailable_detail_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/system/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/system/router_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/users/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/users/authenticated/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/users/authenticated/avatar_upload_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/users/authenticated/banner_upload_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/users/authenticated/router_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/users/deletion_audit_integration_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/users/public/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/users/public/router_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/api/users/user_deletion_integration_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/_media_upload_helpers.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/account_deletion_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/aggregate_comment_stats_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/aggregate_post_stats_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/aggregate_user_stats_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/analytics_prune_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/analytics_refresh_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/attachment_upload_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/avatar_upload_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/banner_upload_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/conftest.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/create_idempotency_retention_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/moderation_outbox_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/mute_lift_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/read_notifications_retention_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/stale_staging_sweep_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/user_outbox_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/integration/worker/utils_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/api/comments/authenticated/comment_request_schemas_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/api/dedupe_preserving_order_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/api/posts/authenticated/post_update_mapper_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/api/users/profile_update_mapper_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/application/comments/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/application/events/event_service_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/application/notifications/inbox_service_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/application/shared/request_context_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/application/users/deletion_audit_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/core/bundled_alembic_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/core/migrate_cli_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/core/pagination_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/docs/_api_reference.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/docs/api_routes_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/domains/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/domains/comment/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/domains/post/models_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/domains/user/service_test.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/infrastructure/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/infrastructure/media/__init__.py +0 -0
- {core_framework-3.1.0 → core_framework-3.2.0}/tests/unit/infrastructure/media/ssh_finals_publisher_adapter_test.py +0 -0
|
@@ -37,7 +37,7 @@ Platform contract: **`docs/platform/client-error-visibility.md`**. Domain import
|
|
|
37
37
|
|
|
38
38
|
## Catalog constants
|
|
39
39
|
|
|
40
|
-
**`GENERIC_BAD_REQUEST_DETAIL`** (400), **`GENERIC_NOT_FOUND_DETAIL`** (404), **`GENERIC_FORBIDDEN_DETAIL`** (403), **`GENERIC_CONFLICT_DETAIL`** (409), ingest 413/415, **`GENERIC_INTERNAL_SERVER_ERROR_DETAIL`** (500).
|
|
40
|
+
**`GENERIC_BAD_REQUEST_DETAIL`** (400), **`GENERIC_NOT_FOUND_DETAIL`** (404), **`GENERIC_FORBIDDEN_DETAIL`** (403), **`GENERIC_CONFLICT_DETAIL`** (409), ingest 413/415, **`GENERIC_SERVICE_UNAVAILABLE_DETAIL`** (503), **`GENERIC_INTERNAL_SERVER_ERROR_DETAIL`** (500).
|
|
41
41
|
|
|
42
42
|
## Anti-enumeration (public routes)
|
|
43
43
|
|
|
@@ -31,6 +31,14 @@ Diff by **basename** against the host's **`.cursor/`** and applicable **`docs/`*
|
|
|
31
31
|
|
|
32
32
|
**Do not overwrite** host-only rules, skills, or product docs.
|
|
33
33
|
|
|
34
|
+
### Transactional email (when the bumped core ships it)
|
|
35
|
+
|
|
36
|
+
Remind the host (via upgrade notes / playbook, not code changes here):
|
|
37
|
+
|
|
38
|
+
1. Put **`register_email_content`** in the **same** shared host bootstrap helper API and worker already call — worker is load-bearing.
|
|
39
|
+
1. Register branded renderers **before** **`EMAIL_ENABLED=true`** (core default deletion copy is generic English).
|
|
40
|
+
1. Do **not** **`enqueue_transactional_email`** for **`account_deletion_scheduled`** unless the host re-implements gated checks — use the core gated task.
|
|
41
|
+
|
|
34
42
|
### `docs/deployments/guides/` layout
|
|
35
43
|
|
|
36
44
|
Core walkthroughs live under **`docs/deployments/guides/setup/`** (one-time install) and **`docs/deployments/guides/playbook/`** (ops / incident runbooks) — not a flat **`guides/`** directory.
|
|
@@ -86,6 +86,11 @@ jobs:
|
|
|
86
86
|
RETENTION_POST_VIEWS_DAYS: ${{ vars.RETENTION_POST_VIEWS_DAYS }}
|
|
87
87
|
RETENTION_READ_NOTIFICATIONS_DAYS: ${{ vars.RETENTION_READ_NOTIFICATIONS_DAYS }}
|
|
88
88
|
ANALYTICS_REFRESH_INTERVAL_SECONDS: ${{ vars.ANALYTICS_REFRESH_INTERVAL_SECONDS }}
|
|
89
|
+
EMAIL_ENABLED: ${{ vars.EMAIL_ENABLED }}
|
|
90
|
+
EMAIL_PROVIDER: ${{ vars.EMAIL_PROVIDER }}
|
|
91
|
+
EMAIL_API_KEY: ${{ secrets.EMAIL_API_KEY }}
|
|
92
|
+
EMAIL_FROM_ADDRESS: ${{ vars.EMAIL_FROM_ADDRESS }}
|
|
93
|
+
EMAIL_FROM_NAME: ${{ vars.EMAIL_FROM_NAME }}
|
|
89
94
|
run: |
|
|
90
95
|
set -euo pipefail
|
|
91
96
|
|
|
@@ -4,6 +4,36 @@ Notable changes to **core-framework** (import **`core_framework`**). Format foll
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [3.2.0] - 2026-07-20
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Transactional email (Resend):** Capability-only email domain (port + facade + Resend adapter), **`[email]`** Settings (`provider` allowlist **`resend`**; **`api_key`** / **`from_address`** required when enabled; **`from_name`** composition; values stripped; CR/LF rejected in envelope fields when enabled), content registry with host **`register_email_content`** (replace-on-duplicate; put all kinds in **one** shared host bootstrap helper called from API **and** worker — worker is load-bearing), generic **`enqueue_transactional_email`** / **`process_transactional_email`**, and gated **`process_account_deletion_scheduled_email`** after a **newly** scheduled **`PUT /users/me/account/deletion`**. Schedule/cancel HTTP stay **204**; mail is best-effort (verified Firebase email only; missing/unverified skips without failing the schedule). Default deletion copy is generic English (no `user_id` in body). Deploy: **`EMAIL_*`** vars + **`EMAIL_API_KEY`** secret. Design: **`docs/design/transactional-email-design.md`**.
|
|
12
|
+
|
|
13
|
+
### Breaking
|
|
14
|
+
|
|
15
|
+
- **Breaking (hosts — config):** Add **`job_timeout`** under **`[worker]`** (required on **`Settings`**; core **`config.toml`** / template ship **300**). Nested env override **`WORKER__JOB_TIMEOUT`**. `Settings` rejects values above **300** so the outbox visibility lease (**420s**) keeps **120s** of queue/finalization headroom. ARQ **`job_completion_wait`** is **`job_timeout` + 30s** (hooks/Redis after the timed coroutine; ARQ 0.28 applies this wait to **SIGTERM and SIGINT**, so local Ctrl+C waits the same budget). Deploy worker compose should keep **`stop_grace_period`** above that wait plus shutdown teardown (core ships **360s** for the default **300s** timeout).
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **Domain strong-read renames (hosts calling domain services/repositories):** Stats-aggregation and destructive-purge recount helpers that previously used normal-read names now exist only as **`*_strong`** accessors (replica-safe recount / write-adjacent enumeration). Update call sites:
|
|
20
|
+
- **post:** `retrieve_post_ids_by_author_id` → `retrieve_post_ids_by_author_id_strong`; `retrieve_post_like_count` → `retrieve_post_like_count_strong`; `retrieve_post_view_count_delta` → `retrieve_post_view_count_delta_strong` (matching `select_*_strong` on the repository).
|
|
21
|
+
- **comment:** `retrieve_post_comment_count` → `retrieve_post_comment_count_strong`; `retrieve_comment_like_count` → `retrieve_comment_like_count_strong`; `retrieve_comment_reply_count` → `retrieve_comment_reply_count_strong`.
|
|
22
|
+
- **user:** `retrieve_user_follow_counts` → `retrieve_user_follow_counts_strong`.
|
|
23
|
+
- **moderation:** `retrieve_post_report_count` → `retrieve_post_report_count_strong`; `retrieve_comment_report_count` → `retrieve_comment_report_count_strong`.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- **Public profile false 404 on snapshot miss:** `GET /users/{target_user_id}/profile` falls back to a strong full-detail load when the Redis/replica presentation snapshot misses but the authoritative profile still exists.
|
|
28
|
+
- **Worker media decode / shutdown timeouts:** Avatar, banner, and attachment encode/probe run in a **warm spawn worker** (one per API/worker process, serialized) with separate **30s admission** and **30s execution** deadlines, so queue wait does not consume image-processing time. Admission saturation leaves active work untouched: attachment ingest returns retryable **503** with **`Retry-After: 30`**, and media worker jobs raise explicit ARQ retry. Execution timeout terminates and replaces the affected Pillow worker. Worker settings wire ARQ **`job_timeout`** from **`[worker].job_timeout`** and **`job_completion_wait`** as **`job_timeout` + 30s**; deploy worker compose uses **`stop_grace_period: 360s`**.
|
|
29
|
+
- **Cache invalidation failure semantics:** Post-write paths (including admin mutations and media) use best-effort invalidation. Public-profile reads independently strong-read current existence and visibility, so failed invalidation may stale presentation fields but cannot expose a full card after a privacy change or keep a deleted profile visible. Admin cache tooling uses strict invalidation: exact-key requests log and continue on per-item failures; pattern-invalidation worker jobs re-raise so the failure is visible in ARQ/logs, but ordinary exceptions are terminal (no ARQ auto-retry)—operators resubmit the pattern request after Redis recovers.
|
|
30
|
+
- **Outbox timeout headroom:** The visibility lease is **420s**, with `Settings` enforcing **120s** of expected queue/finalization headroom beyond the maximum permitted **300s** global worker job timeout.
|
|
31
|
+
- **Event tokens:** Each issued token embeds a fresh ULID payload so impression dedup on `(post_id, token)` no longer collapses unrelated clients that previously shared empty-payload tokens. Docs describe collision risk as negligible rather than impossible.
|
|
32
|
+
- **Unknown mentions:** Post/comment mention references use `UserIdentity.DEFAULT` for missing users instead of echoing the stored `user_id` as `username`, which could fail response validation and 500 feed reads.
|
|
33
|
+
- **Notification failures after commit:** In-app notification attempts after a successful primary write log and swallow errors so a missed notification cannot turn the write into an HTTP 500 or skip follow-up scheduling. Flow docs for post/comment create and follow state this as best-effort (not on the outbox).
|
|
34
|
+
- **Username case sensitivity:** Availability checks and database uniqueness are **case-sensitive** (exact string match in Postgres and the taken-usernames cache). Docs that previously described case-insensitive checks were corrected.
|
|
35
|
+
- **Strong reads for write-adjacent counts:** Stats aggregation recounts and destructive user purge post-id enumeration use strong reads so replica lag cannot permanently stale counters or leave orphan comments. Flow docs link to platform/design for that mechanics; they no longer embed strong-read implementation detail.
|
|
36
|
+
|
|
7
37
|
## [3.1.0] - 2026-07-16
|
|
8
38
|
|
|
9
39
|
### Fixed
|
|
@@ -572,4 +602,5 @@ First **SemVer-stable** release per **`docs/library/package-api.md`**.
|
|
|
572
602
|
[2.3.3]: https://github.com/NepNepFFXIV/core-framework/compare/v2.3.2...v2.3.3
|
|
573
603
|
[3.0.0]: https://github.com/NepNepFFXIV/core-framework/compare/v2.3.3...v3.0.0
|
|
574
604
|
[3.1.0]: https://github.com/NepNepFFXIV/core-framework/compare/v3.0.0...v3.1.0
|
|
575
|
-
[
|
|
605
|
+
[3.2.0]: https://github.com/NepNepFFXIV/core-framework/compare/v3.1.0...v3.2.0
|
|
606
|
+
[unreleased]: https://github.com/NepNepFFXIV/core-framework/compare/v3.2.0...HEAD
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: core-framework
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: Core framework package (import as core_framework)
|
|
5
5
|
Project-URL: Homepage, https://github.com/NepNepFFXIV/core-framework
|
|
6
6
|
Project-URL: Repository, https://github.com/NepNepFFXIV/core-framework
|
|
@@ -34,6 +34,7 @@ Requires-Dist: pillow-heif>=1.4.0
|
|
|
34
34
|
Requires-Dist: pillow>=12.3.0
|
|
35
35
|
Requires-Dist: pyinstrument>=5.1.2
|
|
36
36
|
Requires-Dist: python-ulid>=3.1.0
|
|
37
|
+
Requires-Dist: resend[async]>=2.34.0
|
|
37
38
|
Requires-Dist: structlog>=26.1.0
|
|
38
39
|
Requires-Dist: tenacity>=9.1.4
|
|
39
40
|
Provides-Extra: testing
|
|
@@ -114,6 +114,7 @@ max_connections = 4
|
|
|
114
114
|
[worker]
|
|
115
115
|
max_jobs = 10
|
|
116
116
|
max_tries = 2
|
|
117
|
+
job_timeout = 300
|
|
117
118
|
|
|
118
119
|
[retention]
|
|
119
120
|
post_views_retention_days = 90
|
|
@@ -137,3 +138,10 @@ host = "127.0.0.1"
|
|
|
137
138
|
port = 6379
|
|
138
139
|
cache_db = 0
|
|
139
140
|
queue_db = 1
|
|
141
|
+
|
|
142
|
+
[email]
|
|
143
|
+
enabled = false
|
|
144
|
+
provider = "resend"
|
|
145
|
+
api_key = ""
|
|
146
|
+
from_address = ""
|
|
147
|
+
from_name = ""
|
|
@@ -98,6 +98,7 @@ max_connections = 4
|
|
|
98
98
|
[worker]
|
|
99
99
|
max_jobs = 10
|
|
100
100
|
max_tries = 2
|
|
101
|
+
job_timeout = 300
|
|
101
102
|
|
|
102
103
|
[retention]
|
|
103
104
|
post_views_retention_days = $RETENTION_POST_VIEWS_DAYS
|
|
@@ -121,3 +122,10 @@ host = "$REDIS_HOST"
|
|
|
121
122
|
port = $REDIS_PORT
|
|
122
123
|
cache_db = 0
|
|
123
124
|
queue_db = 1
|
|
125
|
+
|
|
126
|
+
[email]
|
|
127
|
+
enabled = $EMAIL_ENABLED
|
|
128
|
+
provider = "$EMAIL_PROVIDER"
|
|
129
|
+
api_key = "$EMAIL_API_KEY"
|
|
130
|
+
from_address = "$EMAIL_FROM_ADDRESS"
|
|
131
|
+
from_name = "$EMAIL_FROM_NAME"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import core_framework.domains.moderation.dependencies as moderation_deps
|
|
2
2
|
import core_framework.domains.user.dependencies as user_deps
|
|
3
3
|
from core_framework.application.analytics.registry import configure_activity_sources
|
|
4
|
+
from core_framework.application.email.registry import configure_email_content_registry
|
|
4
5
|
from core_framework.application.events.event_service import configure_event_handlers
|
|
5
6
|
from core_framework.application.events.event_token import configure_event_token
|
|
6
7
|
from core_framework.application.events.utm_capture_kinds import configure_utm_capture_kinds
|
|
@@ -13,6 +14,7 @@ from core_framework.core.runtime import CoreRuntime
|
|
|
13
14
|
from core_framework.domains.analytics.dependencies import configure_analytics_dependencies
|
|
14
15
|
from core_framework.domains.auth.dependencies import configure_auth_dependencies
|
|
15
16
|
from core_framework.domains.comment.dependencies import configure_comment_dependencies
|
|
17
|
+
from core_framework.domains.email.dependencies import configure_email_dependencies
|
|
16
18
|
from core_framework.domains.media.dependencies import configure_media_dependencies
|
|
17
19
|
from core_framework.domains.moderation.dependencies import configure_moderation_dependencies
|
|
18
20
|
from core_framework.domains.notification.dependencies import configure_notification_dependencies
|
|
@@ -35,11 +37,13 @@ def configure_application_dependencies(*, runtime: CoreRuntime) -> None:
|
|
|
35
37
|
"""Wire application-level dependencies from the shared runtime."""
|
|
36
38
|
configure_event_handlers()
|
|
37
39
|
configure_utm_capture_kinds()
|
|
40
|
+
configure_email_content_registry()
|
|
38
41
|
configure_activity_sources()
|
|
39
42
|
configure_worker_jobs(runtime=runtime)
|
|
40
43
|
configure_cashews_backend(redis_cache_url=runtime.settings.redis.redis_cache_url)
|
|
41
44
|
configure_auth_dependencies()
|
|
42
45
|
configure_comment_dependencies(runtime)
|
|
46
|
+
configure_email_dependencies(runtime)
|
|
43
47
|
configure_media_dependencies(runtime)
|
|
44
48
|
configure_moderation_dependencies(runtime)
|
|
45
49
|
configure_notification_dependencies(runtime)
|
|
@@ -9,10 +9,14 @@ from core_framework.domains.comment import CommentStatsImpact, CommentSubjectTyp
|
|
|
9
9
|
async def aggregate_comment_stats(*, comment_id: str) -> None:
|
|
10
10
|
"""Recompute and persist aggregate stats for a comment."""
|
|
11
11
|
async with TaskGroup() as tg:
|
|
12
|
-
like_task = tg.create_task(
|
|
13
|
-
|
|
12
|
+
like_task = tg.create_task(
|
|
13
|
+
comment_deps.comment_service.retrieve_comment_like_count_strong(comment_id=comment_id)
|
|
14
|
+
)
|
|
15
|
+
reply_task = tg.create_task(
|
|
16
|
+
comment_deps.comment_service.retrieve_comment_reply_count_strong(comment_id=comment_id)
|
|
17
|
+
)
|
|
14
18
|
report_task = tg.create_task(
|
|
15
|
-
moderation_deps.moderation_service.
|
|
19
|
+
moderation_deps.moderation_service.retrieve_comment_report_count_strong(comment_id=comment_id)
|
|
16
20
|
)
|
|
17
21
|
|
|
18
22
|
await comment_deps.comment_service.update_comment_stats(
|
{core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/comments/public_service.py
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from asyncio import TaskGroup
|
|
2
2
|
from collections import defaultdict
|
|
3
3
|
from datetime import datetime
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any
|
|
5
5
|
|
|
6
6
|
import core_framework.domains.comment.dependencies as comment_deps
|
|
7
7
|
import core_framework.domains.moderation.dependencies as moderation_deps
|
|
@@ -20,7 +20,6 @@ from core_framework.domains.notification import NotificationMuteSubjectType
|
|
|
20
20
|
from core_framework.domains.user import UserIdentity
|
|
21
21
|
|
|
22
22
|
_MAX_AUTHOR_COMMENT_VISIBILITY_FETCH_ROUNDS = 10
|
|
23
|
-
_UNKNOWN_MENTION_DISPLAY_NAME: Final[str] = "unknown"
|
|
24
23
|
|
|
25
24
|
|
|
26
25
|
def _mention_references(
|
|
@@ -28,26 +27,15 @@ def _mention_references(
|
|
|
28
27
|
mentioned_user_ids: list[str],
|
|
29
28
|
user_identity_mapping: defaultdict[str, UserIdentity],
|
|
30
29
|
) -> list[dict[str, Any]]:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
)
|
|
42
|
-
else:
|
|
43
|
-
mentions.append(
|
|
44
|
-
{
|
|
45
|
-
"user_id": user_id,
|
|
46
|
-
"username": user_id,
|
|
47
|
-
"display_name": _UNKNOWN_MENTION_DISPLAY_NAME,
|
|
48
|
-
}
|
|
49
|
-
)
|
|
50
|
-
return mentions
|
|
30
|
+
"""Build mention ``UserReference`` dicts; unknown ids use ``UserIdentity.DEFAULT``."""
|
|
31
|
+
return [
|
|
32
|
+
{
|
|
33
|
+
"user_id": user_id,
|
|
34
|
+
"username": user_identity_mapping[user_id].username,
|
|
35
|
+
"display_name": user_identity_mapping[user_id].display_name,
|
|
36
|
+
}
|
|
37
|
+
for user_id in mentioned_user_ids
|
|
38
|
+
]
|
|
51
39
|
|
|
52
40
|
|
|
53
41
|
def _mentioned_user_ids_for_comments(
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Transactional email content registry and generic enqueue (host surface).
|
|
2
|
+
|
|
3
|
+
Hosts put every ``register_email_content`` call in one shared bootstrap helper
|
|
4
|
+
(alongside UTM / activity sources) and invoke that helper from both API and
|
|
5
|
+
worker after ``configure_application_dependencies``. Worker registration is
|
|
6
|
+
load-bearing; API-only register still enqueues but the worker keeps core
|
|
7
|
+
defaults or hits ``missing_renderer``. Register branded renderers before enabling
|
|
8
|
+
email in production (core default deletion copy is generic English).
|
|
9
|
+
|
|
10
|
+
Enqueue host-owned kinds with ``enqueue_transactional_email``. Do not use it for
|
|
11
|
+
core gated kinds such as ``account_deletion_scheduled`` unless the host
|
|
12
|
+
re-implements those gates (prefer the gated core task).
|
|
13
|
+
|
|
14
|
+
Render+send runs only on the worker (via core tasks); it is not part of this
|
|
15
|
+
public export — do not call it from the API process. Registry reset
|
|
16
|
+
(``configure_email_content_registry``) is bootstrap-internal only.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from core_framework.application.email.enqueue import enqueue_transactional_email
|
|
20
|
+
from core_framework.application.email.registry import (
|
|
21
|
+
ACCOUNT_DELETION_SCHEDULED_KIND,
|
|
22
|
+
register_email_content,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"ACCOUNT_DELETION_SCHEDULED_KIND",
|
|
27
|
+
"enqueue_transactional_email",
|
|
28
|
+
"register_email_content",
|
|
29
|
+
]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from datetime import UTC
|
|
2
|
+
|
|
3
|
+
from structlog import get_logger
|
|
4
|
+
|
|
5
|
+
import core_framework.domains.auth.dependencies as auth_deps
|
|
6
|
+
import core_framework.domains.user.dependencies as user_deps
|
|
7
|
+
from core_framework.application.email.registry import ACCOUNT_DELETION_SCHEDULED_KIND
|
|
8
|
+
from core_framework.application.email.send import render_and_send_transactional_email
|
|
9
|
+
|
|
10
|
+
logger = get_logger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def process_account_deletion_scheduled_email(*, user_id: str) -> None:
|
|
14
|
+
"""Send ``account_deletion_scheduled`` mail when the schedule and verified email allow."""
|
|
15
|
+
scheduled_for = await user_deps.user_service.retrieve_user_deletion_scheduled_for_strong(user_id=user_id)
|
|
16
|
+
if scheduled_for is None:
|
|
17
|
+
logger.info(
|
|
18
|
+
"Skipped account deletion scheduled email",
|
|
19
|
+
reason="canceled",
|
|
20
|
+
user_id=user_id,
|
|
21
|
+
kind=ACCOUNT_DELETION_SCHEDULED_KIND,
|
|
22
|
+
)
|
|
23
|
+
return
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
firebase_user = await auth_deps.auth_service.get_user(user_id=user_id)
|
|
27
|
+
except Exception:
|
|
28
|
+
logger.exception(
|
|
29
|
+
"Account deletion scheduled email auth lookup failed",
|
|
30
|
+
reason="auth_lookup_failure",
|
|
31
|
+
user_id=user_id,
|
|
32
|
+
kind=ACCOUNT_DELETION_SCHEDULED_KIND,
|
|
33
|
+
)
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
if firebase_user is None:
|
|
37
|
+
logger.info(
|
|
38
|
+
"Skipped account deletion scheduled email",
|
|
39
|
+
reason="auth_user_missing",
|
|
40
|
+
user_id=user_id,
|
|
41
|
+
kind=ACCOUNT_DELETION_SCHEDULED_KIND,
|
|
42
|
+
)
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
email = (firebase_user.email or "").strip()
|
|
46
|
+
if not email:
|
|
47
|
+
logger.info(
|
|
48
|
+
"Skipped account deletion scheduled email",
|
|
49
|
+
reason="missing_email",
|
|
50
|
+
user_id=user_id,
|
|
51
|
+
kind=ACCOUNT_DELETION_SCHEDULED_KIND,
|
|
52
|
+
)
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
if not firebase_user.email_verified:
|
|
56
|
+
logger.info(
|
|
57
|
+
"Skipped account deletion scheduled email",
|
|
58
|
+
reason="unverified_email",
|
|
59
|
+
user_id=user_id,
|
|
60
|
+
kind=ACCOUNT_DELETION_SCHEDULED_KIND,
|
|
61
|
+
)
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
context: dict[str, object] = {
|
|
65
|
+
"user_id": user_id,
|
|
66
|
+
"deletion_date": scheduled_for.astimezone(UTC).date().isoformat(),
|
|
67
|
+
"grace_period_days": user_deps.user_service.ACCOUNT_DELETION_GRACE_DAYS,
|
|
68
|
+
}
|
|
69
|
+
identity = await user_deps.user_service.retrieve_user_identity(user_id=user_id)
|
|
70
|
+
if identity.user_id == user_id:
|
|
71
|
+
context["username"] = identity.username
|
|
72
|
+
|
|
73
|
+
await render_and_send_transactional_email(
|
|
74
|
+
to=email,
|
|
75
|
+
kind=ACCOUNT_DELETION_SCHEDULED_KIND,
|
|
76
|
+
context=context,
|
|
77
|
+
)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from structlog import get_logger
|
|
5
|
+
|
|
6
|
+
import core_framework.application.shared.worker_jobs as worker_jobs
|
|
7
|
+
from core_framework.application.shared.enums import WorkerJobName
|
|
8
|
+
|
|
9
|
+
logger = get_logger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def enqueue_transactional_email(
|
|
13
|
+
*,
|
|
14
|
+
to: str,
|
|
15
|
+
kind: str,
|
|
16
|
+
context: Mapping[str, Any],
|
|
17
|
+
_job_id: str | None = None,
|
|
18
|
+
) -> None:
|
|
19
|
+
"""Best-effort enqueue of ``process_transactional_email``."""
|
|
20
|
+
trimmed_kind = kind.strip()
|
|
21
|
+
if not trimmed_kind:
|
|
22
|
+
raise ValueError("email content kind must not be empty or whitespace-only")
|
|
23
|
+
job_kwargs: dict[str, Any] = {
|
|
24
|
+
"to": to,
|
|
25
|
+
"kind": trimmed_kind,
|
|
26
|
+
"context": dict(context),
|
|
27
|
+
}
|
|
28
|
+
if _job_id is not None:
|
|
29
|
+
job_kwargs["_job_id"] = _job_id
|
|
30
|
+
try:
|
|
31
|
+
await worker_jobs.enqueue_job(
|
|
32
|
+
job_name=WorkerJobName.PROCESS_TRANSACTIONAL_EMAIL,
|
|
33
|
+
**job_kwargs,
|
|
34
|
+
)
|
|
35
|
+
except Exception:
|
|
36
|
+
logger.exception("Failed to enqueue transactional email", kind=trimmed_kind)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
from collections.abc import Callable, Mapping
|
|
2
|
+
from html import escape
|
|
3
|
+
from typing import Any, Final
|
|
4
|
+
|
|
5
|
+
ACCOUNT_DELETION_SCHEDULED_KIND: Final[str] = "account_deletion_scheduled"
|
|
6
|
+
|
|
7
|
+
# Sync pure function: context → (subject, html_body, plain_text_body). No I/O.
|
|
8
|
+
type EmailRenderer = Callable[[Mapping[str, Any]], tuple[str, str, str]]
|
|
9
|
+
|
|
10
|
+
_renderers: dict[str, EmailRenderer] = {}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def configure_email_content_registry() -> None:
|
|
14
|
+
"""Reset the email content registry and register core default renderers."""
|
|
15
|
+
global _renderers
|
|
16
|
+
_renderers = {}
|
|
17
|
+
register_email_content(
|
|
18
|
+
ACCOUNT_DELETION_SCHEDULED_KIND,
|
|
19
|
+
render_account_deletion_scheduled,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def register_email_content(kind: str, renderer: EmailRenderer) -> None:
|
|
24
|
+
"""Register or replace a transactional email renderer for ``kind``."""
|
|
25
|
+
trimmed = kind.strip()
|
|
26
|
+
if not trimmed:
|
|
27
|
+
raise ValueError("email content kind must not be empty or whitespace-only")
|
|
28
|
+
_renderers[trimmed] = renderer
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_email_renderer(kind: str) -> EmailRenderer | None:
|
|
32
|
+
"""Return the registered renderer for ``kind``, or ``None`` if missing."""
|
|
33
|
+
return _renderers.get(kind.strip())
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def render_account_deletion_scheduled(context: Mapping[str, Any]) -> tuple[str, str, str]:
|
|
37
|
+
"""Default English copy for ``account_deletion_scheduled`` (HTML-escapes interpolations)."""
|
|
38
|
+
_ = context["user_id"] # required context key; not shown in copy
|
|
39
|
+
deletion_date = context["deletion_date"]
|
|
40
|
+
grace_period_days = context["grace_period_days"]
|
|
41
|
+
username = context.get("username")
|
|
42
|
+
|
|
43
|
+
deletion_date_str = str(deletion_date)
|
|
44
|
+
grace_str = str(grace_period_days)
|
|
45
|
+
greeting_plain = f"Hi {username}," if username is not None else "Hi,"
|
|
46
|
+
greeting_html = f"Hi {escape(str(username))}," if username is not None else "Hi,"
|
|
47
|
+
|
|
48
|
+
subject = "Your account is scheduled for deletion"
|
|
49
|
+
plain_text_body = (
|
|
50
|
+
f"{greeting_plain}\n\n"
|
|
51
|
+
f"Your account is scheduled for deletion on {deletion_date_str}. "
|
|
52
|
+
f"You have a {grace_str}-day grace period.\n\n"
|
|
53
|
+
"If you did not request this, cancel the deletion from your account settings "
|
|
54
|
+
"before that date.\n"
|
|
55
|
+
)
|
|
56
|
+
html_body = (
|
|
57
|
+
f"<p>{greeting_html}</p>"
|
|
58
|
+
f"<p>Your account is scheduled for deletion on "
|
|
59
|
+
f"<strong>{escape(deletion_date_str)}</strong>. "
|
|
60
|
+
f"You have a {escape(grace_str)}-day grace period.</p>"
|
|
61
|
+
"<p>If you did not request this, cancel the deletion from your account settings "
|
|
62
|
+
"before that date.</p>"
|
|
63
|
+
)
|
|
64
|
+
return subject, html_body, plain_text_body
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from structlog import get_logger
|
|
5
|
+
|
|
6
|
+
import core_framework.domains.email.dependencies as email_deps
|
|
7
|
+
from core_framework.application.email.registry import get_email_renderer
|
|
8
|
+
|
|
9
|
+
logger = get_logger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def render_and_send_transactional_email(
|
|
13
|
+
*,
|
|
14
|
+
to: str,
|
|
15
|
+
kind: str,
|
|
16
|
+
context: Mapping[str, Any],
|
|
17
|
+
) -> None:
|
|
18
|
+
"""Render a registered kind and send via the email facade (worker-facing)."""
|
|
19
|
+
kind = kind.strip()
|
|
20
|
+
email_service = email_deps.email_service
|
|
21
|
+
if not email_service.enabled:
|
|
22
|
+
await email_service.send(
|
|
23
|
+
to="",
|
|
24
|
+
subject="",
|
|
25
|
+
html_body="",
|
|
26
|
+
plain_text_body="",
|
|
27
|
+
kind=kind,
|
|
28
|
+
)
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
renderer = get_email_renderer(kind)
|
|
32
|
+
if renderer is None:
|
|
33
|
+
logger.error(
|
|
34
|
+
"Transactional email renderer missing",
|
|
35
|
+
reason="missing_renderer",
|
|
36
|
+
kind=kind,
|
|
37
|
+
)
|
|
38
|
+
raise RuntimeError(f"missing email renderer for kind {kind!r}")
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
subject, html_body, plain_text_body = renderer(context)
|
|
42
|
+
except Exception:
|
|
43
|
+
logger.exception("Transactional email render failed", kind=kind)
|
|
44
|
+
raise
|
|
45
|
+
|
|
46
|
+
await email_service.send(
|
|
47
|
+
to=to,
|
|
48
|
+
subject=subject,
|
|
49
|
+
html_body=html_body,
|
|
50
|
+
plain_text_body=plain_text_body,
|
|
51
|
+
kind=kind,
|
|
52
|
+
)
|
{core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/events/event_token.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from itsdangerous import BadSignature, SignatureExpired, URLSafeTimedSerializer
|
|
2
2
|
from structlog import get_logger
|
|
3
|
+
from ulid import ULID
|
|
3
4
|
|
|
4
5
|
logger = get_logger(__name__)
|
|
5
6
|
|
|
@@ -24,16 +25,12 @@ def _get_serializer() -> URLSafeTimedSerializer:
|
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
def generate_token() -> str:
|
|
27
|
-
"""Return a signed token
|
|
28
|
-
return _get_serializer().dumps(
|
|
28
|
+
"""Return a signed token with a unique ULID payload for analytics event posts."""
|
|
29
|
+
return _get_serializer().dumps(str(ULID()))
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
def verify_token(*, token: str) -> bool:
|
|
32
|
-
"""Return whether the event token is valid (signed and not expired).
|
|
33
|
-
|
|
34
|
-
The token is type-agnostic: one global max-age applies to every event type,
|
|
35
|
-
so hosts can introduce event types without a core token-policy change.
|
|
36
|
-
"""
|
|
33
|
+
"""Return whether the event token is valid (signed and not expired)."""
|
|
37
34
|
try:
|
|
38
35
|
_get_serializer().loads(token, max_age=EVENT_TOKEN_MAX_AGE_SECONDS)
|
|
39
36
|
return True
|
{core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/attachment_service.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from collections.abc import AsyncIterator, Awaitable
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
+
from math import ceil
|
|
3
4
|
from typing import Final, Protocol
|
|
4
5
|
|
|
5
6
|
from structlog import get_logger
|
|
@@ -14,7 +15,9 @@ from core_framework.application.shared.exceptions import (
|
|
|
14
15
|
BadRequestException,
|
|
15
16
|
IngestBodyTooLargeException,
|
|
16
17
|
IngestUnsupportedMediaTypeException,
|
|
18
|
+
ServiceUnavailableException,
|
|
17
19
|
)
|
|
20
|
+
from core_framework.core.process import ProcessAdmissionTimeoutError
|
|
18
21
|
from core_framework.domains.media import (
|
|
19
22
|
ATTACHMENT_STAGING_TTL_DAYS,
|
|
20
23
|
AttachmentIngestRegistration,
|
|
@@ -61,12 +64,17 @@ async def accept_attachment_ingest_upload(
|
|
|
61
64
|
raise IngestBodyTooLargeException()
|
|
62
65
|
if staging_bytes is None:
|
|
63
66
|
raise BadRequestException()
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
try:
|
|
68
|
+
(
|
|
69
|
+
probe_outcome,
|
|
70
|
+
display_dimensions,
|
|
71
|
+
) = await media_deps.attachment_service.try_probe_attachment_display_dimensions_from_staging(
|
|
72
|
+
staging_bytes=staging_bytes,
|
|
73
|
+
)
|
|
74
|
+
except ProcessAdmissionTimeoutError as exc:
|
|
75
|
+
raise ServiceUnavailableException(
|
|
76
|
+
retry_after_seconds=max(1, ceil(exc.timeout_seconds)),
|
|
77
|
+
) from exc
|
|
70
78
|
if probe_outcome == AttachmentStagingImageProbeOutcome.PROCESSING_FAILED or display_dimensions is None:
|
|
71
79
|
raise BadRequestException()
|
|
72
80
|
display_width, display_height = display_dimensions
|
{core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/avatar_service.py
RENAMED
|
@@ -13,7 +13,7 @@ from core_framework.application.shared.exceptions import (
|
|
|
13
13
|
IngestUnsupportedMediaTypeException,
|
|
14
14
|
)
|
|
15
15
|
from core_framework.core import CacheKey
|
|
16
|
-
from core_framework.core.cache import
|
|
16
|
+
from core_framework.core.cache import invalidate_cache_best_effort
|
|
17
17
|
from core_framework.domains.media import (
|
|
18
18
|
AvatarIngestRegistration,
|
|
19
19
|
IngestBodyOutcome,
|
|
@@ -142,8 +142,8 @@ async def process_media_asset_variants(*, owner_user_id: str, asset_id: str, ing
|
|
|
142
142
|
actor_id=owner_user_id,
|
|
143
143
|
)
|
|
144
144
|
if apply_result.applied:
|
|
145
|
-
await
|
|
146
|
-
await
|
|
145
|
+
await invalidate_cache_best_effort(CacheKey.USER_PUBLIC_PROFILE, user_id=owner_user_id)
|
|
146
|
+
await invalidate_cache_best_effort(CacheKey.USER_DETAIL, user_id=owner_user_id)
|
|
147
147
|
if apply_result.old_avatar_id is not None:
|
|
148
148
|
await enqueue_delete_superseded_avatar_finals(asset_id=apply_result.old_avatar_id)
|
|
149
149
|
await media_deps.avatar_service.delete_avatar_staging_blob(staging_filename=registry_entry.staging_filename)
|
{core_framework-3.1.0 → core_framework-3.2.0}/core_framework/application/media/banner_service.py
RENAMED
|
@@ -13,7 +13,7 @@ from core_framework.application.shared.exceptions import (
|
|
|
13
13
|
IngestUnsupportedMediaTypeException,
|
|
14
14
|
)
|
|
15
15
|
from core_framework.core import CacheKey
|
|
16
|
-
from core_framework.core.cache import
|
|
16
|
+
from core_framework.core.cache import invalidate_cache_best_effort
|
|
17
17
|
from core_framework.domains.media import (
|
|
18
18
|
BannerIngestRegistration,
|
|
19
19
|
IngestBodyOutcome,
|
|
@@ -138,8 +138,8 @@ async def process_banner_asset_variants(*, owner_user_id: str, asset_id: str, in
|
|
|
138
138
|
actor_id=owner_user_id,
|
|
139
139
|
)
|
|
140
140
|
if apply_result.applied:
|
|
141
|
-
await
|
|
142
|
-
await
|
|
141
|
+
await invalidate_cache_best_effort(CacheKey.USER_PUBLIC_PROFILE, user_id=owner_user_id)
|
|
142
|
+
await invalidate_cache_best_effort(CacheKey.USER_DETAIL, user_id=owner_user_id)
|
|
143
143
|
if apply_result.old_banner_id is not None:
|
|
144
144
|
await enqueue_delete_superseded_banner_finals(asset_id=apply_result.old_banner_id)
|
|
145
145
|
await media_deps.banner_service.delete_banner_staging_blob(staging_filename=registry_entry.staging_filename)
|