Skip to content

feat: instance settings + instance-wide storage cap (backend)#98

Merged
ravirajsinh45 merged 7 commits into
mainfrom
feature/instance-settings-storage-cap
Jul 7, 2026
Merged

feat: instance settings + instance-wide storage cap (backend)#98
ravirajsinh45 merged 7 commits into
mainfrom
feature/instance-settings-storage-cap

Conversation

@ravirajsinh45

Copy link
Copy Markdown
Contributor

Summary

Adds an admin-editable instance_settings singleton table — the foundation for
instance-level settings on this single-tenant, self-hosted deployment — with an
instance-wide total-storage cap as its first setting, enforced at upload time.
Backend only; the admin UI and a real storage indicator are a separate follow-up.

Context: emerged from #64 (configurable per-file cap + removal of a fake per-project
"10 GB" indicator). FreeFrame is single-tenant — one implicit org == the instance —
so an "org-level" cap is really an instance-level cap.

What's added

  • InstanceSettings model + migration — single-row table (fixed sentinel PK →
    true singleton), storage_limit_bytes BigInteger (0 = unlimited). Sibling to
    feat: Instance branding — white-label logos, org name, favicon, Powered by FreeFrame #90's instance_branding, kept separate by concern.
  • services/storage.pyinstance_storage_used_bytes (committed-only usage:
    processing/ready, non-deleted), get_storage_limit, storage_cap_error, and
    upload_guard_error (per-file MAX_UPLOAD_BYTES first, then the instance cap).
  • GET /instance/settings (any member → {storage_limit_bytes, storage_used_bytes},
    read-only) and PUT /instance/settings (admin). Per-role curated response;
    storage numbers never exposed to unauthenticated callers.
  • Enforcement at both upload-initiate handlers via the shared guard, before the
    MediaFile row is created. Initiate-only (concurrent-burst race documented).
    Per-file env cap unchanged.

Design decisions

  • 0 = unlimited everywhere (consistent with MAX_UPLOAD_BYTES).
  • One committed-only usage query feeds both the GET indicator and the cap check.
  • True singleton via sentinel PK + IntegrityError handling (fails closed under
    concurrent first access).

Testing

  • Full backend suite: 86 passed.
  • New apps/api/tests/test_instance_settings.py (model, service, router auth/shape,
    enforcement, singleton no-row default).
  • Real-Postgres smoke (the mock harness can't execute the SUM):
    instance_storage_used_bytes ran against the dev DB → 13.2 MB; over-cap and
    unlimited branches verified.

Follow-ups (out of scope)

ravirajsinh45 added a commit that referenced this pull request Jul 7, 2026
ravirajsinh45 added a commit that referenced this pull request Jul 7, 2026
MediaFile.file_size_bytes and CommentAttachment.file_size_bytes were INTEGER
(int4, ~2.1GB ceiling), so files >2GB could not be recorded — undermining #64
(unlimited per-file uploads) and #98 (instance storage accounting). Migrate both
columns to BigInteger with an alembic migration. Structural tests added.
…tion

Adds an admin-editable instance_settings singleton table for a future
instance-wide storage cap. storage_limit_bytes defaults to 0 (unlimited).

Note: the brief specified revision id f1a2b3c4d5e6, but that id already
exists in this repo's applied migration history (f1a2b3c4d5e6_add_poster_s3_key_to_projects.py),
so a new unique revision id (dfcdaa30f89e) was generated instead. down_revision
(8ca3dffea55f) and all schema/behavior are otherwise unchanged from the brief.
get_or_create_instance_settings and GET /instance/settings could each
insert a row on concurrent first-time access, producing duplicate rows
and letting the storage cap silently fail open. GET is now read-only
(defaults to unlimited when no row exists) and creates use a fixed
sentinel PK so concurrent inserts collide via IntegrityError instead of
duplicating.
@ravirajsinh45 ravirajsinh45 force-pushed the feature/instance-settings-storage-cap branch from a4515fc to 44a1a45 Compare July 7, 2026 05:26
ravirajsinh45 added a commit that referenced this pull request Jul 7, 2026
MediaFile.file_size_bytes and CommentAttachment.file_size_bytes were INTEGER
(int4, ~2.1GB ceiling), so files >2GB could not be recorded — undermining #64
(unlimited per-file uploads) and #98 (instance storage accounting). Migrate both
columns to BigInteger with an alembic migration. Structural tests added.
@ravirajsinh45 ravirajsinh45 merged commit 1443933 into main Jul 7, 2026
4 checks passed
@ravirajsinh45 ravirajsinh45 deleted the feature/instance-settings-storage-cap branch July 7, 2026 05:37
ravirajsinh45 added a commit that referenced this pull request Jul 7, 2026
MediaFile.file_size_bytes and CommentAttachment.file_size_bytes were INTEGER
(int4, ~2.1GB ceiling), so files >2GB could not be recorded — undermining #64
(unlimited per-file uploads) and #98 (instance storage accounting). Migrate both
columns to BigInteger with an alembic migration. Structural tests added.
ravirajsinh45 added a commit that referenced this pull request Jul 7, 2026
* fix: widen file_size_bytes to BigInteger (int4 -> bigint)

MediaFile.file_size_bytes and CommentAttachment.file_size_bytes were INTEGER
(int4, ~2.1GB ceiling), so files >2GB could not be recorded — undermining #64
(unlimited per-file uploads) and #98 (instance storage accounting). Migrate both
columns to BigInteger with an alembic migration. Structural tests added.

* docs(changelog): add file_size_bytes bigint fix under [Unreleased] (#99)

* docs(migration): warn that int4->int8 widen locks media_files (ACCESS EXCLUSIVE) (review #99)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant