Skip to content

Storage Module

The Storage module manages user-owned storage provider configurations (S3 buckets, GCS buckets, local-FS roots, Dropbox, Google Drive, etc.) and bridges them to the storage.Provider runtime backends used by the Media module. The module owns configuration — credentials, OAuth tokens, encryption — and delegates byte movement to the pkg/storage provider implementations. Each user can have multiple configured providers and exactly one default.

Class Diagram

uses

uses

caches

queries

Controller

-service: Service

-logger: Logger

+List(w, r)

+Create(w, r)

+Get(w, r)

+Update(w, r)

+Delete(w, r)

+GetCurrent(w, r)

+GetUsage(w, r)

+InitiateOAuth(w, r)

+OAuthCallback(w, r)

+TestConnection(w, r)

Service

-repo: Repository

-cache: Cache

-logger: Logger

+Create()

+Get()

+List()

+Update()

+Delete()

Repository

-db: Database

+Query()

+Insert()

+Update()

+Delete()

Cache

Database

Request Flow

  1. CRUD path → Standard Controller → Service → Repository chain for storage-provider records.
  2. OAuth path → For providers that need delegated auth (Google Drive, Dropbox), InitiateOAuth returns an authorization URL; OAuthCallback exchanges the code for tokens, encrypts the refresh token, and stores it on the provider record.
  3. TestConnection → Service builds a transient Provider via the factory and runs a no-op Stat/List to validate credentials before persisting.
  4. Runtime resolution → The Media module calls GetProviderForUser (or GetDefaultProvider for system uploads) to obtain a ready-to-stream storage.Provider.

Key Components

Controller

  • Pure CRUD plus three operational endpoints: GetCurrent (which provider this user uses by default), GetUsage (quota/used bytes), and TestConnection (round-trip credential check).
  • OAuth endpoints are isolated to providers that require it — direct-credential providers (S3 access keys, local FS) bypass these.

Service

  • GetProviderForUser is the runtime bridge: it loads the user’s chosen storage-provider config, decrypts secrets, and returns a storage.Provider instance the Media module can stream to.
  • GetDefaultProvider returns the system-wide fallback (typically local FS) used when a user hasn’t configured one yet.

Repository

Stores storage_providers rows: ID, owner, type slug, encrypted config JSON, OAuth token reference, last-tested timestamp.

Special Behaviors

Encrypted-at-rest credentials

Every persisted provider config (S3 secret keys, OAuth refresh tokens, etc.) is encrypted with the platform’s pkg/crypto AEAD before hitting the database. Decryption happens lazily — only when GetProviderForUser is called to instantiate a runtime Provider. Reads of the bare config JSON via GET /storage-providers/{id} redact secret fields.

OAuth flow isolation

Providers that need OAuth (Google Drive, Dropbox) go through InitiateOAuth → OAuthCallback. The state token is HMAC-signed (binds the redirect to the calling user) and short-lived (5 minutes). Direct-credential providers (S3 access key + secret, local FS root) bypass these endpoints entirely. The OAuth code path is fully separate from the CRUD code path so a future provider that uses neither (e.g. mTLS-authed object stores) can land without disturbing existing flows.

Runtime bridge to Media

GetProviderForUser(ctx, userID, providerType) is the only way the rest of the platform obtains a storage.Provider instance. The Blobs module wires this in via the StorageProviderGetter interface (a deliberate inversion to break the circular import). The Media module calls it directly. This single chokepoint means future cross-cutting concerns (rate limiting per provider, quota enforcement, audit logging on byte movement) all land in one place.

SDK Surface

Cross-Module Integration

ModuleRelationship
MediaCalls GetProviderForUser to obtain a runtime backend; never touches storage_providers directly.
BlobsHolds a StorageProviderGetter set post-construction; uses it to clean up files on blob hard-delete.
UsersEach storage_providers row is owned by a user; deletes cascade.

API Reference

See the interactive API reference /storage-providers tag for live examples, including the OAuth handshake.