diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ac3e6c3d..526d280d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,6 +96,7 @@ All notable changes to this project are documented in this file. The format is b - `audit_log` follows the module layout: `audit_log.core.{constants,settings,services,capture}`, `audit_log.entries.{service,filters,resolve,activity,export}`, `audit_log.web.{deps,endpoints}`, and the page components move from `pages/components/` to `ui/components/`. The `simple_module.audit_sinks` entry point now targets `audit_log.core.capture:audit_callback`. Nothing outside the module imported the old paths, so no aliases remain. - `auth` follows the module layout: `auth.core.{constants,state}` and `auth.web.{deps,middleware}`; `auth.oidc_common` stays as a declared domain area. `auth.constants`, `auth.state`, `auth.deps` and `auth.middleware` still import, with a `DeprecationWarning`, until 0.0.46. - `tenants` follows the module layout: `tenants.core.{constants,settings,services,errors,service,user_cleanup}` (with the membership, owner-guard and deletion helpers), `tenants.resolution.{resolver,host_resolver,token_tenant}`, `tenants.invitations.{service,mail}`, `tenants.web.{deps,shared_props,endpoints}`, and the page components and hooks move to `ui/components` and `ui/hooks`. The invitation mail template moves to `invitations/templates/invitation.txt`. `tenants.constants`, `tenants.errors`, `tenants.host_resolver`, `tenants.resolver`, `tenants.service` and `tenants.deps` still import, with a `DeprecationWarning`, until 0.0.46. The durable handler names `tenants.user_cleanup.purge_user_memberships` and `tenants.send_invitation_email` are unchanged. +- `file_storage` follows the module layout: `file_storage.core.{constants,settings,services,errors,scope,format,health,audit_labels,tenant_cache,tenant_deletion,object_reaper,worker,worker_support}`, `file_storage.files.*`, `file_storage.multipart.{sessions,guards,support,checksum}`, `file_storage.backends.guard`, `file_storage.web.{deps,serving,cors,csp,cookieless,body_limits,endpoints}`, and the page components, hook and helpers move to `ui/`. `file_storage.settings`, `.service`, `.deps`, `.scope`, `.downloads`, `.upload_support` and `.tenant_deletion` still import, with a `DeprecationWarning`, until 0.0.46; the other old flat paths (`.constants`, `.errors`, `.services`, `.endpoints.*`, `.upload_guards`, `.uploads`, `.checksum`, `.worker`, `.cors`, `.csp` and so on) have no alias. The durable `TenantStorageCleanupRequested` event key and its handler names, and the Celery task names, are unchanged. - Coalesce concurrent tenant membership and permission grant cache misses. In-flight invalidation retries against fresh authorization data; failures and cancellation release waiting readers. Cache unknown tenant subdomains for five seconds and drop the new slug's host lookups (on a new `tenants.hosts` invalidation channel, so no user id is read as a slug) and the owner's memberships after committed tenant creation; other users' caches are not cleared. Services built outside request dependencies pass `invalidate_hosts=make_host_invalidator(app)` beside `invalidate=make_invalidator(app)`. Retries after concurrent invalidation are bounded to three reads (2026-11-05 local performance review). diff --git a/docs/module-layout.md b/docs/module-layout.md index e06c219b4..6084d5658 100644 --- a/docs/module-layout.md +++ b/docs/module-layout.md @@ -219,7 +219,7 @@ when a later module PR moves files. | branding | 2 (+6 shims) | 0 | follows this layout | | dashboard | 2 | 0 | follows this layout | | feature_flags | 3 (+1 shim) | 0 | follows this layout | -| file_storage | 39 | 65 | flat | +| file_storage | 4 (+7 shims) | 0 | follows this layout | | keycloak | 3 (+5 shims) | 0 | follows this layout | | mail | 2 (+2 shims) | 0 | follows this layout | | oidc | 3 (+3 shims) | 0 | follows this layout | diff --git a/docs/modules/background_tasks.md b/docs/modules/background_tasks.md index bb489ba57..23a933f5a 100644 --- a/docs/modules/background_tasks.md +++ b/docs/modules/background_tasks.md @@ -678,7 +678,7 @@ Workers never run the hosting lifespan, so `FileStorageSettings()` (or any `DbBa ```python from background_tasks import settings_for -from file_storage.settings import FileStorageSettings +from file_storage.core.settings import FileStorageSettings @celery.task diff --git a/docs/modules/file_storage.md b/docs/modules/file_storage.md index c00de307f..fd577c4b5 100644 --- a/docs/modules/file_storage.md +++ b/docs/modules/file_storage.md @@ -453,7 +453,7 @@ files the caller uploaded (`created_by` is their user id), unless they hold `file_storage.manage`. Someone else's file answers `404`, exactly like an unknown id, and the browse screen's type and uploader filters list only the caller's own rows (the usage figure stays bucket-wide, as the quota is). The -scope is passed explicitly by the endpoints (`deps.get_owner_scope`) to the +scope is passed explicitly by the endpoints (`web.deps.get_owner_scope`) to the service's `owner=` argument; internal callers such as branding and reapers pass none and stay unscoped, and anonymous `public` serving is unaffected. @@ -486,7 +486,7 @@ class MyModule(ModuleBase): ## Inertia pages - `FileStorage/Browse.tsx` — file list + upload dropzone; handles the upload progress + delete confirmation flow. Each row shows a "Public" badge and a make public / make private action. -- `FileStorage/components/UploadDropzone.tsx` — drag-drop upload child component. +- `ui/components/UploadDropzone.tsx` — drag-drop upload child component. ## Locales diff --git a/host/tests/test_module_layout.py b/host/tests/test_module_layout.py index a3627a7dc..7cdd36b9e 100644 --- a/host/tests/test_module_layout.py +++ b/host/tests/test_module_layout.py @@ -37,6 +37,7 @@ "audit_log": frozenset({"entries"}), "auth": frozenset({"oidc_common"}), "branding": frozenset({"assets"}), + "file_storage": frozenset({"backends", "files", "multipart"}), "keycloak": frozenset({"identity"}), "mail": frozenset({"delivery"}), "oidc": frozenset({"identity"}), @@ -68,16 +69,6 @@ workers_state.py """.split() ), - "file_storage": frozenset( - """ - aggregates.py audit_labels.py backend_guard.py backends body_limits.py checksum.py - constants.py cookieless.py cors.py csp.py deletes.py deps.py downloads.py endpoints - errors.py format.py health.py media_types.py object_reaper.py queries.py ranges.py - reads.py resolve.py scope.py service.py services.py serving.py settings.py - tenant_cache.py tenant_deletion.py thumbnails.py upload_guards.py upload_support.py - uploads.py visibility.py worker.py worker_support.py - """.split() - ), "users": frozenset( """ admin administrators.py audit.py auth_local backend.py bootstrap.py constants.py diff --git a/modules/branding/branding/assets/images.py b/modules/branding/branding/assets/images.py index eb1323631..0c9c39aa8 100644 --- a/modules/branding/branding/assets/images.py +++ b/modules/branding/branding/assets/images.py @@ -18,7 +18,7 @@ if TYPE_CHECKING: from file_storage.contracts.schemas import StoredFileOut - from file_storage.service import FileStorageService + from file_storage.files.service import FileStorageService # Raster + icon only. SVG is excluded on purpose: it is an XML document that can # carry