core-framework 1.7.0__tar.gz → 1.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/application-layer.mdc +4 -0
- core_framework-1.8.0/.cursor/rules/docstrings.mdc +81 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/domain-services-and-adapters.mdc +4 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/skills/code-review/SKILL.md +2 -1
- {core_framework-1.7.0 → core_framework-1.8.0}/CHANGELOG.md +20 -1
- {core_framework-1.7.0 → core_framework-1.8.0}/PKG-INFO +2 -1
- core_framework-1.8.0/alembic/comment/alembic/versions/comment_add_comment_mentions.py +36 -0
- core_framework-1.8.0/alembic/post/alembic/versions/post_add_post_mentions.py +36 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/config.toml +2 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/config.toml.template +2 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/admin/users/schemas.py +2 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/comments/authenticated/mappers.py +11 -1
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/comments/authenticated/schemas.py +8 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/comments/public/schemas.py +1 -0
- core_framework-1.8.0/core_framework/api/posts/authenticated/mappers.py +28 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/posts/authenticated/schemas.py +8 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/posts/public/schemas.py +1 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/users/authenticated/router.py +11 -5
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/users/authenticated/schemas.py +2 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/users/public/router.py +13 -3
- core_framework-1.8.0/core_framework/api/users/public/schemas.py +33 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/auth/access_service.py +2 -7
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/auth/auth_service.py +1 -8
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/bootstrap.py +1 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/cache/invalidate_service.py +4 -20
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/comments/admin_service.py +8 -30
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/comments/authenticated_service.py +16 -38
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/comments/public_service.py +65 -56
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/events/event_service.py +4 -13
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/events/event_token.py +3 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/media/avatar_service.py +10 -37
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/media/banner_service.py +1 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/moderation/appeal_service.py +3 -24
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/moderation/moderator_service.py +2 -9
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/moderation/report_service.py +2 -11
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/moderation/scheduled_service.py +2 -4
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/moderation/user_service.py +12 -70
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/notifications/inbox_service.py +4 -27
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/notifications/mute_service.py +1 -9
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/notifications/notification_service.py +2 -12
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/posts/admin_service.py +8 -22
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/posts/authenticated_service.py +8 -21
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/posts/public_service.py +83 -26
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/shared/user_agent.py +1 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/shared/worker_jobs.py +2 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/users/admin_service.py +30 -88
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/users/authenticated_service.py +38 -40
- core_framework-1.8.0/core_framework/application/users/public_service.py +155 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/cache.py +8 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/settings.py +14 -2
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/comment/README.md +18 -1
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/comment/constants.py +2 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/comment/dependencies.py +27 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/comment/models.py +32 -2
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/comment/repository.py +461 -7
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/comment/service.py +185 -25
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/__init__.py +4 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/avatar/components/staging.py +9 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/avatar/components/variant_encoder.py +15 -6
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/avatar/constants.py +2 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/avatar/exceptions.py +5 -1
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/avatar/ports/variant_encoder.py +2 -2
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/banner/components/finals_publisher.py +13 -2
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/banner/components/staging.py +40 -4
- core_framework-1.8.0/core_framework/domains/media/banner/components/variant_encoder.py +32 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/banner/constants.py +2 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/banner/exceptions.py +5 -1
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/banner/ports/variant_encoder.py +2 -2
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/banner/service.py +141 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/dependencies.py +101 -1
- core_framework-1.8.0/core_framework/domains/media/shared/constants.py +4 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/shared/exceptions.py +4 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/shared/repository.py +176 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/shared/utils.py +22 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/moderation/dependencies.py +27 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/moderation/repository.py +469 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/moderation/service.py +253 -29
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/notification/README.md +1 -1
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/notification/dependencies.py +27 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/notification/repository.py +163 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/notification/service.py +55 -7
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/post/README.md +11 -7
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/post/constants.py +1 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/post/dependencies.py +27 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/post/models.py +37 -1
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/post/repository.py +443 -11
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/post/service.py +146 -16
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/user/dependencies.py +27 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/user/models.py +69 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/user/repository.py +519 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/user/service.py +325 -34
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/user/utils.py +15 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/utils.py +81 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/infrastructure/media/local_banner_staging_adapter.py +28 -0
- core_framework-1.8.0/core_framework/infrastructure/media/pillow_avatar_variant_encoder_adapter.py +50 -0
- core_framework-1.8.0/core_framework/infrastructure/media/pillow_banner_variant_encoder_adapter.py +33 -0
- core_framework-1.8.0/core_framework/infrastructure/media/pillow_staging_image.py +72 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/infrastructure/media/ssh_banner_finals_publisher_adapter.py +19 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/deployments/README.md +1 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/deployments/deploy-playbook.md +6 -0
- core_framework-1.8.0/docs/deployments/release-playbook.md +84 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/README.md +12 -12
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/cache/admin_cache_invalidation.md +8 -6
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/comments/admin_comments.md +2 -1
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/comments/create_comment.md +20 -5
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/comments/edit_comment.md +12 -8
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/comments/retrieve_comments.md +5 -5
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/media/avatar-upload-design.md +9 -8
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/media/banner-upload-design.md +1 -1
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/media/upload-pipeline-design.md +9 -9
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/mentions/mentions_in_content.md +74 -14
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/posts/admin_posts.md +1 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/posts/author_context.md +3 -1
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/posts/hashtag_discovery.md +1 -1
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/users/avatar.md +7 -7
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/users/banner.md +3 -3
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/users/follow.md +1 -1
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/users/profile.md +12 -5
- core_framework-1.8.0/docs/domains/users/public_profile.md +97 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/library/api.md +1 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/platform/domain-services-and-adapters.md +10 -8
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/platform/patch-update-typed-payload.md +3 -1
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/todo.md +1 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/pyproject.toml +2 -1
- core_framework-1.8.0/tests/integration/api/mentions/mentions_integration_test.py +348 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/users/authenticated/avatar_upload_test.py +35 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/users/authenticated/banner_upload_test.py +35 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/users/authenticated/router_test.py +28 -7
- core_framework-1.8.0/tests/integration/api/users/public/router_test.py +331 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/worker/avatar_upload_test.py +67 -2
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/worker/banner_upload_test.py +67 -2
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/worker/utils_test.py +14 -1
- {core_framework-1.7.0 → core_framework-1.8.0}/uv.lock +28 -1
- core_framework-1.7.0/core_framework/api/posts/authenticated/mappers.py +0 -14
- core_framework-1.7.0/core_framework/api/users/public/schemas.py +0 -7
- core_framework-1.7.0/core_framework/application/users/public_service.py +0 -9
- core_framework-1.7.0/core_framework/domains/media/banner/components/variant_encoder.py +0 -16
- core_framework-1.7.0/core_framework/infrastructure/media/pillow_banner_variant_encoder_adapter.py +0 -36
- core_framework-1.7.0/core_framework/infrastructure/media/pillow_variant_encoder_adapter.py +0 -61
- core_framework-1.7.0/tests/integration/api/users/public/router_test.py +0 -49
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/api-layer.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/api-reference-docs.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/api-security.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/api-validation.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/code-review-output.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/constants-final.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/database-triggers.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/domain-caller-context.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/domain-imports.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/domain-input-guards.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/domain-repository-exceptions.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/exception-handlers.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/flow-documentation.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/henry-cursor-rules-sync.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/implementation-workflow.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/integration-test-strategy.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/karpathy-guidelines.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/layer-boundaries.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/no-code-in-docs.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/postgres-config-conventions.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/repository-read-consistency.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/strong-read-opt-in.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/structured-logging.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/tech-stack.mdc +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/skills/add-config/SKILL.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/skills/add-domain/SKILL.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.cursor/skills/recommend-features/SKILL.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.dockerignore +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.github/workflows/_deploy.yml +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.github/workflows/dev-ci-cd.yaml +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.github/workflows/manual-deployment.yaml +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.github/workflows/publish-pypi.yml +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.github/workflows/test.yaml +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.gitignore +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.pre-commit-config.yaml +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/.python-version +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/LICENSE +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/README.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/comment/alembic/README +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/comment/alembic/env.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/comment/alembic/script.py.mako +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/comment/alembic/versions/v1_comment_init_baseline.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/comment/alembic.ini +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/extension/alembic/README +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/extension/alembic/env.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/extension/alembic/script.py.mako +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/extension/alembic/versions/v1_ext_init_baseline.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/extension/alembic.ini +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/media/alembic/README +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/media/alembic/env.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/media/alembic/script.py.mako +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/media/alembic/versions/media_add_banner_staging_and_lease.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/media/alembic/versions/v1_media_init_baseline.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/media/alembic.ini +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/moderation/alembic/README +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/moderation/alembic/env.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/moderation/alembic/script.py.mako +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/moderation/alembic/versions/v1_mod_init_baseline.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/moderation/alembic.ini +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/notification/alembic/README +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/notification/alembic/env.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/notification/alembic/script.py.mako +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/notification/alembic/versions/v1_notif_init_baseline.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/notification/alembic.ini +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/post/alembic/README +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/post/alembic/env.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/post/alembic/script.py.mako +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/post/alembic/versions/v1_post_init_baseline.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/post/alembic.ini +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/user/alembic/README +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/user/alembic/env.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/user/alembic/script.py.mako +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/user/alembic/versions/user_add_avatar_ingest_sequences.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/user/alembic/versions/user_add_banner_ingest_sequences.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/user/alembic/versions/user_add_date_of_birth_to_profiles.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/user/alembic/versions/v1_user_init_baseline.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/alembic/user/alembic.ini +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/admin/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/admin/cache/router.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/admin/cache/schemas.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/admin/comments/router.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/admin/comments/schemas.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/admin/moderation/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/admin/moderation/router.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/admin/moderation/schemas.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/admin/posts/router.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/admin/posts/schemas.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/admin/router.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/admin/users/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/admin/users/router.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/auth/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/auth/router.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/auth/schemas.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/comments/authenticated/router.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/comments/public/router.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/comments/router.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/comments/schemas.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/constants.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/dependencies.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/events/router.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/events/schemas.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/notifications/authenticated/router.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/notifications/authenticated/schemas.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/notifications/router.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/posts/authenticated/router.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/posts/public/router.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/posts/router.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/posts/schemas.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/router.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/schemas.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/system/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/system/router.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/users/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/users/authenticated/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/users/authenticated/mappers.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/users/mappers.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/users/public/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/users/router.py +1 -1
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/users/shared/schemas.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/auth/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/auth/models.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/cache/models.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/comments/aggregation_service.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/events/README.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/events/models.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/media/README.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/moderation/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/notifications/README.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/notifications/enums.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/posts/aggregation_service.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/shared/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/shared/enums.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/shared/exceptions.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/users/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/users/aggregation_service.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/users/scheduled_service.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/asgi.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/bundled_alembic.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/constants.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/context.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/database.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/exception_handlers/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/exception_handlers/comment.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/exception_handlers/common.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/exception_handlers/media.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/exception_handlers/moderation.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/exception_handlers/notification.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/exception_handlers/post.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/exception_handlers/setup.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/exception_handlers/user.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/firebase.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/http_client.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/logging.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/middleware.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/observability.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/pagination.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/redis.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/core/runtime.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/comment/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/comment/enums.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/comment/exceptions.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/exceptions.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/README.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/avatar/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/avatar/components/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/avatar/components/finals_publisher.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/avatar/models.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/avatar/ports/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/avatar/ports/finals_publisher.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/avatar/ports/staging.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/avatar/service.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/banner/models.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/banner/ports/finals_publisher.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/banner/ports/staging.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/shared/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/media/shared/service.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/moderation/README.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/moderation/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/moderation/enums.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/moderation/exceptions.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/moderation/models.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/notification/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/notification/enums.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/notification/exceptions.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/notification/models.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/post/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/post/enums.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/post/exceptions.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/user/README.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/user/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/user/constants.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/user/enums.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/domains/user/exceptions.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/infrastructure/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/infrastructure/media/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/infrastructure/media/local_staging_adapter.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/infrastructure/media/ssh_finals_publisher_adapter.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/main.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/migrate_cli.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/testing/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/testing/arq.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/testing/auth.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/testing/config.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/testing/containers.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/testing/firebase.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/testing/hookspecs.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/testing/httpx_test_client.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/testing/media.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/testing/migrations.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/testing/plugin.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/main.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/schedules/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/schedules/schedule_aggregate_comment_stats.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/schedules/schedule_aggregate_post_stats.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/schedules/schedule_aggregate_user_stats.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/schedules/schedule_expired_account_deletions.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/schedules/schedule_expired_mute_lifts.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/schedules/schedule_sweep_stale_avatar_staging.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/schedules/schedule_sweep_stale_banner_staging.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/tasks/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/tasks/delete_superseded_avatar_finals.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/tasks/delete_superseded_banner_finals.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/tasks/process_account_deletion.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/tasks/process_aggregate_comment_stats.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/tasks/process_aggregate_post_stats.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/tasks/process_aggregate_user_stats.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/tasks/process_banner_asset_variants.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/tasks/process_cache_pattern_invalidation.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/tasks/process_media_asset_variants.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/tasks/process_mute_lift.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/core_framework/worker/worker_context.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docker-compose.dev.yaml +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docker-compose.yml +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/dockerfile +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/README.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/deployments/edge-upload-limits.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/deployments/examples/Caddyfile.example +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/deployments/examples/image-server-known-hosts.example +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/deployments/guides/app-server-deploy-ssh.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/deployments/guides/image-server-ssh.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/deployments/guides/postgres-setup.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/deployments/guides/redis-setup.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/deployments/guides/ubuntu-server-setup.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/auth/access_control.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/auth/registration.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/comments/comment_report.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/comments/comment_stats_aggregation.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/comments/delete_comment.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/events/events.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/media/upload_pipeline.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/moderation/appeals.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/moderation/internal_notes.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/moderation/moderator_actions.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/moderation/reports.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/moderation/restrictions.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/notifications/notification_inbox.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/posts/post_like.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/posts/post_stats_aggregation.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/posts/post_visibility.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/users/account.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/users/account_deletion.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/users/blocks.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/users/change_history.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/users/check_username_exists.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/users/follow-system-design.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/users/my_posts_and_comments.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/users/preferences.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/domains/users/user_removal.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/library/README.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/library/core-framework-migration.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/library/overview.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/library/package-api.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/library/testing-plugin-design.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/platform/README.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/platform/architecture-decisions.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/platform/conventions.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/platform/database-triggers.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/platform/event-outbox-design.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/docs/platform/layers-and-boundaries.md +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/firebase_config.example.json +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/makefile +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/conftest.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/_http_helpers.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/admin/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/admin/cache/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/admin/cache/router_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/admin/comments/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/admin/comments/router_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/admin/moderation/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/admin/moderation/router_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/admin/posts/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/admin/posts/router_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/admin/users/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/admin/users/router_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/auth/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/auth/router_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/comments/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/comments/authenticated/comment_writes_integration_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/comments/public/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/comments/public/router_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/events/router_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/notifications/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/notifications/router_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/posts/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/posts/authenticated/post_writes_integration_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/posts/comment_count_aggregation_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/posts/followers_visibility_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/posts/post_stats_dirty_marking_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/posts/public/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/posts/public/router_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/system/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/system/router_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/users/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/users/authenticated/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/api/users/public/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/worker/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/worker/account_deletion_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/worker/aggregate_comment_stats_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/worker/aggregate_post_stats_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/worker/aggregate_user_stats_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/worker/conftest.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/integration/worker/mute_lift_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/unit/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/unit/application/comments/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/unit/application/events/event_service_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/unit/application/notifications/inbox_service_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/unit/core/bundled_alembic_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/unit/core/migrate_cli_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/unit/core/pagination_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/unit/domains/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/unit/domains/comment/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/unit/domains/user/service_test.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/unit/infrastructure/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/unit/infrastructure/media/__init__.py +0 -0
- {core_framework-1.7.0 → core_framework-1.8.0}/tests/unit/infrastructure/media/ssh_finals_publisher_adapter_test.py +0 -0
|
@@ -77,3 +77,7 @@ async def retrieve_my_username_suggestions(...) -> list[UsernameSuggestion]: ...
|
|
|
77
77
|
- Input validation (`validate_user_id`, `validate_report_id`, etc.) belongs in schemas
|
|
78
78
|
- Services do NOT raise `RequestValidationError` or import from `fastapi.exceptions`
|
|
79
79
|
- Routers call `validate_*` from schemas before invoking services
|
|
80
|
+
|
|
81
|
+
## Docstrings
|
|
82
|
+
|
|
83
|
+
Application functions get a **one-line** docstring (what it does). No `Args` / `Returns` / `Raises` — see **`docstrings.mdc`**.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Docstring depth by layer — one-line in application, full Args/Returns/Raises in domain and infrastructure
|
|
3
|
+
globs:
|
|
4
|
+
- core_framework/application/**/*.py
|
|
5
|
+
- core_framework/domains/**/*.py
|
|
6
|
+
- core_framework/infrastructure/**/*.py
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Docstrings by layer
|
|
10
|
+
|
|
11
|
+
Docstring **depth** depends on which layer owns the symbol. Type hints and keyword-only signatures carry parameter detail in application code; domain and infrastructure document the contract at the boundary.
|
|
12
|
+
|
|
13
|
+
## Application layer (`core_framework/application/`)
|
|
14
|
+
|
|
15
|
+
**One short sentence** — what the function does. No `Args`, `Returns`, or `Raises` sections.
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
# ✅ Preferred
|
|
19
|
+
async def enqueue_delete_superseded_banner_finals(*, asset_id: str) -> None:
|
|
20
|
+
"""Enqueue cleanup of published finals for a replaced banner asset."""
|
|
21
|
+
...
|
|
22
|
+
|
|
23
|
+
# ❌ Avoid — too heavy for application orchestration
|
|
24
|
+
async def enqueue_delete_superseded_banner_finals(...):
|
|
25
|
+
"""Enqueue cleanup of published finals for a replaced banner asset.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
asset_id: ...
|
|
29
|
+
Returns:
|
|
30
|
+
...
|
|
31
|
+
"""
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Applies to **public and private** helpers in application modules. When you touch a function in an application file, use the simple style.
|
|
35
|
+
|
|
36
|
+
## Domain layer (`core_framework/domains/`)
|
|
37
|
+
|
|
38
|
+
**Full docstring** on public service, repository, component, and port methods that implement or define behavior:
|
|
39
|
+
|
|
40
|
+
- Opening summary (one or two sentences)
|
|
41
|
+
- **`Args:`** — keyword-only parameters and meaning
|
|
42
|
+
- **`Returns:`** — when the return value is not obvious from the type alone
|
|
43
|
+
- **`Raises:`** — domain exceptions and validation errors callers should handle
|
|
44
|
+
|
|
45
|
+
Skip `Raises` only when the method truly cannot raise beyond programming errors. Trivial private helpers may stay undocumented; **exported and orchestration paths** get the full treatment.
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
# ✅ Preferred (domain service)
|
|
49
|
+
async def insert_avatar_staging_registry(...) -> AvatarStagingRegistryEntry:
|
|
50
|
+
"""Record a new avatar staging registry row before blob upload completes.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
asset_id: Unique avatar asset identifier.
|
|
54
|
+
owner_user_id: User who owns the avatar.
|
|
55
|
+
staging_filename: Basename of the staging blob on disk.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Persisted registry entry including staging metadata.
|
|
59
|
+
|
|
60
|
+
Raises:
|
|
61
|
+
DomainValidationError: If required string fields are blank or ``staging_filename``
|
|
62
|
+
is unsafe.
|
|
63
|
+
"""
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Repositories: document query semantics (filtering, empty results, strong vs normal read) in the summary or **`Returns:`** when non-obvious.
|
|
67
|
+
|
|
68
|
+
## Infrastructure layer (`core_framework/infrastructure/`)
|
|
69
|
+
|
|
70
|
+
Same as **domain** — adapters implement ports and perform I/O. Document inputs, outputs, failure modes (`Raises:` or raised infrastructure exceptions), and non-obvious side effects (network, disk, SSH).
|
|
71
|
+
|
|
72
|
+
## When editing
|
|
73
|
+
|
|
74
|
+
- **New** application functions → simple docstring.
|
|
75
|
+
- **New** domain/infrastructure public methods → full docstring.
|
|
76
|
+
- When you **change** a function in a file, bring **that function's** docstring in line with its layer; do not repo-wide migrate unrelated symbols unless the task asks for it.
|
|
77
|
+
|
|
78
|
+
## Out of scope
|
|
79
|
+
|
|
80
|
+
- **API routers and schemas** — no docstring requirement from this rule (OpenAPI and Pydantic carry the contract).
|
|
81
|
+
- **Workers** (`core_framework/worker/`) — thin task/schedule wrappers follow **application** style; logic belongs in application services.
|
{core_framework-1.7.0 → core_framework-1.8.0}/.cursor/rules/domain-services-and-adapters.mdc
RENAMED
|
@@ -45,3 +45,7 @@ See **`docs/platform/domain-services-and-adapters.md`** for the full model. Appl
|
|
|
45
45
|
- **Layout:** `domains/media/shared/`, `avatar/ports/`, `avatar/components/`, `AvatarService`.
|
|
46
46
|
- **Avatar:** `media_deps.avatar_service` only (not individual components).
|
|
47
47
|
- **Cross-domain:** `avatar_ingest_sequence` / profile `avatar_id` via **user** domain; orchestration in `application/media/avatar_service`.
|
|
48
|
+
|
|
49
|
+
## Docstrings
|
|
50
|
+
|
|
51
|
+
Domain services, repositories, ports, and components use **full** docstrings (`Args`, `Returns`, `Raises` where applicable). Application orchestration uses **one-line** summaries — see **`docstrings.mdc`**.
|
|
@@ -39,7 +39,7 @@ Read these files **before** proceeding with the review:
|
|
|
39
39
|
1. **`docs/platform/domain-services-and-adapters.md`** – Application orchestration vs domain services, ports/adapters, multiple collaborators per bounded context
|
|
40
40
|
1. **`docs/platform/conventions.md`** – Coding conventions, domain rules, caching, Redis, exception handling, API layer rules
|
|
41
41
|
1. **`docs/platform/architecture-decisions.md`** – Intentional design choices; do not flag these as issues
|
|
42
|
-
1. **`.cursor/rules/api-layer.mdc`**, **`.cursor/rules/application-layer.mdc`**, **`.cursor/rules/domain-services-and-adapters.mdc`**, **`.cursor/rules/domain-imports.mdc`**, **`.cursor/rules/domain-caller-context.mdc`** – Apply when reviewing API/application/domain code
|
|
42
|
+
1. **`.cursor/rules/api-layer.mdc`**, **`.cursor/rules/application-layer.mdc`**, **`.cursor/rules/docstrings.mdc`**, **`.cursor/rules/domain-services-and-adapters.mdc`**, **`.cursor/rules/domain-imports.mdc`**, **`.cursor/rules/domain-caller-context.mdc`** – Apply when reviewing API/application/domain code
|
|
43
43
|
|
|
44
44
|
## Step 2: Key Context
|
|
45
45
|
|
|
@@ -76,6 +76,7 @@ Verify the following. Also apply api-layer, application-layer, and domain-import
|
|
|
76
76
|
|
|
77
77
|
### Application Layer Services (`core_framework/application/**/*_service.py`)
|
|
78
78
|
|
|
79
|
+
- [ ] **Docstrings** – One-line summary only; no `Args`/`Returns`/`Raises` (see `docstrings.mdc`)
|
|
79
80
|
- [ ] **No HTTP handling** – No `HTTPException`, `RequestValidationError`, `BackgroundTasks`, or other FastAPI HTTP imports
|
|
80
81
|
- [ ] **No Pydantic return types** – Services return `dict`, `list[dict]`, dataclasses, or plain types; routers use `response_model`
|
|
81
82
|
- [ ] **Not found** – Return `None` or `Optional`; routers raise `HTTPException(404)` when appropriate
|
|
@@ -4,6 +4,24 @@ Notable changes to **core-framework** (import **`core_framework`**). Format foll
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.8.0] - 2026-06-06
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`[avatar].public_path_prefix`** and **`[banner].public_path_prefix`** — optional URL path segment in **`config.toml`** when avatar/banner finals are served under a subdirectory on a shared media host. Empty by default (dedicated product vhost docroot).
|
|
12
|
+
|
|
13
|
+
- **Follow counts on profile surfaces:** **`GET /users/me/profile`**, **`PATCH /users/me/profile`**, and admin **`GET /admin/users/{user_id}`** profile payload include **`follower_count`** and **`following_count`** (same **user_stats** source as public profile). Follow/unfollow invalidates **`user_stats`** cache for both parties when the edge changes.
|
|
14
|
+
|
|
15
|
+
- **HEIC/HEIF ingest (avatar and banner):** Allow **`image/heic`** and **`image/heif`** on profile image uploads; decode via **`pillow-heif`** before WebP variant encoding.
|
|
16
|
+
|
|
17
|
+
- **Image ingest hardening:** Staging decode uses a **30s** timeout, **4096×4096** pixel cap, and Pillow decompression-bomb limits; published WebP finals omit embedded metadata.
|
|
18
|
+
|
|
19
|
+
- **Structured mentions (posts and comments):** **`post_mentions`** / **`comment_mentions`** persistence; **`mentions`** on **POST** create, **PATCH** replace (omit unchanged, **`[]`** clears), and **GET** responses as **`UserReference`** list. **`mention` inbox notifications** remain **create-only**. Migrations **`post_add_post_mentions`**, **`comment_add_comment_mentions`**. See **`docs/domains/mentions/mentions_in_content.md`**.
|
|
20
|
+
|
|
21
|
+
### Database
|
|
22
|
+
|
|
23
|
+
- **Post** migration **`post_add_post_mentions`**; **comment** migration **`comment_add_comment_mentions`**. Run **`uv run cf-alembic`** before deploying.
|
|
24
|
+
|
|
7
25
|
## [1.7.0] - 2026-06-01
|
|
8
26
|
|
|
9
27
|
### Added
|
|
@@ -233,4 +251,5 @@ First **SemVer-stable** release per **`docs/library/package-api.md`**.
|
|
|
233
251
|
[1.5.0]: https://github.com/NepNepFFXIV/core-framework/compare/v1.4.0...v1.5.0
|
|
234
252
|
[1.6.0]: https://github.com/NepNepFFXIV/core-framework/compare/v1.5.0...v1.6.0
|
|
235
253
|
[1.7.0]: https://github.com/NepNepFFXIV/core-framework/compare/v1.6.0...v1.7.0
|
|
236
|
-
[
|
|
254
|
+
[1.8.0]: https://github.com/NepNepFFXIV/core-framework/compare/v1.7.0...v1.8.0
|
|
255
|
+
[unreleased]: https://github.com/NepNepFFXIV/core-framework/compare/v1.8.0...HEAD
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: core-framework
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.0
|
|
4
4
|
Summary: Core framework package (import as core_framework)
|
|
5
5
|
Project-URL: Homepage, https://github.com/NepNepFFXIV/core-framework
|
|
6
6
|
Project-URL: Repository, https://github.com/NepNepFFXIV/core-framework
|
|
@@ -24,6 +24,7 @@ Requires-Dist: itsdangerous>=2.2.0
|
|
|
24
24
|
Requires-Dist: logfire[asyncpg,fastapi,httpx,redis]>=4.32.1
|
|
25
25
|
Requires-Dist: mashumaro[orjson]>=3.20
|
|
26
26
|
Requires-Dist: orjson>=3.11.7
|
|
27
|
+
Requires-Dist: pillow-heif>=1.3.0
|
|
27
28
|
Requires-Dist: pillow>=12.2.0
|
|
28
29
|
Requires-Dist: python-ulid>=3.1.0
|
|
29
30
|
Requires-Dist: structlog>=25.5.0
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""add comment_mentions for structured @mentions on comments
|
|
2
|
+
|
|
3
|
+
Revision ID: comment_add_comment_mentions
|
|
4
|
+
Revises: v1_comment_init
|
|
5
|
+
Create Date: 2026-06-03 12:00:00.000000
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
from alembic import op
|
|
12
|
+
|
|
13
|
+
revision: str = "comment_add_comment_mentions"
|
|
14
|
+
down_revision: str | Sequence[str] | None = "v1_comment_init"
|
|
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 comment_mentions (
|
|
22
|
+
comment_id varchar(26) not null references comments(id) on delete cascade,
|
|
23
|
+
mentioned_user_id varchar(128) not null,
|
|
24
|
+
created_at timestamptz not null default now(),
|
|
25
|
+
primary key (comment_id, mentioned_user_id)
|
|
26
|
+
);
|
|
27
|
+
""")
|
|
28
|
+
|
|
29
|
+
op.execute("""
|
|
30
|
+
create index idx_comment_mentions_mentioned_user_id
|
|
31
|
+
on comment_mentions (mentioned_user_id);
|
|
32
|
+
""")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def downgrade() -> None:
|
|
36
|
+
pass
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""add post_mentions for structured @mentions on posts
|
|
2
|
+
|
|
3
|
+
Revision ID: post_add_post_mentions
|
|
4
|
+
Revises: v1_post_init
|
|
5
|
+
Create Date: 2026-06-03 12:00:00.000000
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from collections.abc import Sequence
|
|
10
|
+
|
|
11
|
+
from alembic import op
|
|
12
|
+
|
|
13
|
+
revision: str = "post_add_post_mentions"
|
|
14
|
+
down_revision: str | Sequence[str] | None = "v1_post_init"
|
|
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 post_mentions (
|
|
22
|
+
post_id varchar(26) not null references posts(id) on delete cascade,
|
|
23
|
+
mentioned_user_id varchar(128) not null,
|
|
24
|
+
created_at timestamptz not null default now(),
|
|
25
|
+
primary key (post_id, mentioned_user_id)
|
|
26
|
+
);
|
|
27
|
+
""")
|
|
28
|
+
|
|
29
|
+
op.execute("""
|
|
30
|
+
create index idx_post_mentions_mentioned_user_id
|
|
31
|
+
on post_mentions (mentioned_user_id);
|
|
32
|
+
""")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def downgrade() -> None:
|
|
36
|
+
pass
|
|
@@ -18,11 +18,13 @@ staging_root = "media-staging"
|
|
|
18
18
|
avatar_host_domain = "avatar.core-framework.com"
|
|
19
19
|
default_avatar_image = "default.webp"
|
|
20
20
|
publish_root = "/var/www/sites/avatar"
|
|
21
|
+
public_path_prefix = "avatar"
|
|
21
22
|
|
|
22
23
|
[banner]
|
|
23
24
|
banner_host_domain = "banner.core-framework.com"
|
|
24
25
|
default_banner_image = "default.webp"
|
|
25
26
|
publish_root = "/var/www/sites/banner"
|
|
27
|
+
public_path_prefix = "banner"
|
|
26
28
|
|
|
27
29
|
[image_server]
|
|
28
30
|
host = "127.0.0.1"
|
|
@@ -13,11 +13,13 @@ staging_root = "/app/media-staging"
|
|
|
13
13
|
avatar_host_domain = "$AVATAR_HOST_DOMAIN"
|
|
14
14
|
default_avatar_image = "$AVATAR_DEFAULT_IMAGE"
|
|
15
15
|
publish_root = "$AVATAR_PUBLISH_ROOT"
|
|
16
|
+
public_path_prefix = "avatar"
|
|
16
17
|
|
|
17
18
|
[banner]
|
|
18
19
|
banner_host_domain = "$BANNER_HOST_DOMAIN"
|
|
19
20
|
default_banner_image = "$BANNER_DEFAULT_IMAGE"
|
|
20
21
|
publish_root = "$BANNER_PUBLISH_ROOT"
|
|
22
|
+
public_path_prefix = "banner"
|
|
21
23
|
|
|
22
24
|
[image_server]
|
|
23
25
|
host = "$IMAGE_SERVER_HOST"
|
|
@@ -80,6 +80,8 @@ class AdminProfileResponse(AvatarMixin, BannerMixin):
|
|
|
80
80
|
social_links: Annotated[dict[str, str], Field(default_factory=dict)]
|
|
81
81
|
profile_visibility: ProfileVisibility
|
|
82
82
|
date_of_birth: DateOfBirth | None
|
|
83
|
+
follower_count: Annotated[int, Field(ge=0)] = 0
|
|
84
|
+
following_count: Annotated[int, Field(ge=0)] = 0
|
|
83
85
|
|
|
84
86
|
|
|
85
87
|
class UserDetailResponse(BaseModel):
|
{core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/comments/authenticated/mappers.py
RENAMED
|
@@ -4,7 +4,17 @@ from core_framework.domains.comment import CommentUpdate
|
|
|
4
4
|
|
|
5
5
|
def comment_update_from_request(*, request: UpdateCommentRequest) -> CommentUpdate:
|
|
6
6
|
fields_set = frozenset(request.model_fields_set)
|
|
7
|
+
|
|
8
|
+
content = None
|
|
9
|
+
if "content" in fields_set and request.content is not None:
|
|
10
|
+
content = request.content.root
|
|
11
|
+
|
|
12
|
+
mentions = None
|
|
13
|
+
if "mentions" in fields_set:
|
|
14
|
+
mentions = None if request.mentions is None else [mention.root for mention in request.mentions]
|
|
15
|
+
|
|
7
16
|
return CommentUpdate(
|
|
8
17
|
fields_set=fields_set,
|
|
9
|
-
content=
|
|
18
|
+
content=content,
|
|
19
|
+
mentions=mentions,
|
|
10
20
|
)
|
{core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/comments/authenticated/schemas.py
RENAMED
|
@@ -12,6 +12,14 @@ from core_framework.domains.moderation import ReportCategory
|
|
|
12
12
|
|
|
13
13
|
class UpdateCommentRequest(BasePatchRequest):
|
|
14
14
|
content: CommentContent | None = None
|
|
15
|
+
mentions: Annotated[list[UserID], Field(max_length=MAX_MENTIONS_PER_REQUEST)] | None = None
|
|
16
|
+
|
|
17
|
+
@field_validator("mentions", mode="after")
|
|
18
|
+
@classmethod
|
|
19
|
+
def dedupe_mentions(cls, v: list[UserID] | None) -> list[UserID] | None:
|
|
20
|
+
if v is None:
|
|
21
|
+
return None
|
|
22
|
+
return dedupe_mention_user_ids(v)
|
|
15
23
|
|
|
16
24
|
|
|
17
25
|
class CommentReportRequest(BaseModel):
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from core_framework.api.posts.authenticated.schemas import UpdatePostRequest
|
|
2
|
+
from core_framework.domains.post import PostUpdate
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def post_update_from_request(*, request: UpdatePostRequest) -> PostUpdate:
|
|
6
|
+
fields_set = frozenset(request.model_fields_set)
|
|
7
|
+
|
|
8
|
+
content = None
|
|
9
|
+
if "content" in fields_set and request.content is not None:
|
|
10
|
+
content = request.content.root
|
|
11
|
+
|
|
12
|
+
visibility = request.visibility if "visibility" in fields_set else None
|
|
13
|
+
|
|
14
|
+
hashtags = None
|
|
15
|
+
if "hashtags" in fields_set:
|
|
16
|
+
hashtags = None if request.hashtags is None else [hashtag.root for hashtag in request.hashtags]
|
|
17
|
+
|
|
18
|
+
mentions = None
|
|
19
|
+
if "mentions" in fields_set:
|
|
20
|
+
mentions = None if request.mentions is None else [mention.root for mention in request.mentions]
|
|
21
|
+
|
|
22
|
+
return PostUpdate(
|
|
23
|
+
fields_set=fields_set,
|
|
24
|
+
content=content,
|
|
25
|
+
visibility=visibility,
|
|
26
|
+
hashtags=hashtags,
|
|
27
|
+
mentions=mentions,
|
|
28
|
+
)
|
{core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/posts/authenticated/schemas.py
RENAMED
|
@@ -37,6 +37,7 @@ class UpdatePostRequest(BasePatchRequest):
|
|
|
37
37
|
content: PostContent | None = None
|
|
38
38
|
visibility: PostVisibility | None = None
|
|
39
39
|
hashtags: Annotated[list[Hashtag], Field(max_length=MAX_HASHTAGS)] | None = None
|
|
40
|
+
mentions: Annotated[list[UserID], Field(max_length=MAX_MENTIONS_PER_REQUEST)] | None = None
|
|
40
41
|
|
|
41
42
|
@field_validator("hashtags", mode="after")
|
|
42
43
|
@classmethod
|
|
@@ -44,3 +45,10 @@ class UpdatePostRequest(BasePatchRequest):
|
|
|
44
45
|
if v is None:
|
|
45
46
|
return None
|
|
46
47
|
return list({h.root: h for h in v}.values())
|
|
48
|
+
|
|
49
|
+
@field_validator("mentions", mode="after")
|
|
50
|
+
@classmethod
|
|
51
|
+
def dedupe_mentions(cls, v: list[UserID] | None) -> list[UserID] | None:
|
|
52
|
+
if v is None:
|
|
53
|
+
return None
|
|
54
|
+
return dedupe_mention_user_ids(v)
|
{core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/users/authenticated/router.py
RENAMED
|
@@ -41,7 +41,7 @@ from core_framework.application.users.authenticated_service import (
|
|
|
41
41
|
change_my_profile,
|
|
42
42
|
delete_my_current_appeal,
|
|
43
43
|
follow_user,
|
|
44
|
-
|
|
44
|
+
mark_follow_counterparty_stats_stale,
|
|
45
45
|
remove_user_report,
|
|
46
46
|
retrieve_my_account,
|
|
47
47
|
retrieve_my_appeals,
|
|
@@ -446,8 +446,11 @@ async def put_follow_user(
|
|
|
446
446
|
validated_target_user_id = validate_user_id(user_id=target_user_id)
|
|
447
447
|
changed = await follow_user(user_id=user_id.root, target_user_id=validated_target_user_id.root)
|
|
448
448
|
if changed:
|
|
449
|
-
background_tasks.add_task(
|
|
450
|
-
|
|
449
|
+
background_tasks.add_task(
|
|
450
|
+
mark_follow_counterparty_stats_stale,
|
|
451
|
+
user_id=user_id.root,
|
|
452
|
+
target_user_id=validated_target_user_id.root,
|
|
453
|
+
)
|
|
451
454
|
|
|
452
455
|
|
|
453
456
|
@router.delete(
|
|
@@ -463,8 +466,11 @@ async def delete_follow_user(
|
|
|
463
466
|
validated_target_user_id = validate_user_id(user_id=target_user_id)
|
|
464
467
|
changed = await unfollow_user(user_id=user_id.root, target_user_id=validated_target_user_id.root)
|
|
465
468
|
if changed:
|
|
466
|
-
background_tasks.add_task(
|
|
467
|
-
|
|
469
|
+
background_tasks.add_task(
|
|
470
|
+
mark_follow_counterparty_stats_stale,
|
|
471
|
+
user_id=user_id.root,
|
|
472
|
+
target_user_id=validated_target_user_id.root,
|
|
473
|
+
)
|
|
468
474
|
|
|
469
475
|
|
|
470
476
|
@router.get(
|
{core_framework-1.7.0 → core_framework-1.8.0}/core_framework/api/users/authenticated/schemas.py
RENAMED
|
@@ -48,6 +48,8 @@ class ProfileResponse(AvatarMixin, BannerMixin):
|
|
|
48
48
|
social_links: dict[str, str]
|
|
49
49
|
profile_visibility: ProfileVisibility
|
|
50
50
|
date_of_birth: DateOfBirth | None
|
|
51
|
+
follower_count: Annotated[int, Field(ge=0)] = 0
|
|
52
|
+
following_count: Annotated[int, Field(ge=0)] = 0
|
|
51
53
|
|
|
52
54
|
|
|
53
55
|
class AccountResponse(AvatarMixin):
|
|
@@ -4,9 +4,10 @@ from fastapi import APIRouter, Query
|
|
|
4
4
|
from fastapi.exceptions import RequestValidationError
|
|
5
5
|
from pydantic import ValidationError
|
|
6
6
|
|
|
7
|
-
from core_framework.api.
|
|
8
|
-
from core_framework.api.users.
|
|
9
|
-
from core_framework.
|
|
7
|
+
from core_framework.api.dependencies import OptionalUserID
|
|
8
|
+
from core_framework.api.users.public.schemas import UsernameExistsResponse, UserProfileResponse
|
|
9
|
+
from core_framework.api.users.shared.schemas import Username, validate_user_id
|
|
10
|
+
from core_framework.application.users.public_service import is_username_available, retrieve_user_profile
|
|
10
11
|
|
|
11
12
|
router = APIRouter(
|
|
12
13
|
prefix="/users",
|
|
@@ -23,3 +24,12 @@ async def get_username_exists(username: Annotated[str, Query()]) -> Any:
|
|
|
23
24
|
except ValidationError as e:
|
|
24
25
|
raise RequestValidationError(e.errors()) from e
|
|
25
26
|
return {"status": "available" if await is_username_available(username=validated_username.root) else "taken"}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@router.get("/{target_user_id}/profile", response_model=UserProfileResponse)
|
|
30
|
+
async def get_user_profile(target_user_id: str, viewer_id: OptionalUserID) -> Any:
|
|
31
|
+
validated_target_user_id = validate_user_id(user_id=target_user_id)
|
|
32
|
+
return await retrieve_user_profile(
|
|
33
|
+
target_user_id=validated_target_user_id.root,
|
|
34
|
+
viewer_id=viewer_id.root if viewer_id else None,
|
|
35
|
+
)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from typing import Annotated, Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
from core_framework.api.users.shared.schemas import (
|
|
6
|
+
AvatarMixin,
|
|
7
|
+
BannerMixin,
|
|
8
|
+
Bio,
|
|
9
|
+
DisplayName,
|
|
10
|
+
UserID,
|
|
11
|
+
Username,
|
|
12
|
+
UserStatus,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class UsernameExistsResponse(BaseModel):
|
|
17
|
+
status: Literal["available", "taken"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class UserProfileViewerContext(BaseModel):
|
|
21
|
+
is_following: bool
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class UserProfileResponse(AvatarMixin, BannerMixin):
|
|
25
|
+
user_id: UserID
|
|
26
|
+
username: Username
|
|
27
|
+
display_name: DisplayName | None = None
|
|
28
|
+
bio: Bio | None = None
|
|
29
|
+
status: UserStatus | None = None
|
|
30
|
+
social_links: dict[str, str]
|
|
31
|
+
follower_count: Annotated[int, Field(ge=0)] = 0
|
|
32
|
+
following_count: Annotated[int, Field(ge=0)] = 0
|
|
33
|
+
viewer_context: UserProfileViewerContext | None = None
|
{core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/auth/access_service.py
RENAMED
|
@@ -15,16 +15,11 @@ from core_framework.domains.user import UserIdentity
|
|
|
15
15
|
condition=lambda result, args, kwargs, key=None: result.identity != UserIdentity.DEFAULT,
|
|
16
16
|
)
|
|
17
17
|
async def get_user_detail(*, user_id: str) -> UserDetail:
|
|
18
|
-
"""
|
|
18
|
+
"""
|
|
19
|
+
Return cached user identity and moderation state for auth middleware.
|
|
19
20
|
|
|
20
21
|
Fetches identity and moderation mappings concurrently. Results are cached
|
|
21
22
|
under ``CacheKey.USER_DETAIL``; default (missing) identities are not cached.
|
|
22
|
-
|
|
23
|
-
Args:
|
|
24
|
-
user_id: External user ID from the auth token.
|
|
25
|
-
|
|
26
|
-
Returns:
|
|
27
|
-
Combined identity and moderation snapshot for access checks.
|
|
28
23
|
"""
|
|
29
24
|
async with asyncio.TaskGroup() as tg:
|
|
30
25
|
identity_task = tg.create_task(user_deps.user_service.retrieve_user_identity_mapping(user_ids={user_id}))
|
{core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/auth/auth_service.py
RENAMED
|
@@ -6,14 +6,7 @@ from core_framework.domains.user import CreatedUser
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
async def register_user(*, user_id: str) -> CreatedUser:
|
|
9
|
-
"""Register a new user and prime username availability cache.
|
|
10
|
-
|
|
11
|
-
Args:
|
|
12
|
-
user_id: External identity for the new account.
|
|
13
|
-
|
|
14
|
-
Returns:
|
|
15
|
-
Created user ID and assigned username.
|
|
16
|
-
"""
|
|
9
|
+
"""Register a new user and prime username availability cache."""
|
|
17
10
|
created_user = await user_deps.user_service.add_user(user_id=user_id)
|
|
18
11
|
await cache.set(
|
|
19
12
|
CacheKey.TAKEN_USERNAMES.format_key(username=created_user.username),
|
|
@@ -10,6 +10,7 @@ from core_framework.domains.user.dependencies import configure_user_dependencies
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def configure_application_dependencies(*, runtime: CoreRuntime) -> None:
|
|
13
|
+
"""Wire application-level dependencies from the shared runtime."""
|
|
13
14
|
configure_worker_jobs(runtime=runtime)
|
|
14
15
|
configure_cashews_backend(redis_cache_url=runtime.settings.redis.redis_cache_url)
|
|
15
16
|
configure_comment_dependencies(runtime)
|
{core_framework-1.7.0 → core_framework-1.8.0}/core_framework/application/cache/invalidate_service.py
RENAMED
|
@@ -9,14 +9,11 @@ logger = get_logger(__name__)
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
async def invalidate_cache_targets(*, actor_id: str, items: list[CacheInvalidateTarget]) -> None:
|
|
12
|
-
"""
|
|
12
|
+
"""
|
|
13
|
+
Invalidate cashews cache entries requested by an admin.
|
|
13
14
|
|
|
14
15
|
Best-effort: failures are logged and do not propagate. Callers receive no
|
|
15
16
|
signal when Redis or cashews is unavailable (see admin cache invalidation flow doc).
|
|
16
|
-
|
|
17
|
-
Args:
|
|
18
|
-
actor_id: Firebase uid of the admin performing the operation.
|
|
19
|
-
items: Cache keys and parameters to invalidate.
|
|
20
17
|
"""
|
|
21
18
|
for item in items:
|
|
22
19
|
try:
|
|
@@ -39,12 +36,7 @@ async def invalidate_cache_targets(*, actor_id: str, items: list[CacheInvalidate
|
|
|
39
36
|
|
|
40
37
|
|
|
41
38
|
async def enqueue_cache_pattern_invalidation(*, actor_id: str, target: CacheInvalidatePatternTarget) -> None:
|
|
42
|
-
"""Enqueue a worker job to invalidate cache entries matching a partial pattern.
|
|
43
|
-
|
|
44
|
-
Args:
|
|
45
|
-
actor_id: Firebase uid of the admin performing the operation.
|
|
46
|
-
target: Cache resource and partial key parameters (omitted fields wildcard).
|
|
47
|
-
"""
|
|
39
|
+
"""Enqueue a worker job to invalidate cache entries matching a partial pattern."""
|
|
48
40
|
await worker_jobs.enqueue_job(
|
|
49
41
|
job_name=WorkerJobName.PROCESS_CACHE_PATTERN_INVALIDATION,
|
|
50
42
|
actor_id=actor_id,
|
|
@@ -54,15 +46,7 @@ async def enqueue_cache_pattern_invalidation(*, actor_id: str, target: CacheInva
|
|
|
54
46
|
|
|
55
47
|
|
|
56
48
|
async def invalidate_cache_pattern_target(*, actor_id: str, target: CacheInvalidatePatternTarget) -> None:
|
|
57
|
-
"""Invalidate cashews cache entries matching a partial pattern (worker entrypoint).
|
|
58
|
-
|
|
59
|
-
Args:
|
|
60
|
-
actor_id: Firebase uid of the admin who requested the invalidation.
|
|
61
|
-
target: Cache resource and partial key parameters (omitted fields wildcard).
|
|
62
|
-
|
|
63
|
-
Raises:
|
|
64
|
-
Exception: Re-raised after logging so the worker can retry transient failures.
|
|
65
|
-
"""
|
|
49
|
+
"""Invalidate cashews cache entries matching a partial pattern (worker entrypoint)."""
|
|
66
50
|
try:
|
|
67
51
|
await invalidate_cache(target.resource, **target.params)
|
|
68
52
|
except Exception:
|