uhifadhi / storage-module
Storage: the platform's file-storage machinery — named Flysystem storages, a private evidence API with a detected-MIME allowlist, thumbnails, and authenticated streaming behind a per-module permission seam.
Package info
github.com/uhifadhilabs/storage-module
Type:symfony-bundle
pkg:composer/uhifadhi/storage-module
Requires
- php: >=8.4
- league/flysystem: ^3.0
- league/flysystem-bundle: ^3.4
- symfony/asset: ^7.3 || ^8.0
- symfony/config: ^7.3 || ^8.0
- symfony/dependency-injection: ^7.3 || ^8.0
- symfony/framework-bundle: ^7.3 || ^8.0
- symfony/http-foundation: ^7.3 || ^8.0
- symfony/http-kernel: ^7.3 || ^8.0
- symfony/mime: ^7.3 || ^8.0
- symfony/routing: ^7.3 || ^8.0
- symfony/security-core: ^7.3 || ^8.0
- symfony/security-csrf: ^7.3 || ^8.0
- symfony/uid: ^7.3 || ^8.0
- twig/twig: ^3.12
- uhifadhi/module-contracts: ^0.3
- uhifadhi/widget-module: ^0.1
Requires (Dev)
- doctrine/doctrine-bundle: ^3.0
- friendsofphp/php-cs-fixer: ^3.95
- league/flysystem-async-aws-s3: ^3.0
- phpstan/phpstan: ^2.2
- phpunit/phpunit: ^13.2
- symfony/asset-mapper: ^7.3 || ^8.0
- symfony/browser-kit: ^7.3 || ^8.0
- symfony/css-selector: ^7.3 || ^8.0
- symfony/security-bundle: ^7.3 || ^8.0
- symfony/security-http: ^7.3 || ^8.0
- symfony/twig-bundle: ^7.3 || ^8.0
- symfony/ux-icons: ^2.20 || ^3.0
- uhifadhi/shell-module: ^0.5
- uhifadhi/team-module: ^0.4
Suggests
- ext-gd: Fallback thumbnail engine (JPEG/PNG/WebP/AVIF).
- ext-imagick: Preferred thumbnail engine. Decodes HEIC/HEIF where the system ImageMagick was built with libheif; GD cannot.
- league/flysystem-async-aws-s3: Required when a deployment selects the S3 adapter (storage.evidence.adapter: s3).
- symfony/twig-bundle: Required to render the Files hub. Without Twig the bundle registers no screen at all and is storage machinery only — the named storages, the evidence API, the thumbnails and the serving route.
- uhifadhi/shell-module: The page frame the four Files screens render in, and the sidebar this module contributes its Files row to; without it the screens have no frame to extend.
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-04 14:39:37 UTC
README
The uhifadhi platform's file-storage machinery: named Flysystem storages, a private evidence API with a detected-MIME allowlist, thumbnails, and one authenticated route by which any of it comes back out.
What it is
Mechanism only. The bundle owns no entities, no migrations and no screens of a module's own: it holds the named storages, the store/stream/delete API, the MIME allowlist and size cap, thumbnail generation, and the authenticated serving route. Which record a file hangs off, and who may read it, stays with the module that wrote the key — see the charter.
On top of that machinery it ships one optional cross-module screen, the Files
hub (/files), which is a dashboard on uhifadhi/widget-module — which is why
that package is a hard requirement rather than a suggestion.
Installation
composer require uhifadhi/storage-module
The recipe registers the bundles and writes config/packages/storage.yaml and
config/routes/storage.yaml. Without Flex, config/bundles.php needs three
lines:
League\FlysystemBundle\FlysystemBundle::class => ['all' => true], Uhifadhi\Widget\UhifadhiWidgetBundle::class => ['all' => true], Uhifadhi\Storage\UhifadhiStorageBundle::class => ['all' => true],
The bundle prepends its own flysystem block, so an installation never
writes config/packages/flysystem.yaml to get an evidence store.
The widget module keeps its layouts in two tables of its own, so after
installing run your own doctrine:migrations:diff and migrate — and see that
module's recipe for the single resolve_target_entities line an installation
without uhifadhi/team-module has to write.
Getting started
An unconfigured installation already has a working, private, on-disk evidence store. Three steps put files through it:
1 · Store and read bytes from the module that owns the record:
use Uhifadhi\Storage\Service\EvidenceStorage; $stored = $evidence->store($uploadedFile, 'observation/'.$uuid, $clientUuid); $stored->key; // RELATIVE, always — this is what you record on your entity $stored->mimeType; // DETECTED, never what the client claimed $stored->thumbKey; // the ~400px variant, or NULL
2 · Mount the routes, so stored bytes can come back out:
# config/routes/storage.yaml storage: resource: '@UhifadhiStorageBundle/src/Controller/' type: attribute
The serving route storage_evidence_show (GET /storage/evidence/{key}) is
registered only when SecurityBundle is in the kernel.
3 · Ship a voter for your key prefix, tagged
uhifadhi.evidence_access_voter. Storage cannot know what an observation is, so
it asks the module that wrote the key — and denies by default until a voter
claims it and agrees.
Configuration (a different adapter, a narrower allowlist, the Files hub) is optional and layered on from there.
Learn more
- docs/charter.md — what belongs in this bundle and what stays in the owning module.
- docs/configuration.md — the full
storage.yamlreference: local and S3-compatible object storage, and why visibility is not a setting. - docs/evidence-api.md —
EvidenceStorage, the key rules, the three exceptions, validation and thumbnails. - docs/serving-and-permissions.md — the serving route and the permission seam: voters, deny-by-default, and enumeration.
- docs/files-hub.md — the cross-module
/filesscreens: what an installation wires, the widgets,FileSourceInterface, and removal. - docs/adopting-in-a-module.md — step-by-step adoption for patrol-module and incident-module.
- docs/service-reference.md — service ids, classes, the tag and the route.
- docs/development.md — running the suite, and what CI deliberately does without.
License
AGPL-3.0-or-later — see LICENSE: the same license as the uhifadhi platform this module is part of. Use, modify and self-host freely; if you offer a modified version to users over a network, they are entitled to the source of what they're running.