core-framework 2.2.0__tar.gz → 2.3.1__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-2.2.0/.cursor/rules/alembic-revision-ids.mdc → core_framework-2.3.1/.cursor/rules/alembic-migrations.mdc +26 -5
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/postgres-config-conventions.mdc +9 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/repository-schema-isolation.mdc +21 -8
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/skills/add-domain/SKILL.md +1 -1
- {core_framework-2.2.0 → core_framework-2.3.1}/.github/workflows/_deploy.yml +1 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/CHANGELOG.md +32 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/PKG-INFO +1 -1
- core_framework-2.3.1/alembic/analytics/alembic/env.py +74 -0
- core_framework-2.3.1/alembic/analytics/alembic/versions/5b9ac98bb150_analytics_daily_fact.py +33 -0
- core_framework-2.3.1/alembic/analytics/alembic/versions/7f2a9c1d4e8b_analytics_daily_fact_day_index.py +27 -0
- core_framework-2.3.1/alembic/notification/alembic.ini +52 -0
- core_framework-2.3.1/alembic/user/alembic/README +1 -0
- core_framework-2.3.1/alembic/user/alembic/script.py.mako +28 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/config.toml +7 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/config.toml.template +7 -0
- core_framework-2.3.1/core_framework/api/admin/analytics/router.py +44 -0
- core_framework-2.3.1/core_framework/api/admin/analytics/schemas.py +59 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/admin/outbox/schemas.py +7 -3
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/admin/router.py +2 -0
- core_framework-2.3.1/core_framework/application/analytics/bucket_service.py +43 -0
- core_framework-2.3.1/core_framework/application/analytics/prune_service.py +6 -0
- core_framework-2.3.1/core_framework/application/analytics/read_service.py +74 -0
- core_framework-2.3.1/core_framework/application/analytics/refresh_service.py +52 -0
- core_framework-2.3.1/core_framework/application/analytics/registry.py +36 -0
- core_framework-2.3.1/core_framework/application/analytics/sources/comment_created.py +33 -0
- core_framework-2.3.1/core_framework/application/analytics/sources/post_created.py +33 -0
- core_framework-2.3.1/core_framework/application/analytics/sources/post_viewed.py +33 -0
- core_framework-2.3.1/core_framework/application/analytics/sources/user_registered.py +33 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/auth/models.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/bootstrap.py +4 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/cache/models.py +4 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/events/models.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/media/attachment_read.py +1 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/media/attachment_service.py +15 -3
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/media/read_url_config.py +4 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/notifications/enums.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/outbox/events.py +9 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/posts/public_service.py +6 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/shared/enums.py +7 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/shared/exceptions.py +20 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/users/deletion_audit.py +1 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/users/enums.py +4 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/users/user_deletion_service.py +21 -2
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/bundled_alembic.py +1 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/core/database.py +5 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/core/settings.py +41 -13
- core_framework-2.3.1/core_framework/domains/analytics/README.md +26 -0
- core_framework-2.3.1/core_framework/domains/analytics/__init__.py +14 -0
- core_framework-2.3.1/core_framework/domains/analytics/constants.py +3 -0
- core_framework-2.3.1/core_framework/domains/analytics/dependencies.py +64 -0
- core_framework-2.3.1/core_framework/domains/analytics/exceptions.py +5 -0
- core_framework-2.3.1/core_framework/domains/analytics/models.py +23 -0
- core_framework-2.3.1/core_framework/domains/analytics/ports/activity_source.py +39 -0
- core_framework-2.3.1/core_framework/domains/analytics/repository.py +231 -0
- core_framework-2.3.1/core_framework/domains/analytics/service.py +236 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/auth/ports/auth.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/auth/service.py +4 -4
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/comment/dependencies.py +6 -1
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/comment/repository.py +272 -206
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/comment/service.py +36 -1
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/attachment/components/finals_publisher.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/attachment/components/staging.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/attachment/components/variant_encoder.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/attachment/ports/finals_publisher.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/attachment/ports/staging.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/attachment/ports/variant_encoder.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/attachment/service.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/avatar/components/finals_publisher.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/avatar/components/staging.py +3 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/avatar/components/variant_encoder.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/avatar/ports/finals_publisher.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/avatar/ports/staging.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/avatar/ports/variant_encoder.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/avatar/service.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/banner/components/finals_publisher.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/banner/components/staging.py +3 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/banner/components/variant_encoder.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/banner/ports/finals_publisher.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/banner/ports/staging.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/banner/ports/variant_encoder.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/banner/service.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/dependencies.py +6 -1
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/shared/repository.py +63 -52
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/shared/service.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/moderation/dependencies.py +14 -6
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/moderation/outbox_repository.py +19 -11
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/moderation/repository.py +104 -102
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/moderation/service.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/notification/dependencies.py +6 -1
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/notification/repository.py +85 -74
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/notification/service.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/outbox/ports/consumer.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/outbox/ports/producer.py +4 -4
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/outbox/service.py +3 -3
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/post/README.md +3 -3
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/post/dependencies.py +6 -1
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/post/repository.py +357 -279
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/post/service.py +73 -4
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/user/dependencies.py +14 -6
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/user/outbox_repository.py +19 -11
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/user/repository.py +177 -124
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/user/service.py +36 -1
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/utils.py +15 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/infrastructure/media/attachment_staging_display_geometry.py +4 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/infrastructure/media/local_attachment_staging_adapter.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/infrastructure/media/local_banner_staging_adapter.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/infrastructure/media/local_staging_adapter.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/infrastructure/media/pillow_attachment_variant_encoder_adapter.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/infrastructure/media/pillow_avatar_variant_encoder_adapter.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/infrastructure/media/pillow_banner_variant_encoder_adapter.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/infrastructure/media/ssh_attachment_finals_publisher_adapter.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/infrastructure/media/ssh_banner_finals_publisher_adapter.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/infrastructure/media/ssh_finals_publisher_adapter.py +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/infrastructure/outbox/outbox_consumer_adapter.py +21 -10
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/infrastructure/outbox/outbox_producer_adapter.py +6 -4
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/testing/auth.py +20 -11
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/main.py +6 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/schedules/__init__.py +15 -0
- core_framework-2.3.1/core_framework/worker/schedules/analytics_refresh_cron.py +20 -0
- core_framework-2.3.1/core_framework/worker/schedules/schedule_prune_analytics_daily_facts.py +16 -0
- core_framework-2.3.1/core_framework/worker/schedules/schedule_refresh_analytics.py +7 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/tasks/__init__.py +8 -0
- core_framework-2.3.1/core_framework/worker/tasks/process_refresh_analytics_bucket.py +19 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/worker_context.py +4 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/README.md +2 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/deployments/deploy-playbook.md +5 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/deployments/e2e-smoke-tests-design.md +1 -1
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/comments/admin_comments.md +1 -1
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/deletion/comments.md +2 -1
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/deletion/overview.md +12 -3
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/deletion/posts.md +36 -23
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/deletion/users.md +68 -27
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/media/post-comment-attachments-design.md +1 -1
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/posts/admin_posts.md +2 -2
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/posts/author_context.md +1 -1
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/posts/post_like.md +2 -1
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/posts/post_stats_aggregation.md +5 -5
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/users/account_deletion.md +1 -1
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/users/user_removal.md +11 -7
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/library/README.md +2 -1
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/library/package-api.md +13 -1
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/library/testing-plugin-design.md +7 -6
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/platform/README.md +15 -14
- core_framework-2.3.1/docs/platform/analytics-rollup-design.md +990 -0
- core_framework-2.3.1/docs/testing.md +149 -0
- core_framework-2.3.1/docs/todo.md +76 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/pyproject.toml +1 -1
- core_framework-2.3.1/tests/integration/analytics/_helpers.py +140 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/_outbox_helpers.py +4 -7
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/admin/_auth_cases.py +5 -0
- core_framework-2.3.1/tests/integration/api/admin/analytics/router_test.py +265 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/admin/moderation/router_test.py +13 -7
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/admin/posts/router_test.py +14 -8
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/admin/users/router_test.py +9 -2
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/comments/comment_attachments_integration_test.py +236 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/comments/public/router_test.py +15 -11
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/create_idempotency_integration_test.py +16 -10
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/notifications/router_test.py +9 -4
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/posts/post_attachments_integration_test.py +294 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/posts/post_deletion_integration_test.py +108 -13
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/users/authenticated/router_test.py +4 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/users/deletion_audit_integration_test.py +6 -1
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/users/public/router_test.py +21 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/users/user_deletion_integration_test.py +134 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/worker/account_deletion_test.py +16 -8
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/worker/aggregate_user_stats_test.py +4 -2
- core_framework-2.3.1/tests/integration/worker/analytics_prune_test.py +77 -0
- core_framework-2.3.1/tests/integration/worker/analytics_refresh_test.py +109 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/worker/create_idempotency_retention_test.py +14 -8
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/worker/moderation_outbox_test.py +15 -7
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/worker/stale_staging_sweep_test.py +6 -1
- core_framework-2.3.1/tests/unit/infrastructure/media/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/uv.lock +1 -1
- core_framework-2.2.0/docs/todo.md +0 -11
- {core_framework-2.2.0 → core_framework-2.3.1}/.agents/skills/fastapi +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.agents/skills/library-skills/.library-skills.json +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.agents/skills/library-skills/SKILL.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/api-layer.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/api-reference-docs.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/api-validation.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/application-layer.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/constants-final.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/constitution.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/database-triggers.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/docstrings.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/domain-caller-context.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/domain-imports.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/domain-input-guards.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/domain-repository-exceptions.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/domain-services-and-adapters.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/error-boundary.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/flow-documentation.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/inline-raise-messages.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/integration-test-strategy.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/layer-boundaries.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/no-code-in-docs.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/strong-reads.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/rules/structured-logging.mdc +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/skills/add-config/SKILL.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/skills/code-review/SKILL.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/skills/fastapi +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/skills/implement-feature/SKILL.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/skills/recommend-features/SKILL.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/skills/sync-host-rules/SKILL.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.cursor/skills/write-flow-doc/SKILL.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.dockerignore +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.github/workflows/dev-ci-cd.yaml +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.github/workflows/manual-deployment.yaml +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.github/workflows/publish-pypi.yml +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.github/workflows/test.yaml +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.gitignore +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.pre-commit-config.yaml +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/.python-version +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/LICENSE +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/README.md +0 -0
- {core_framework-2.2.0/alembic/comment → core_framework-2.3.1/alembic/analytics}/alembic/README +0 -0
- {core_framework-2.2.0/alembic/comment → core_framework-2.3.1/alembic/analytics}/alembic/script.py.mako +0 -0
- {core_framework-2.2.0/alembic/comment → core_framework-2.3.1/alembic/analytics}/alembic.ini +0 -0
- {core_framework-2.2.0/alembic/extension → core_framework-2.3.1/alembic/comment}/alembic/README +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/comment/alembic/env.py +0 -0
- {core_framework-2.2.0/alembic/extension → core_framework-2.3.1/alembic/comment}/alembic/script.py.mako +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/comment/alembic/versions/comment_add_comment_attachments.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/comment/alembic/versions/comment_add_comment_create_idempotency.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/comment/alembic/versions/comment_add_comment_create_idempotency_created_at_index.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/comment/alembic/versions/comment_add_comment_mentions.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/comment/alembic/versions/comment_tombstone_status_model.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/comment/alembic/versions/v1_comment_init_baseline.py +0 -0
- {core_framework-2.2.0/alembic/media → core_framework-2.3.1/alembic/comment}/alembic.ini +0 -0
- {core_framework-2.2.0/alembic/media → core_framework-2.3.1/alembic/extension}/alembic/README +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/extension/alembic/env.py +0 -0
- {core_framework-2.2.0/alembic/media → core_framework-2.3.1/alembic/extension}/alembic/script.py.mako +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/extension/alembic/versions/v1_ext_init_baseline.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/extension/alembic.ini +0 -0
- {core_framework-2.2.0/alembic/moderation → core_framework-2.3.1/alembic/media}/alembic/README +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/media/alembic/env.py +0 -0
- {core_framework-2.2.0/alembic/moderation → core_framework-2.3.1/alembic/media}/alembic/script.py.mako +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/media/alembic/versions/media_add_attachment_staging_registry.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/media/alembic/versions/media_add_banner_staging_and_lease.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/media/alembic/versions/media_attachment_staging_registry_published_at.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/media/alembic/versions/v1_media_init_baseline.py +0 -0
- {core_framework-2.2.0/alembic/notification → core_framework-2.3.1/alembic/media}/alembic.ini +0 -0
- {core_framework-2.2.0/alembic/notification → core_framework-2.3.1/alembic/moderation}/alembic/README +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/moderation/alembic/env.py +0 -0
- {core_framework-2.2.0/alembic/notification → core_framework-2.3.1/alembic/moderation}/alembic/script.py.mako +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/moderation/alembic/versions/moderation_add_event_outbox.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/moderation/alembic/versions/moderation_add_event_outbox_dead_indexes.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/moderation/alembic/versions/moderation_add_event_outbox_pending_created_at_index.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/moderation/alembic/versions/v1_mod_init_baseline.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/moderation/alembic.ini +0 -0
- {core_framework-2.2.0/alembic/post → core_framework-2.3.1/alembic/notification}/alembic/README +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/notification/alembic/env.py +0 -0
- {core_framework-2.2.0/alembic/post → core_framework-2.3.1/alembic/notification}/alembic/script.py.mako +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/notification/alembic/versions/v1_notif_init_baseline.py +0 -0
- {core_framework-2.2.0/alembic/user → core_framework-2.3.1/alembic/post}/alembic/README +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/post/alembic/env.py +0 -0
- {core_framework-2.2.0/alembic/user → core_framework-2.3.1/alembic/post}/alembic/script.py.mako +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/post/alembic/versions/post_add_post_attachments.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/post/alembic/versions/post_add_post_create_idempotency.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/post/alembic/versions/post_add_post_create_idempotency_created_at_index.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/post/alembic/versions/post_add_post_mentions.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/post/alembic/versions/post_post_views_retention_high_water_mark.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/post/alembic/versions/post_tombstone_status_model.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/post/alembic/versions/v1_post_init_baseline.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/post/alembic.ini +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/user/alembic/env.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/user/alembic/versions/user_add_avatar_ingest_sequences.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/user/alembic/versions/user_add_banner_ingest_sequences.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/user/alembic/versions/user_add_date_of_birth_to_profiles.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/user/alembic/versions/user_add_event_outbox.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/user/alembic/versions/user_add_event_outbox_dead_indexes.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/user/alembic/versions/user_add_event_outbox_pending_created_at_index.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/user/alembic/versions/user_reserve_sentinel_user_ids.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/user/alembic/versions/v1_user_init_baseline.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/alembic/user/alembic.ini +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/admin/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/admin/cache/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/admin/cache/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/admin/comments/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/admin/comments/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/admin/moderation/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/admin/moderation/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/admin/moderation/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/admin/outbox/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/admin/posts/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/admin/posts/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/admin/users/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/admin/users/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/admin/users/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/attachments/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/attachments/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/auth/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/auth/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/auth/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/comments/authenticated/mappers.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/comments/authenticated/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/comments/authenticated/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/comments/public/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/comments/public/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/comments/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/comments/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/constants.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/dependencies.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/events/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/events/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/idempotency/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/idempotency/dependencies.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/notifications/authenticated/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/notifications/authenticated/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/notifications/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/posts/authenticated/mappers.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/posts/authenticated/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/posts/authenticated/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/posts/public/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/posts/public/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/posts/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/posts/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/system/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/system/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/users/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/users/authenticated/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/users/authenticated/mappers.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/users/authenticated/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/users/authenticated/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/users/mappers.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/users/public/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/users/public/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/users/public/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/users/router.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/api/users/shared/schemas.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/auth/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/auth/access_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/auth/auth_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/cache/invalidate_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/comments/admin_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/comments/aggregation_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/comments/authenticated_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/comments/cleanup_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/comments/public_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/events/README.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/events/event_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/events/event_token.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/media/README.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/media/avatar_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/media/banner_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/media/ingest_guards.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/moderation/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/moderation/appeal_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/moderation/moderator_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/moderation/report_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/moderation/scheduled_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/moderation/user_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/notifications/README.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/notifications/inbox_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/notifications/mute_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/notifications/notification_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/outbox/block_mirrors.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/outbox/exceptions.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/outbox/follow_mirrors.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/outbox/handlers.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/outbox/ops_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/outbox/polling_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/outbox/processing_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/outbox/restriction_mirrors.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/posts/admin_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/posts/aggregation_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/posts/authenticated_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/posts/cleanup_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/shared/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/shared/user_agent.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/shared/worker_jobs.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/users/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/users/admin_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/users/aggregation_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/users/authenticated_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/users/public_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/application/users/scheduled_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/asgi.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/constants.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/core/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/core/cache.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/core/context.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/core/exception_handlers/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/core/exception_handlers/application.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/core/exception_handlers/common.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/core/exception_handlers/setup.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/core/exceptions.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/core/firebase.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/core/http_client.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/core/logging.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/core/middleware.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/core/observability.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/core/pagination.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/core/redis.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/core/runtime.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/auth/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/auth/dependencies.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/auth/exceptions.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/auth/models.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/auth/ports/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/comment/README.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/comment/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/comment/constants.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/comment/enums.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/comment/exceptions.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/comment/models.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/comment/outcomes.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/exceptions.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/README.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/attachment/constants.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/attachment/exceptions.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/attachment/models.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/avatar/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/avatar/components/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/avatar/constants.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/avatar/exceptions.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/avatar/models.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/avatar/ports/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/banner/constants.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/banner/exceptions.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/banner/models.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/shared/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/shared/constants.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/shared/exceptions.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/shared/outcomes.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/media/shared/utils.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/moderation/README.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/moderation/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/moderation/enums.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/moderation/exceptions.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/moderation/models.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/moderation/outbox_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/moderation/outcomes.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/notification/README.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/notification/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/notification/enums.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/notification/exceptions.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/notification/models.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/outbox/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/outbox/dependencies.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/outbox/models.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/outbox/ports/admin.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/post/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/post/constants.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/post/enums.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/post/exceptions.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/post/models.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/post/outcomes.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/user/README.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/user/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/user/constants.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/user/enums.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/user/exceptions.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/user/models.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/user/outbox_service.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/user/outcomes.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/domains/user/utils.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/infrastructure/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/infrastructure/firebase/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/infrastructure/firebase/firebase_auth_adapter.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/infrastructure/media/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/infrastructure/media/pillow_staging_image.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/main.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/migrate_cli.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/testing/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/testing/arq.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/testing/config.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/testing/containers.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/testing/firebase.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/testing/firebase_identity_toolkit.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/testing/hookspecs.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/testing/httpx_test_client.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/testing/media.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/testing/migrations.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/testing/plugin.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/schedules/schedule_aggregate_comment_stats.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/schedules/schedule_aggregate_post_stats.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/schedules/schedule_aggregate_user_stats.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/schedules/schedule_expired_account_deletions.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/schedules/schedule_expired_mute_lifts.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/schedules/schedule_poll_outbox.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/schedules/schedule_prune_post_views.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/schedules/schedule_purge_expired_create_idempotency_keys.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/schedules/schedule_sweep_stale_attachment_staging.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/schedules/schedule_sweep_stale_avatar_staging.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/schedules/schedule_sweep_stale_banner_staging.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/tasks/delete_attachment_finals.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/tasks/delete_superseded_avatar_finals.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/tasks/delete_superseded_banner_finals.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/tasks/process_account_deletion.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/tasks/process_admin_user_deletion.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/tasks/process_aggregate_comment_stats.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/tasks/process_aggregate_post_stats.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/tasks/process_aggregate_user_stats.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/tasks/process_attachment_asset_variants.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/tasks/process_banner_asset_variants.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/tasks/process_cache_pattern_invalidation.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/tasks/process_destructive_comment_delete.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/tasks/process_destructive_post_delete.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/tasks/process_media_asset_variants.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/tasks/process_mute_lift.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/tasks/process_outbox_row.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/core_framework/worker/tasks/process_user_content_deletion.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docker-compose.dev.yaml +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docker-compose.yml +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/dockerfile +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/deployments/README.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/deployments/edge-upload-limits.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/deployments/examples/Caddyfile.example +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/deployments/examples/image-server-known-hosts.example +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/deployments/guides/app-server-deploy-ssh.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/deployments/guides/image-server-ssh.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/deployments/guides/postgres-setup.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/deployments/guides/redis-setup.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/deployments/guides/ubuntu-server-setup.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/deployments/release-playbook.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/README.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/auth/access_control.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/auth/registration.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/cache/admin_cache_invalidation.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/comments/comment_report.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/comments/comment_stats_aggregation.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/comments/create_comment.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/comments/delete_comment.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/comments/edit_comment.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/comments/retrieve_comments.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/events/events.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/media/avatar-upload-design.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/media/banner-upload-design.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/media/upload-pipeline-design.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/media/upload_pipeline.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/mentions/mentions_in_content.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/moderation/appeals.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/moderation/internal_notes.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/moderation/moderator_actions.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/moderation/reports.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/moderation/restrictions.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/notifications/notification_inbox.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/outbox/admin_outbox_ops.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/posts/hashtag_discovery.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/posts/post_visibility.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/users/account.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/users/avatar.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/users/banner.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/users/blocks.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/users/change_history.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/users/check_username_exists.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/users/follow-system-design.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/users/follow.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/users/my_posts_and_comments.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/users/preferences.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/users/profile.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/domains/users/public_profile.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/library/api.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/library/core-framework-migration.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/library/overview.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/platform/architecture-decisions.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/platform/client-error-visibility.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/platform/conventions.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/platform/create-idempotency-design.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/platform/database-triggers.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/platform/domain-services-and-adapters.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/platform/event-outbox-design.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/platform/events-ingest-design.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/platform/layers-and-boundaries.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/platform/media-storage-topology.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/platform/outbox-admin-ops-design.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/docs/platform/patch-update-typed-payload.md +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/firebase_config.example.json +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/makefile +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/conftest.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/e2e/_auth.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/e2e/_canaries.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/e2e/_media.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/e2e/_settings.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/e2e/_upload_payloads.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/e2e/conftest.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/e2e/media/pipeline_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/e2e/smoke/canary_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/e2e/smoke/media_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/e2e/smoke/system_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/e2e/smoke/tls_host_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/_http_error_details.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/_auth_helpers.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/_destructive_delete_helpers.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/_http_helpers.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/_user_deletion_helpers.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/_user_helpers.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/admin/__init__.py +0 -0
- {core_framework-2.2.0/tests/integration/api/admin/cache → core_framework-2.3.1/tests/integration/api/admin/analytics}/__init__.py +0 -0
- {core_framework-2.2.0/tests/integration/api/admin/comments → core_framework-2.3.1/tests/integration/api/admin/cache}/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/admin/cache/router_test.py +0 -0
- {core_framework-2.2.0/tests/integration/api/admin/moderation → core_framework-2.3.1/tests/integration/api/admin/comments}/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/admin/comments/router_test.py +0 -0
- {core_framework-2.2.0/tests/integration/api/admin/outbox → core_framework-2.3.1/tests/integration/api/admin/moderation}/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/admin/moderation/_auth_cases.py +0 -0
- {core_framework-2.2.0/tests/integration/api/admin/posts → core_framework-2.3.1/tests/integration/api/admin/outbox}/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/admin/outbox/router_test.py +0 -0
- {core_framework-2.2.0/tests/integration/api/admin/users → core_framework-2.3.1/tests/integration/api/admin/posts}/__init__.py +0 -0
- {core_framework-2.2.0/tests/integration/api/auth → core_framework-2.3.1/tests/integration/api/admin/users}/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/attachments/_helpers.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/attachments/attachment_upload_test.py +0 -0
- {core_framework-2.2.0/tests/integration/api/comments → core_framework-2.3.1/tests/integration/api/auth}/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/auth/router_test.py +0 -0
- {core_framework-2.2.0/tests/integration/api/comments/authenticated → core_framework-2.3.1/tests/integration/api/comments}/__init__.py +0 -0
- {core_framework-2.2.0/tests/integration/api/comments/public → core_framework-2.3.1/tests/integration/api/comments/authenticated}/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/comments/authenticated/comment_writes_integration_test.py +0 -0
- {core_framework-2.2.0/tests/integration/api/notifications → core_framework-2.3.1/tests/integration/api/comments/public}/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/events/router_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/media/ingest_contract_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/mentions/mentions_integration_test.py +0 -0
- {core_framework-2.2.0/tests/integration/api/posts → core_framework-2.3.1/tests/integration/api/notifications}/__init__.py +0 -0
- {core_framework-2.2.0/tests/integration/api/posts/public → core_framework-2.3.1/tests/integration/api/posts}/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/posts/authenticated/post_writes_integration_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/posts/comment_count_aggregation_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/posts/followers_visibility_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/posts/post_stats_dirty_marking_test.py +0 -0
- {core_framework-2.2.0/tests/integration/api/system → core_framework-2.3.1/tests/integration/api/posts/public}/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/posts/public/router_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/public_read_by_id_unavailable_detail_test.py +0 -0
- {core_framework-2.2.0/tests/integration/api/users → core_framework-2.3.1/tests/integration/api/system}/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/system/router_test.py +0 -0
- {core_framework-2.2.0/tests/integration/api/users/authenticated → core_framework-2.3.1/tests/integration/api/users}/__init__.py +0 -0
- {core_framework-2.2.0/tests/integration/api/users/public → core_framework-2.3.1/tests/integration/api/users/authenticated}/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/users/authenticated/avatar_upload_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/api/users/authenticated/banner_upload_test.py +0 -0
- {core_framework-2.2.0/tests/integration/worker → core_framework-2.3.1/tests/integration/api/users/public}/__init__.py +0 -0
- {core_framework-2.2.0/tests/unit → core_framework-2.3.1/tests/integration/worker}/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/worker/_media_upload_helpers.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/worker/aggregate_comment_stats_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/worker/aggregate_post_stats_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/worker/attachment_upload_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/worker/avatar_upload_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/worker/banner_upload_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/worker/conftest.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/worker/mute_lift_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/worker/user_outbox_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/integration/worker/utils_test.py +0 -0
- {core_framework-2.2.0/tests/unit/application/comments → core_framework-2.3.1/tests/unit}/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/unit/api/comments/authenticated/comment_request_schemas_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/unit/api/dedupe_preserving_order_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/unit/api/posts/authenticated/post_update_mapper_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/unit/api/users/profile_update_mapper_test.py +0 -0
- {core_framework-2.2.0/tests/unit/domains → core_framework-2.3.1/tests/unit/application/comments}/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/unit/application/events/event_service_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/unit/application/notifications/inbox_service_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/unit/application/users/deletion_audit_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/unit/core/bundled_alembic_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/unit/core/migrate_cli_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/unit/core/pagination_test.py +0 -0
- {core_framework-2.2.0/tests/unit/domains/comment → core_framework-2.3.1/tests/unit/domains}/__init__.py +0 -0
- {core_framework-2.2.0/tests/unit/infrastructure → core_framework-2.3.1/tests/unit/domains/comment}/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/unit/domains/post/models_test.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/unit/domains/user/service_test.py +0 -0
- {core_framework-2.2.0/tests/unit/infrastructure/media → core_framework-2.3.1/tests/unit/infrastructure}/__init__.py +0 -0
- {core_framework-2.2.0 → core_framework-2.3.1}/tests/unit/infrastructure/media/ssh_finals_publisher_adapter_test.py +0 -0
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Alembic revision id format
|
|
2
|
+
description: Alembic migrations — revision id format and index DDL conventions
|
|
3
3
|
globs: alembic/**/alembic/versions/*.py
|
|
4
4
|
alwaysApply: false
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
# Alembic
|
|
7
|
+
# Alembic migrations
|
|
8
|
+
|
|
9
|
+
## Revision IDs
|
|
8
10
|
|
|
9
11
|
`alembic_version.version_num` is a **32-character** string. Revision ids must stay well under that limit.
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
### New revisions (required)
|
|
12
14
|
|
|
13
15
|
- Set `revision` to a **12-character lowercase hex** string (Alembic default), e.g. `a56bc6b7f799`.
|
|
14
16
|
- Generate with `python -c "import secrets; print(secrets.token_hex(6))"` or `alembic revision`.
|
|
@@ -30,10 +32,29 @@ revision: str = "120bf6be81eb"
|
|
|
30
32
|
revision: str = "post_tombstone_status_model"
|
|
31
33
|
```
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
### Exceptions
|
|
34
36
|
|
|
35
37
|
- Collapsed **`v1_*_init`** baseline revisions keep their stable names (`v1_post_init`, etc.) — do not rename shipped baselines.
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
### Do not rename shipped revision ids
|
|
38
40
|
|
|
39
41
|
If a revision may already be applied in any environment, **do not** change its `revision` string. Add a forward migration instead.
|
|
42
|
+
|
|
43
|
+
## Index DDL
|
|
44
|
+
|
|
45
|
+
### New index migrations (required)
|
|
46
|
+
|
|
47
|
+
In **`upgrade()`**, create indexes with **`IF NOT EXISTS`** so migrations succeed when an index was already applied manually in production:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
op.execute("""
|
|
51
|
+
create index if not exists idx_example_created_at
|
|
52
|
+
on example_table (created_at);
|
|
53
|
+
""")
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Use **`drop index if exists`** in **`downgrade()`**.
|
|
57
|
+
|
|
58
|
+
### Do not retrofit index DDL
|
|
59
|
+
|
|
60
|
+
Do **not** edit shipped migrations only to add `if not exists` — those revisions may already be applied. New forward migrations follow this rule; legacy `create index` without `if not exists` stays as-is.
|
|
@@ -45,3 +45,12 @@ Use explicit write/read configuration and keep schema names shared.
|
|
|
45
45
|
- `read_postgres`
|
|
46
46
|
- Domain dependencies pass `(write, read, strong)` explicitly to repositories.
|
|
47
47
|
- Until a dedicated strong instance exists, use write instance for the strong argument intentionally.
|
|
48
|
+
|
|
49
|
+
## Schema names in SQL
|
|
50
|
+
|
|
51
|
+
- Schema names come only from `settings.postgres_schemas` (see **Settings Model** above).
|
|
52
|
+
- Domain `dependencies.py` passes unquoted `schema=runtime.settings.postgres_schemas.schema_<domain>` into repositories (keyword-only `__init__`).
|
|
53
|
+
- Repositories cache `self.schema_sql = quote_postgres_identifier(schema)` and use `{self.schema_sql}.table` in f-string SQL — never hard-coded `"post".` literals.
|
|
54
|
+
- Outbox producer adapter is wired per domain with `schema` at construction; consumer adapter receives unquoted `schema` per call and caches quoted SQL.
|
|
55
|
+
|
|
56
|
+
Repository isolation rules (no cross-domain schema access): **`repository-schema-isolation.mdc`**.
|
|
@@ -11,10 +11,19 @@ Each domain repository may only read/write tables in **its own** Postgres schema
|
|
|
11
11
|
|
|
12
12
|
Canonical rule: `docs/platform/layers-and-boundaries.md` — *no cross-domain schema reads, writes, joins, or foreign keys*.
|
|
13
13
|
|
|
14
|
+
## Schema qualification
|
|
15
|
+
|
|
16
|
+
Repositories receive unquoted `schema` from `runtime.settings.postgres_schemas` in `dependencies.py` (keyword-only `__init__` arg). In `__init__`:
|
|
17
|
+
|
|
18
|
+
- `self.schema` — config schema name (e.g. `post`)
|
|
19
|
+
- `self.schema_sql` — `quote_postgres_identifier(schema)` for SQL interpolation
|
|
20
|
+
|
|
21
|
+
Use `{self.schema_sql}.table` in f-string SQL — not hard-coded `"post".` literals.
|
|
22
|
+
|
|
14
23
|
## Allowed in a repository
|
|
15
24
|
|
|
16
|
-
- SQL qualified with that domain's schema only (e.g. `
|
|
17
|
-
- **Within-schema** policy gates (e.g. `insert_post_like` checks `
|
|
25
|
+
- SQL qualified with that domain's schema only (e.g. `{self.schema_sql}.posts` in `PostRepository`).
|
|
26
|
+
- **Within-schema** policy gates (e.g. `insert_post_like` checks `{self.schema_sql}.posts.status = active`).
|
|
18
27
|
- **Lookup mirrors** in the same schema (`user_blocks_lookup`, `user_restrictions_lookup`, `user_followers_lookup`) — synced by application flows; mirrors are not authoritative.
|
|
19
28
|
|
|
20
29
|
## Forbidden in a repository
|
|
@@ -23,18 +32,18 @@ Canonical rule: `docs/platform/layers-and-boundaries.md` — *no cross-domain sc
|
|
|
23
32
|
- Importing another domain's `service`, `repository`, or models to enforce policy.
|
|
24
33
|
|
|
25
34
|
```python
|
|
26
|
-
# ❌ moderation/repository.py — cross-domain EXISTS
|
|
27
|
-
insert into
|
|
35
|
+
# ❌ moderation/repository.py — cross-domain EXISTS (illustrative schema literals)
|
|
36
|
+
insert into {moderation_schema_sql}.post_reports (...)
|
|
28
37
|
select $1, $2, $3, $4
|
|
29
38
|
where exists (
|
|
30
|
-
select 1 from
|
|
39
|
+
select 1 from {post_schema_sql}.posts
|
|
31
40
|
where id = $2 and status = 'active'
|
|
32
41
|
)
|
|
33
42
|
|
|
34
43
|
# ❌ notification/repository.py — cross-domain mute gate
|
|
35
|
-
insert into
|
|
44
|
+
insert into {notification_schema_sql}.notification_mutes (...)
|
|
36
45
|
select ...
|
|
37
|
-
where exists (select 1 from
|
|
46
|
+
where exists (select 1 from {comment_schema_sql}.comments where ...)
|
|
38
47
|
```
|
|
39
48
|
|
|
40
49
|
## Alembic migrations (different convention)
|
|
@@ -69,4 +78,8 @@ Before adding a repository write that depends on another domain's row state:
|
|
|
69
78
|
|
|
70
79
|
## Self-check (repositories only)
|
|
71
80
|
|
|
72
|
-
In `domains/<name>/repository.py
|
|
81
|
+
In `domains/<name>/repository.py`:
|
|
82
|
+
|
|
83
|
+
- Every table/type qualifier in SQL must use `{self.schema_sql}.…` (that domain's schema only).
|
|
84
|
+
- Do not hard-code `"user".`, `"post".`, etc. — use `self.schema` / `self.schema_sql` from config.
|
|
85
|
+
- Mirror lookup tables (`user_blocks_lookup`, etc.) still live under `{self.schema_sql}` when they belong to that domain's schema.
|
|
@@ -192,7 +192,7 @@ Worker `startup` and API `init_app` already call `configure_application_dependen
|
|
|
192
192
|
|
|
193
193
|
Do this in a **separate step/PR** when you have DDL to apply.
|
|
194
194
|
|
|
195
|
-
1. Add **`alembic/<domain>/alembic/versions/*.py`** (at least one revision); `upgrade()` may create schema/tables/enums as designed. Use a **12-character lowercase hex** `revision` id (see **`alembic-
|
|
195
|
+
1. Add **`alembic/<domain>/alembic/versions/*.py`** (at least one revision); `upgrade()` may create schema/tables/enums as designed. Use a **12-character lowercase hex** `revision` id (see **`alembic-migrations`**); put the descriptive slug in the filename only.
|
|
196
196
|
1. **Append** `<domain>` to `ALEMBIC_DOMAINS` in `core_framework/bundled_alembic.py` **in the same change** as the first revision (so `cf-alembic` / CI stay green).
|
|
197
197
|
1. `make alembic` and deploy’s `cf-alembic` step pick up the new domain automatically — no makefile or `_deploy.yml` edits needed.
|
|
198
198
|
|
|
@@ -61,6 +61,7 @@ jobs:
|
|
|
61
61
|
LOG_LEVEL: ${{ vars.LOG_LEVEL }}
|
|
62
62
|
CORS_ALLOWED_ORIGINS: ${{ vars.CORS_ALLOWED_ORIGINS }}
|
|
63
63
|
RETENTION_POST_VIEWS_DAYS: ${{ vars.RETENTION_POST_VIEWS_DAYS }}
|
|
64
|
+
ANALYTICS_REFRESH_INTERVAL_SECONDS: ${{ vars.ANALYTICS_REFRESH_INTERVAL_SECONDS }}
|
|
64
65
|
run: |
|
|
65
66
|
set -euo pipefail
|
|
66
67
|
|
|
@@ -4,6 +4,38 @@ Notable changes to **core-framework** (import **`core_framework`**). Format foll
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [2.3.1] - 2026-07-03
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Partial admin user deletions:** `tombstone_authored_posts_only` and `tombstone_authored_comments_only` on `POST /admin/users/{user_id}/deletions` — moderator tombstone of authored posts or comments while keeping the account. See **`docs/domains/deletion/users.md`**.
|
|
12
|
+
- **`docs/testing.md`** — usage-focused pytest plugin guide for host adopters (wiring, fixtures, tiers, troubleshooting). Design reference remains **`docs/library/testing-plugin-design.md`**.
|
|
13
|
+
- **Post/comment attachment integration tests:** v1.1 create/edit coverage — best-effort attach (unknown or already-linked assets), attach before worker publish, edit-budget enforcement, clear-all patch, and tombstone finals cleanup. See **`tests/integration/api/posts/post_attachments_integration_test.py`** and **`tests/integration/api/comments/comment_attachments_integration_test.py`**.
|
|
14
|
+
- **Analytics rollup:** `retrieve_daily_fact_kind_totals_for_range` on `AnalyticsService` and `retrieve_analytics_kind_totals_for_range` in `application/analytics/read_service` — sum `daily_fact` counts per `kind` over an inclusive UTC day range (for host catalog and similar reads).
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **Admin outbox schemas:** rename response field `schema` to `producing_schema` with JSON alias `schema` so Pydantic no longer warns about shadowing `BaseModel.schema`.
|
|
19
|
+
|
|
20
|
+
## [2.3.0] - 2026-07-03
|
|
21
|
+
|
|
22
|
+
Analytics rollup domain (materialized daily activity counts), admin read API, and retention prune. Hosts must add **`[analytics]`** and **`schema_analytics`**, run **`cf-alembic`**, and run the worker for rollup refresh and daily-fact prune.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- **Analytics rollup (Phase 1):** `analytics` domain scaffold, `schema_analytics`, `[analytics].refresh_interval_seconds` (required on **`Settings`**; core **`config.toml`** ships **300**), Alembic tree with `daily_fact` table (PK only), and activity source registry shell. See **`docs/platform/analytics-rollup-design.md`**.
|
|
27
|
+
- **Analytics rollup (Phase 2):** `AnalyticsService.list_refreshable_days` and `is_final_bucket_refresh` (planner inputs), `refresh_daily_fact_bucket` (single-bucket executor), optional `refresh_all_open_buckets` for tests; transactional `replace_daily_fact_bucket`; admin read helpers.
|
|
28
|
+
- **Analytics rollup (Phase 3):** `post` / `created` activity source, planner (`enqueue_refresh_analytics_buckets`) and executor (`refresh_analytics_bucket`) application entrypoints, `schedule_refresh_analytics` + `process_refresh_analytics_bucket` worker (cron from `refresh_interval_seconds`).
|
|
29
|
+
- **Analytics rollup (Phase 4):** `comment` / `created`, `user` / `registered`, and `post` / `viewed` activity sources with domain UTC-day count accessors and registry registration.
|
|
30
|
+
- **Analytics rollup (Phase 5):** admin read API — `GET /admin/analytics/daily/range` and `GET /admin/analytics/daily/day` via `read_service` and Phase 2 domain read helpers.
|
|
31
|
+
- **Analytics rollup (Phase 6):** one-year `daily_fact` retention prune (`schedule_prune_analytics_daily_facts`, daily **05:10 UTC**); `idx_daily_fact_day` migration. Source-feed `created_at` indexes for refresh counts already exist on `posts`, `comments`, `users`, and `post_views`.
|
|
32
|
+
- **Analytics rollup (Phase 7):** integration tests for refresh planner/executor, admin read API, and retention prune.
|
|
33
|
+
|
|
34
|
+
### Breaking
|
|
35
|
+
|
|
36
|
+
- **Breaking (hosts — config):** Add **`[analytics]`** with **`refresh_interval_seconds`** (required on **`Settings`**; core **`config.toml`** ships **300**). Deploy variable **`ANALYTICS_REFRESH_INTERVAL_SECONDS`**. Add **`schema_analytics`** under **`[postgres_schemas]`** (default **`analytics`**).
|
|
37
|
+
- **Breaking (hosts — migration):** When upgrading to a release that includes analytics rollup, run **`cf-alembic`** so the **`analytics`** migration stream applies **`analytics_daily_fact`** (`analytics.daily_fact`) and **`analytics_daily_fact_day_index`** (`idx_daily_fact_day`). The worker must run **`schedule_refresh_analytics`** and **`schedule_prune_analytics_daily_facts`** (registered in core worker schedules) for rollup materialization and one-year retention prune.
|
|
38
|
+
|
|
7
39
|
## [2.2.0] - 2026-06-30
|
|
8
40
|
|
|
9
41
|
Transactional outbox (block, follow, restriction), post views retention, and incremental view-count aggregation. Hosts must update **`config.toml`**, run **`cf-alembic`**, and run the worker for mirror fanout and retention jobs.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: core-framework
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.1
|
|
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
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from logging.config import fileConfig
|
|
3
|
+
|
|
4
|
+
from sqlalchemy import pool, text
|
|
5
|
+
from sqlalchemy.engine import Connection
|
|
6
|
+
from sqlalchemy.ext.asyncio import async_engine_from_config
|
|
7
|
+
|
|
8
|
+
from alembic import context
|
|
9
|
+
from core_framework.core.settings import load_default_settings
|
|
10
|
+
|
|
11
|
+
settings = load_default_settings()
|
|
12
|
+
|
|
13
|
+
config = context.config
|
|
14
|
+
|
|
15
|
+
if config.config_file_name is not None:
|
|
16
|
+
fileConfig(config.config_file_name)
|
|
17
|
+
|
|
18
|
+
target_metadata = None
|
|
19
|
+
|
|
20
|
+
config.set_main_option("sqlalchemy.url", settings.write_postgres.alembic_postgres_url)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def run_migrations_offline() -> None:
|
|
24
|
+
"""Run migrations in 'offline' mode."""
|
|
25
|
+
url = config.get_main_option("sqlalchemy.url")
|
|
26
|
+
context.configure(
|
|
27
|
+
url=url,
|
|
28
|
+
target_metadata=target_metadata,
|
|
29
|
+
literal_binds=True,
|
|
30
|
+
dialect_opts={"paramstyle": "named"},
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
quoted_schema_name = f'"{settings.postgres_schemas.schema_analytics.replace('"', '""')}"'
|
|
34
|
+
|
|
35
|
+
with context.begin_transaction():
|
|
36
|
+
context.execute(text(f"set search_path to {quoted_schema_name}"))
|
|
37
|
+
context.run_migrations()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def do_run_migrations(connection: Connection) -> None:
|
|
41
|
+
quoted_schema_name = f'"{settings.postgres_schemas.schema_analytics.replace('"', '""')}"'
|
|
42
|
+
|
|
43
|
+
connection.execute(text(f"create schema if not exists {quoted_schema_name}"))
|
|
44
|
+
connection.execute(text(f"set search_path to {quoted_schema_name}"))
|
|
45
|
+
connection.commit()
|
|
46
|
+
|
|
47
|
+
context.configure(connection=connection, target_metadata=target_metadata)
|
|
48
|
+
|
|
49
|
+
with context.begin_transaction():
|
|
50
|
+
context.run_migrations()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
async def run_async_migrations() -> None:
|
|
54
|
+
"""Run migrations in 'online' mode."""
|
|
55
|
+
connectable = async_engine_from_config(
|
|
56
|
+
config.get_section(config.config_ini_section, {}),
|
|
57
|
+
prefix="sqlalchemy.",
|
|
58
|
+
poolclass=pool.NullPool,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
async with connectable.connect() as connection:
|
|
62
|
+
await connection.run_sync(do_run_migrations)
|
|
63
|
+
|
|
64
|
+
await connectable.dispose()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def run_migrations_online() -> None:
|
|
68
|
+
asyncio.run(run_async_migrations())
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if context.is_offline_mode():
|
|
72
|
+
run_migrations_offline()
|
|
73
|
+
else:
|
|
74
|
+
run_migrations_online()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""analytics daily_fact
|
|
2
|
+
|
|
3
|
+
Revision ID: 5b9ac98bb150
|
|
4
|
+
Revises:
|
|
5
|
+
Create Date: 2026-07-01 12:00:00.000000
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
from alembic import op
|
|
12
|
+
|
|
13
|
+
revision: str = "5b9ac98bb150"
|
|
14
|
+
down_revision: str | Sequence[str] | None = None
|
|
15
|
+
branch_labels: str | Sequence[str] | None = None
|
|
16
|
+
depends_on: str | Sequence[str] | None = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade() -> None:
|
|
20
|
+
op.execute("""
|
|
21
|
+
create table daily_fact (
|
|
22
|
+
subject_type text not null,
|
|
23
|
+
activity_type text not null,
|
|
24
|
+
day date not null,
|
|
25
|
+
kind text not null default '',
|
|
26
|
+
count bigint not null default 0,
|
|
27
|
+
primary key (subject_type, activity_type, day, kind)
|
|
28
|
+
);
|
|
29
|
+
""")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def downgrade() -> None:
|
|
33
|
+
op.execute("drop table if exists daily_fact;")
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""analytics daily_fact day index for retention prune
|
|
2
|
+
|
|
3
|
+
Revision ID: 7f2a9c1d4e8b
|
|
4
|
+
Revises: 5b9ac98bb150
|
|
5
|
+
Create Date: 2026-07-02 12:00:00.000000
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
from alembic import op
|
|
12
|
+
|
|
13
|
+
revision: str = "7f2a9c1d4e8b"
|
|
14
|
+
down_revision: str | Sequence[str] | None = "5b9ac98bb150"
|
|
15
|
+
branch_labels: str | Sequence[str] | None = None
|
|
16
|
+
depends_on: str | Sequence[str] | None = None
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade() -> None:
|
|
20
|
+
op.execute("""
|
|
21
|
+
create index if not exists idx_daily_fact_day
|
|
22
|
+
on daily_fact (day);
|
|
23
|
+
""")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def downgrade() -> None:
|
|
27
|
+
op.execute("drop index if exists idx_daily_fact_day;")
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# A generic, single database configuration.
|
|
2
|
+
|
|
3
|
+
[alembic]
|
|
4
|
+
# path to migration scripts.
|
|
5
|
+
# this is typically a path given in POSIX (e.g. forward slashes)
|
|
6
|
+
# format, relative to the token %(here)s which refers to the location of this
|
|
7
|
+
# ini file
|
|
8
|
+
script_location = %(here)s/alembic
|
|
9
|
+
|
|
10
|
+
# sys.path path, will be prepended to sys.path if present.
|
|
11
|
+
prepend_sys_path = .
|
|
12
|
+
|
|
13
|
+
path_separator = os
|
|
14
|
+
|
|
15
|
+
sqlalchemy.url = driver://user:pass@localhost/dbname
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
[post_write_hooks]
|
|
19
|
+
|
|
20
|
+
[loggers]
|
|
21
|
+
keys = root,sqlalchemy,alembic
|
|
22
|
+
|
|
23
|
+
[handlers]
|
|
24
|
+
keys = console
|
|
25
|
+
|
|
26
|
+
[formatters]
|
|
27
|
+
keys = generic
|
|
28
|
+
|
|
29
|
+
[logger_root]
|
|
30
|
+
level = WARNING
|
|
31
|
+
handlers = console
|
|
32
|
+
qualname =
|
|
33
|
+
|
|
34
|
+
[logger_sqlalchemy]
|
|
35
|
+
level = WARNING
|
|
36
|
+
handlers =
|
|
37
|
+
qualname = sqlalchemy.engine
|
|
38
|
+
|
|
39
|
+
[logger_alembic]
|
|
40
|
+
level = INFO
|
|
41
|
+
handlers =
|
|
42
|
+
qualname = alembic
|
|
43
|
+
|
|
44
|
+
[handler_console]
|
|
45
|
+
class = StreamHandler
|
|
46
|
+
args = (sys.stderr,)
|
|
47
|
+
level = NOTSET
|
|
48
|
+
formatter = generic
|
|
49
|
+
|
|
50
|
+
[formatter_generic]
|
|
51
|
+
format = %(levelname)-5.5s [%(name)s] %(message)s
|
|
52
|
+
datefmt = %H:%M:%S
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Generic single-database configuration with an async dbapi.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""${message}
|
|
2
|
+
|
|
3
|
+
Revision ID: ${up_revision}
|
|
4
|
+
Revises: ${down_revision | comma,n}
|
|
5
|
+
Create Date: ${create_date}
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from typing import Sequence, Union
|
|
9
|
+
|
|
10
|
+
from alembic import op
|
|
11
|
+
import sqlalchemy as sa
|
|
12
|
+
${imports if imports else ""}
|
|
13
|
+
|
|
14
|
+
# revision identifiers, used by Alembic.
|
|
15
|
+
revision: str = ${repr(up_revision)}
|
|
16
|
+
down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)}
|
|
17
|
+
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
|
|
18
|
+
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def upgrade() -> None:
|
|
22
|
+
"""Upgrade schema."""
|
|
23
|
+
${upgrades if upgrades else "pass"}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def downgrade() -> None:
|
|
27
|
+
"""Downgrade schema."""
|
|
28
|
+
${downgrades if downgrades else "pass"}
|
|
@@ -25,18 +25,21 @@ avatar_host_domain = "avatar.core-framework.com"
|
|
|
25
25
|
default_avatar_image = "default.webp"
|
|
26
26
|
publish_root = "/var/www/sites/avatar"
|
|
27
27
|
public_path_prefix = "avatar"
|
|
28
|
+
extension = "webp"
|
|
28
29
|
|
|
29
30
|
[banner]
|
|
30
31
|
banner_host_domain = "banner.core-framework.com"
|
|
31
32
|
default_banner_image = "default.webp"
|
|
32
33
|
publish_root = "/var/www/sites/banner"
|
|
33
34
|
public_path_prefix = "banner"
|
|
35
|
+
extension = "webp"
|
|
34
36
|
|
|
35
37
|
[attachment]
|
|
36
38
|
attachment_host_domain = "attachment.core-framework.com"
|
|
37
39
|
default_attachment_image = "default.webp"
|
|
38
40
|
publish_root = "/var/www/sites/attachment"
|
|
39
41
|
public_path_prefix = "attachment"
|
|
42
|
+
extension = "webp"
|
|
40
43
|
|
|
41
44
|
[image_server]
|
|
42
45
|
host = "127.0.0.1"
|
|
@@ -113,6 +116,9 @@ max_tries = 2
|
|
|
113
116
|
[retention]
|
|
114
117
|
post_views_retention_days = 90
|
|
115
118
|
|
|
119
|
+
[analytics]
|
|
120
|
+
refresh_interval_seconds = 300
|
|
121
|
+
|
|
116
122
|
[postgres_schemas]
|
|
117
123
|
schema_extension = "extension"
|
|
118
124
|
schema_user = "user"
|
|
@@ -121,6 +127,7 @@ schema_post = "post"
|
|
|
121
127
|
schema_comment = "comment"
|
|
122
128
|
schema_notification = "notification"
|
|
123
129
|
schema_media = "media"
|
|
130
|
+
schema_analytics = "analytics"
|
|
124
131
|
|
|
125
132
|
[redis]
|
|
126
133
|
host = "127.0.0.1"
|
|
@@ -14,18 +14,21 @@ avatar_host_domain = "$AVATAR_HOST_DOMAIN"
|
|
|
14
14
|
default_avatar_image = "$AVATAR_DEFAULT_IMAGE"
|
|
15
15
|
publish_root = "$AVATAR_PUBLISH_ROOT"
|
|
16
16
|
public_path_prefix = "avatar"
|
|
17
|
+
extension = "webp"
|
|
17
18
|
|
|
18
19
|
[banner]
|
|
19
20
|
banner_host_domain = "$BANNER_HOST_DOMAIN"
|
|
20
21
|
default_banner_image = "$BANNER_DEFAULT_IMAGE"
|
|
21
22
|
publish_root = "$BANNER_PUBLISH_ROOT"
|
|
22
23
|
public_path_prefix = "banner"
|
|
24
|
+
extension = "webp"
|
|
23
25
|
|
|
24
26
|
[attachment]
|
|
25
27
|
attachment_host_domain = "$ATTACHMENT_HOST_DOMAIN"
|
|
26
28
|
default_attachment_image = "$ATTACHMENT_DEFAULT_IMAGE"
|
|
27
29
|
publish_root = "$ATTACHMENT_PUBLISH_ROOT"
|
|
28
30
|
public_path_prefix = "attachment"
|
|
31
|
+
extension = "webp"
|
|
29
32
|
|
|
30
33
|
[image_server]
|
|
31
34
|
host = "$IMAGE_SERVER_HOST"
|
|
@@ -97,6 +100,9 @@ max_tries = 2
|
|
|
97
100
|
[retention]
|
|
98
101
|
post_views_retention_days = $RETENTION_POST_VIEWS_DAYS
|
|
99
102
|
|
|
103
|
+
[analytics]
|
|
104
|
+
refresh_interval_seconds = $ANALYTICS_REFRESH_INTERVAL_SECONDS
|
|
105
|
+
|
|
100
106
|
[postgres_schemas]
|
|
101
107
|
schema_extension = "extension"
|
|
102
108
|
schema_user = "user"
|
|
@@ -105,6 +111,7 @@ schema_post = "post"
|
|
|
105
111
|
schema_comment = "comment"
|
|
106
112
|
schema_notification = "notification"
|
|
107
113
|
schema_media = "media"
|
|
114
|
+
schema_analytics = "analytics"
|
|
108
115
|
|
|
109
116
|
[redis]
|
|
110
117
|
host = "$REDIS_HOST"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from typing import Annotated, Any
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, Query
|
|
4
|
+
|
|
5
|
+
from core_framework.api.admin.analytics.schemas import (
|
|
6
|
+
AnalyticsDailyBucketsResponse,
|
|
7
|
+
AnalyticsDailyDayQueryParams,
|
|
8
|
+
AnalyticsDailyRangeQueryParams,
|
|
9
|
+
AnalyticsDailyTotalsResponse,
|
|
10
|
+
)
|
|
11
|
+
from core_framework.application.analytics.read_service import (
|
|
12
|
+
retrieve_analytics_daily_day,
|
|
13
|
+
retrieve_analytics_daily_range,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
router = APIRouter(
|
|
17
|
+
prefix="/analytics",
|
|
18
|
+
tags=["analytics - admin"],
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@router.get("/daily/range", response_model=AnalyticsDailyTotalsResponse)
|
|
23
|
+
async def get_analytics_daily_range(
|
|
24
|
+
query: Annotated[AnalyticsDailyRangeQueryParams, Query()],
|
|
25
|
+
) -> Any:
|
|
26
|
+
return await retrieve_analytics_daily_range(
|
|
27
|
+
subject_type=query.subject_type,
|
|
28
|
+
activity_type=query.activity_type,
|
|
29
|
+
from_day=query.from_day,
|
|
30
|
+
to_day=query.to_day,
|
|
31
|
+
kind=query.kind,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@router.get("/daily/day", response_model=AnalyticsDailyBucketsResponse)
|
|
36
|
+
async def get_analytics_daily_day(
|
|
37
|
+
query: Annotated[AnalyticsDailyDayQueryParams, Query()],
|
|
38
|
+
) -> Any:
|
|
39
|
+
return await retrieve_analytics_daily_day(
|
|
40
|
+
subject_type=query.subject_type,
|
|
41
|
+
activity_type=query.activity_type,
|
|
42
|
+
day=query.day,
|
|
43
|
+
kind=query.kind,
|
|
44
|
+
)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
from typing import Annotated, Self
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
5
|
+
|
|
6
|
+
from core_framework.domains.analytics import DAILY_FACT_RETENTION_DAYS
|
|
7
|
+
|
|
8
|
+
AnalyticsSubjectType = Annotated[str, Field(min_length=1, max_length=128)]
|
|
9
|
+
AnalyticsActivityType = Annotated[str, Field(min_length=1, max_length=128)]
|
|
10
|
+
AnalyticsKind = Annotated[str, Field(max_length=128)]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AnalyticsDailyRangeQueryParams(BaseModel):
|
|
14
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
15
|
+
|
|
16
|
+
subject_type: AnalyticsSubjectType
|
|
17
|
+
activity_type: AnalyticsActivityType
|
|
18
|
+
from_day: Annotated[date, Field(alias="from")]
|
|
19
|
+
to_day: Annotated[date, Field(alias="to")]
|
|
20
|
+
kind: AnalyticsKind | None = None
|
|
21
|
+
|
|
22
|
+
@model_validator(mode="after")
|
|
23
|
+
def validate_day_window(self) -> Self:
|
|
24
|
+
if self.to_day < self.from_day:
|
|
25
|
+
raise ValueError("to must be on or after from")
|
|
26
|
+
inclusive_day_count = (self.to_day - self.from_day).days + 1
|
|
27
|
+
if inclusive_day_count > DAILY_FACT_RETENTION_DAYS:
|
|
28
|
+
raise ValueError(f"range must not exceed {DAILY_FACT_RETENTION_DAYS} days inclusive")
|
|
29
|
+
return self
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AnalyticsDailyDayQueryParams(BaseModel):
|
|
33
|
+
subject_type: AnalyticsSubjectType
|
|
34
|
+
activity_type: AnalyticsActivityType
|
|
35
|
+
day: date
|
|
36
|
+
kind: AnalyticsKind | None = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class AnalyticsDailyDayTotalItem(BaseModel):
|
|
40
|
+
day: date
|
|
41
|
+
count: Annotated[int, Field(ge=0)]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class AnalyticsDailyBucketItem(BaseModel):
|
|
45
|
+
kind: str
|
|
46
|
+
count: Annotated[int, Field(ge=0)]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class AnalyticsDailyTotalsResponse(BaseModel):
|
|
50
|
+
subject_type: str
|
|
51
|
+
activity_type: str
|
|
52
|
+
items: list[AnalyticsDailyDayTotalItem]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class AnalyticsDailyBucketsResponse(BaseModel):
|
|
56
|
+
subject_type: str
|
|
57
|
+
activity_type: str
|
|
58
|
+
day: date
|
|
59
|
+
items: list[AnalyticsDailyBucketItem]
|
|
@@ -2,7 +2,7 @@ from datetime import datetime
|
|
|
2
2
|
from typing import Annotated, Any
|
|
3
3
|
|
|
4
4
|
from fastapi import Path, Query
|
|
5
|
-
from pydantic import BaseModel
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
6
6
|
|
|
7
7
|
OutboxRowId = Annotated[
|
|
8
8
|
int,
|
|
@@ -34,7 +34,9 @@ class OutboxOldestPendingRow(BaseModel):
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
class OutboxSummaryResponse(BaseModel):
|
|
37
|
-
|
|
37
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
38
|
+
|
|
39
|
+
producing_schema: str = Field(alias="schema")
|
|
38
40
|
total_pending: int
|
|
39
41
|
total_dead: int
|
|
40
42
|
pending_by_event: list[OutboxEventCountItem]
|
|
@@ -43,8 +45,10 @@ class OutboxSummaryResponse(BaseModel):
|
|
|
43
45
|
|
|
44
46
|
|
|
45
47
|
class OutboxDeadRowResponse(BaseModel):
|
|
48
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
49
|
+
|
|
46
50
|
id: int
|
|
47
|
-
|
|
51
|
+
producing_schema: str = Field(alias="schema")
|
|
48
52
|
event_name: str
|
|
49
53
|
aggregate_type: str
|
|
50
54
|
aggregate_id: str
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from fastapi import APIRouter, Depends
|
|
2
2
|
|
|
3
|
+
from core_framework.api.admin.analytics.router import router as analytics_router
|
|
3
4
|
from core_framework.api.admin.cache.router import router as cache_router
|
|
4
5
|
from core_framework.api.admin.comments.router import router as comments_router
|
|
5
6
|
from core_framework.api.admin.moderation.router import router as moderation_router
|
|
@@ -14,6 +15,7 @@ router = APIRouter(
|
|
|
14
15
|
)
|
|
15
16
|
|
|
16
17
|
router.include_router(cache_router)
|
|
18
|
+
router.include_router(analytics_router)
|
|
17
19
|
router.include_router(outbox_router)
|
|
18
20
|
router.include_router(users_router)
|
|
19
21
|
router.include_router(moderation_router)
|