feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms
Directus image transform cache in-process with an explicit acceptFormat,
so the generated file matches the browser's cache key byte-for-byte.
- resumable, window-aware backfill with progress + cancel
- system + custom presets, effective-format reporting, shadow warnings
- storage breakdown (previews vs originals), human-readable API fields
- diagnostics: resolve-plan prediction, per-file generate, ops ring buffer
- isolated dev/staging compose stack + deploy/rollback scripts
Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed,
idempotent backfill, custom fixed-format presets, error surfacing.
2026-10-25 11:19:54 +03:00
|
|
|
|
# directus-extension-generate-image-previews
|
|
|
|
|
|
|
|
|
|
|
|
Pre-generate Directus image transform ("preview") cache files so the admin UI and API never pay the
|
|
|
|
|
|
first-request sharp cost.
|
|
|
|
|
|
|
|
|
|
|
|
A Directus **bundle**:
|
|
|
|
|
|
|
|
|
|
|
|
- **upload hook** — warm previews right after an image upload.
|
|
|
|
|
|
- **new-preset trigger** — `Run now` warms every preset (system + custom) for existing images.
|
|
|
|
|
|
- **backfill job** — resumable, window-aware sweep over the existing library.
|
2026-10-25 12:25:19 +03:00
|
|
|
|
- **admin module** — progress, storage breakdown, configuration, diagnostics, and an API reference.
|
feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms
Directus image transform cache in-process with an explicit acceptFormat,
so the generated file matches the browser's cache key byte-for-byte.
- resumable, window-aware backfill with progress + cancel
- system + custom presets, effective-format reporting, shadow warnings
- storage breakdown (previews vs originals), human-readable API fields
- diagnostics: resolve-plan prediction, per-file generate, ops ring buffer
- isolated dev/staging compose stack + deploy/rollback scripts
Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed,
idempotent backfill, custom fixed-format presets, error surfacing.
2026-10-25 11:19:54 +03:00
|
|
|
|
|
2026-10-25 12:25:19 +03:00
|
|
|
|
## Requirements
|
feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms
Directus image transform cache in-process with an explicit acceptFormat,
so the generated file matches the browser's cache key byte-for-byte.
- resumable, window-aware backfill with progress + cancel
- system + custom presets, effective-format reporting, shadow warnings
- storage breakdown (previews vs originals), human-readable API fields
- diagnostics: resolve-plan prediction, per-file generate, ops ring buffer
- isolated dev/staging compose stack + deploy/rollback scripts
Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed,
idempotent backfill, custom fixed-format presets, error surfacing.
2026-10-25 11:19:54 +03:00
|
|
|
|
|
2026-10-25 12:25:19 +03:00
|
|
|
|
- Directus **11.x** (tested on **11.17.4**).
|
|
|
|
|
|
- Node 18+.
|
|
|
|
|
|
- A **non-sandboxed** local extension (it uses Directus's internal services at runtime — see
|
|
|
|
|
|
Compatibility).
|
|
|
|
|
|
- Local disk storage is supported out of the box; other storage drivers work for disk reporting through
|
|
|
|
|
|
the Directus storage manager.
|
|
|
|
|
|
|
|
|
|
|
|
## Why this approach
|
feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms
Directus image transform cache in-process with an explicit acceptFormat,
so the generated file matches the browser's cache key byte-for-byte.
- resumable, window-aware backfill with progress + cancel
- system + custom presets, effective-format reporting, shadow warnings
- storage breakdown (previews vs originals), human-readable API fields
- diagnostics: resolve-plan prediction, per-file generate, ops ring buffer
- isolated dev/staging compose stack + deploy/rollback scripts
Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed,
idempotent backfill, custom fixed-format presets, error surfacing.
2026-10-25 11:19:54 +03:00
|
|
|
|
|
|
|
|
|
|
The cache file is `<filename_disk>__<object-hash(resolved transforms)>.<ext>`. The hash depends on the
|
|
|
|
|
|
RESOLVED transforms, and every `system-*` preset is `format:'auto'` — which resolves differently
|
2026-10-25 12:25:19 +03:00
|
|
|
|
server-side (jpg/png) than in a browser (avif). This extension therefore generates **in-process** with an
|
|
|
|
|
|
explicit `acceptFormat` (`avif` + `webp`), reproducing the browser's cache file byte-for-byte without any
|
|
|
|
|
|
HTTP request. Naive warming (and several community extensions) silently misses because of this.
|
feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms
Directus image transform cache in-process with an explicit acceptFormat,
so the generated file matches the browser's cache key byte-for-byte.
- resumable, window-aware backfill with progress + cancel
- system + custom presets, effective-format reporting, shadow warnings
- storage breakdown (previews vs originals), human-readable API fields
- diagnostics: resolve-plan prediction, per-file generate, ops ring buffer
- isolated dev/staging compose stack + deploy/rollback scripts
Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed,
idempotent backfill, custom fixed-format presets, error surfacing.
2026-10-25 11:19:54 +03:00
|
|
|
|
|
2026-10-25 12:25:19 +03:00
|
|
|
|
## Install
|
feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms
Directus image transform cache in-process with an explicit acceptFormat,
so the generated file matches the browser's cache key byte-for-byte.
- resumable, window-aware backfill with progress + cancel
- system + custom presets, effective-format reporting, shadow warnings
- storage breakdown (previews vs originals), human-readable API fields
- diagnostics: resolve-plan prediction, per-file generate, ops ring buffer
- isolated dev/staging compose stack + deploy/rollback scripts
Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed,
idempotent backfill, custom fixed-format presets, error surfacing.
2026-10-25 11:19:54 +03:00
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-10-25 12:25:19 +03:00
|
|
|
|
git clone https://code.at.locus.im/Rawman/directus-extension-generate-image-previews.git
|
|
|
|
|
|
cd directus-extension-generate-image-previews
|
feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms
Directus image transform cache in-process with an explicit acceptFormat,
so the generated file matches the browser's cache key byte-for-byte.
- resumable, window-aware backfill with progress + cancel
- system + custom presets, effective-format reporting, shadow warnings
- storage breakdown (previews vs originals), human-readable API fields
- diagnostics: resolve-plan prediction, per-file generate, ops ring buffer
- isolated dev/staging compose stack + deploy/rollback scripts
Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed,
idempotent backfill, custom fixed-format presets, error surfacing.
2026-10-25 11:19:54 +03:00
|
|
|
|
npm install
|
2026-10-25 12:25:19 +03:00
|
|
|
|
npm run build
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Copy the build into your Directus extensions directory:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
cp -r dist package.json /path/to/directus/extensions/directus-extension-generate-image-previews/
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Restart Directus (or let `EXTENSIONS_AUTO_RELOAD` pick it up). The **Image Previews** module appears in
|
|
|
|
|
|
the admin sidebar.
|
|
|
|
|
|
|
|
|
|
|
|
## Development
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms
Directus image transform cache in-process with an explicit acceptFormat,
so the generated file matches the browser's cache key byte-for-byte.
- resumable, window-aware backfill with progress + cancel
- system + custom presets, effective-format reporting, shadow warnings
- storage breakdown (previews vs originals), human-readable API fields
- diagnostics: resolve-plan prediction, per-file generate, ops ring buffer
- isolated dev/staging compose stack + deploy/rollback scripts
Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed,
idempotent backfill, custom fixed-format presets, error surfacing.
2026-10-25 11:19:54 +03:00
|
|
|
|
npm run build # emits dist/app.js + dist/api.js
|
|
|
|
|
|
npm run typecheck # 0 errors
|
|
|
|
|
|
npm test # unit tests
|
|
|
|
|
|
npm run dev # watch build
|
2026-10-25 12:25:19 +03:00
|
|
|
|
npm run link # link into a local Directus extensions dir
|
feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms
Directus image transform cache in-process with an explicit acceptFormat,
so the generated file matches the browser's cache key byte-for-byte.
- resumable, window-aware backfill with progress + cancel
- system + custom presets, effective-format reporting, shadow warnings
- storage breakdown (previews vs originals), human-readable API fields
- diagnostics: resolve-plan prediction, per-file generate, ops ring buffer
- isolated dev/staging compose stack + deploy/rollback scripts
Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed,
idempotent backfill, custom fixed-format presets, error surfacing.
2026-10-25 11:19:54 +03:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-10-25 12:25:19 +03:00
|
|
|
|
An isolated Directus 11.17.4 stack for smoke testing lives in `dev/` — see `dev/README.md`.
|
|
|
|
|
|
|
feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms
Directus image transform cache in-process with an explicit acceptFormat,
so the generated file matches the browser's cache key byte-for-byte.
- resumable, window-aware backfill with progress + cancel
- system + custom presets, effective-format reporting, shadow warnings
- storage breakdown (previews vs originals), human-readable API fields
- diagnostics: resolve-plan prediction, per-file generate, ops ring buffer
- isolated dev/staging compose stack + deploy/rollback scripts
Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed,
idempotent backfill, custom fixed-format presets, error surfacing.
2026-10-25 11:19:54 +03:00
|
|
|
|
## Settings
|
|
|
|
|
|
|
|
|
|
|
|
Defaults are applied automatically; everything is editable at runtime from the admin module — no rebuild.
|
|
|
|
|
|
|
|
|
|
|
|
| setting | default | meaning |
|
|
|
|
|
|
| -------------------------- | -------------------------------------- | ---------------------------------------------------------------- |
|
|
|
|
|
|
| `enabled` | `true` | master switch (off = hooks/backfill inert) |
|
|
|
|
|
|
| `onUpload` | `true` | generate previews immediately after an upload |
|
|
|
|
|
|
| `windowStart` / `windowEnd` | `01:00` / `04:00` | scheduled runs only start inside this window (local time) |
|
|
|
|
|
|
| `concurrency` | `1` | parallel generations |
|
|
|
|
|
|
| `formats` | `avif,webp` | output variants **for `format:'auto'` presets only** (all system presets) |
|
|
|
|
|
|
| `presetKeys` | `null` (all) | restrict to specific preset keys |
|
|
|
|
|
|
| `debug` | `false` | verbose generation logs in the Directus server log |
|
|
|
|
|
|
| `IMAGE_PREVIEWS_STATE_DIR` | `<STORAGE_LOCAL_ROOT>/.image-previews` | where settings + progress state live |
|
|
|
|
|
|
|
|
|
|
|
|
### Presets and formats
|
|
|
|
|
|
|
|
|
|
|
|
- **System presets** (`system-small|medium|large` × `cover|contain`) always use `format:'auto'`, so they
|
|
|
|
|
|
generate one file per entry in `formats` (default avif + webp), at their built-in sizes (64 / 300 / 800 px).
|
|
|
|
|
|
- **Custom presets** from Settings → File Storage are added automatically. A custom preset with a fixed
|
|
|
|
|
|
`format` (e.g. `png`) ignores `formats` and generates exactly that format. A custom preset with
|
|
|
|
|
|
`format:'auto'` follows `formats`.
|
|
|
|
|
|
- A custom preset whose `key` equals a system key is **shadowed** (system wins, same as Directus) and is
|
|
|
|
|
|
flagged in the admin module.
|
2026-10-25 12:25:19 +03:00
|
|
|
|
- Unsupported format strings (e.g. `bmp`) fail at generation; the error is shown in Recent operations
|
|
|
|
|
|
and `GET /image-previews/ops`.
|
feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms
Directus image transform cache in-process with an explicit acceptFormat,
so the generated file matches the browser's cache key byte-for-byte.
- resumable, window-aware backfill with progress + cancel
- system + custom presets, effective-format reporting, shadow warnings
- storage breakdown (previews vs originals), human-readable API fields
- diagnostics: resolve-plan prediction, per-file generate, ops ring buffer
- isolated dev/staging compose stack + deploy/rollback scripts
Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed,
idempotent backfill, custom fixed-format presets, error surfacing.
2026-10-25 11:19:54 +03:00
|
|
|
|
|
|
|
|
|
|
## Storage reporting
|
|
|
|
|
|
|
|
|
|
|
|
`GET /image-previews/disk-usage` (and the admin panel) reports bytes + file counts separately for
|
|
|
|
|
|
**preview assets** and **original assets**, plus the total — in both raw bytes and human-readable form.
|
|
|
|
|
|
|
|
|
|
|
|
## API endpoints
|
|
|
|
|
|
|
2026-10-25 12:25:19 +03:00
|
|
|
|
All require a Directus admin session or admin token. Human-readable fields accompany machine-readable ones.
|
feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms
Directus image transform cache in-process with an explicit acceptFormat,
so the generated file matches the browser's cache key byte-for-byte.
- resumable, window-aware backfill with progress + cancel
- system + custom presets, effective-format reporting, shadow warnings
- storage breakdown (previews vs originals), human-readable API fields
- diagnostics: resolve-plan prediction, per-file generate, ops ring buffer
- isolated dev/staging compose stack + deploy/rollback scripts
Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed,
idempotent backfill, custom fixed-format presets, error surfacing.
2026-10-25 11:19:54 +03:00
|
|
|
|
|
|
|
|
|
|
- `GET /image-previews/status` — run progress, rate, ETA, window state, ISO + human dates
|
|
|
|
|
|
- `GET /image-previews/config`, `PUT /image-previews/config`
|
|
|
|
|
|
- `GET /image-previews/presets` — system + custom presets, effective formats, shadowed keys
|
|
|
|
|
|
- `POST /image-previews/run` — start backfill (`{"ignoreWindow":true}` for an immediate run)
|
|
|
|
|
|
- `POST /image-previews/run/cancel`
|
|
|
|
|
|
- `GET /image-previews/disk-usage` — previews vs originals vs total
|
|
|
|
|
|
- `POST /image-previews/generate` — one file (`{fileId, keys?, formats?, dryRun?}`)
|
|
|
|
|
|
- `GET /image-previews/resolve-plan?fileId=&key=` — predicted cache filename, no write
|
|
|
|
|
|
- `GET /image-previews/ops?limit=` — recent operations with timing + error messages
|
|
|
|
|
|
|
|
|
|
|
|
The admin module includes a full API reference with curl examples.
|
|
|
|
|
|
|
2026-10-25 12:25:19 +03:00
|
|
|
|
## Scheduling
|
|
|
|
|
|
|
|
|
|
|
|
Scheduled runs are window-bound; an external cron can trigger them:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
0 * * * * curl -fsS -X POST http://localhost:8055/image-previews/run \
|
|
|
|
|
|
-H 'Authorization: Bearer <admin-token>' -H 'Content-Type: application/json' -d '{}'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`Run now` in the admin module ignores the window.
|
|
|
|
|
|
|
feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms
Directus image transform cache in-process with an explicit acceptFormat,
so the generated file matches the browser's cache key byte-for-byte.
- resumable, window-aware backfill with progress + cancel
- system + custom presets, effective-format reporting, shadow warnings
- storage breakdown (previews vs originals), human-readable API fields
- diagnostics: resolve-plan prediction, per-file generate, ops ring buffer
- isolated dev/staging compose stack + deploy/rollback scripts
Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed,
idempotent backfill, custom fixed-format presets, error surfacing.
2026-10-25 11:19:54 +03:00
|
|
|
|
## Safe testing & deployment
|
|
|
|
|
|
|
2026-10-25 12:25:19 +03:00
|
|
|
|
Never test against a production instance.
|
feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms
Directus image transform cache in-process with an explicit acceptFormat,
so the generated file matches the browser's cache key byte-for-byte.
- resumable, window-aware backfill with progress + cancel
- system + custom presets, effective-format reporting, shadow warnings
- storage breakdown (previews vs originals), human-readable API fields
- diagnostics: resolve-plan prediction, per-file generate, ops ring buffer
- isolated dev/staging compose stack + deploy/rollback scripts
Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed,
idempotent backfill, custom fixed-format presets, error surfacing.
2026-10-25 11:19:54 +03:00
|
|
|
|
|
|
|
|
|
|
- **Local / staging** (`dev/compose.dev.yml`): Directus 11.17.4 + its own postgres + volumes, on separate
|
2026-10-25 12:25:19 +03:00
|
|
|
|
ports. Shares nothing with production. See `dev/README.md`.
|
feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms
Directus image transform cache in-process with an explicit acceptFormat,
so the generated file matches the browser's cache key byte-for-byte.
- resumable, window-aware backfill with progress + cancel
- system + custom presets, effective-format reporting, shadow warnings
- storage breakdown (previews vs originals), human-readable API fields
- diagnostics: resolve-plan prediction, per-file generate, ops ring buffer
- isolated dev/staging compose stack + deploy/rollback scripts
Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed,
idempotent backfill, custom fixed-format presets, error surfacing.
2026-10-25 11:19:54 +03:00
|
|
|
|
- **Fail-open**: entrypoints never throw into Directus core.
|
2026-10-25 12:25:19 +03:00
|
|
|
|
- **Rollout**: build → verify on staging → back up the previous `dist/` → deploy → healthcheck → enable.
|
|
|
|
|
|
**Rollback**: restore the previous `dist/`.
|
|
|
|
|
|
- Helpers: `dev/deploy-staging.sh`, `dev/rollback-staging.sh`, template `dev/compose.staging.yml`.
|
feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms
Directus image transform cache in-process with an explicit acceptFormat,
so the generated file matches the browser's cache key byte-for-byte.
- resumable, window-aware backfill with progress + cancel
- system + custom presets, effective-format reporting, shadow warnings
- storage breakdown (previews vs originals), human-readable API fields
- diagnostics: resolve-plan prediction, per-file generate, ops ring buffer
- isolated dev/staging compose stack + deploy/rollback scripts
Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed,
idempotent backfill, custom fixed-format presets, error surfacing.
2026-10-25 11:19:54 +03:00
|
|
|
|
|
|
|
|
|
|
## Caveats
|
|
|
|
|
|
|
|
|
|
|
|
- **Transform dimension guard, not a file-size limit.** Directus rejects transformations of images whose
|
|
|
|
|
|
width or height exceeds `ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION` (default **6000 px**). This is a Directus
|
|
|
|
|
|
env var, not something this extension imposes. To process larger images, raise it on the Directus
|
|
|
|
|
|
instance (and watch memory). Upload size limits (`FILES_MAX_UPLOAD_SIZE`, `MAX_PAYLOAD_SIZE`) are separate.
|
2026-10-25 12:25:19 +03:00
|
|
|
|
- Warm only after uploads are committed (local storage writes are non-atomic).
|
feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms
Directus image transform cache in-process with an explicit acceptFormat,
so the generated file matches the browser's cache key byte-for-byte.
- resumable, window-aware backfill with progress + cancel
- system + custom presets, effective-format reporting, shadow warnings
- storage breakdown (previews vs originals), human-readable API fields
- diagnostics: resolve-plan prediction, per-file generate, ops ring buffer
- isolated dev/staging compose stack + deploy/rollback scripts
Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed,
idempotent backfill, custom fixed-format presets, error surfacing.
2026-10-25 11:19:54 +03:00
|
|
|
|
- In-process generation bypasses the HTTP rate limiter; bound it with `concurrency` + the window.
|
2026-10-25 12:25:19 +03:00
|
|
|
|
- PM2 cluster: shared state is file-backed, so all workers see the same run state.
|
|
|
|
|
|
|
|
|
|
|
|
## Compatibility
|
|
|
|
|
|
|
|
|
|
|
|
Tested against Directus **11.17.4**. The extension reads Directus's own transform pipeline and storage
|
|
|
|
|
|
manager (`@directus/api/utils/transformations`, `@directus/api/storage/index`) via runtime imports; these
|
|
|
|
|
|
are **not a public API** and may change between Directus versions. Re-verify after a Directus upgrade,
|
|
|
|
|
|
especially the hash-match gate (`resolve-plan` vs a real browser asset request).
|
feat: pre-generate Directus image previews
Bundle extension (upload hook + API endpoint + admin module) that warms
Directus image transform cache in-process with an explicit acceptFormat,
so the generated file matches the browser's cache key byte-for-byte.
- resumable, window-aware backfill with progress + cancel
- system + custom presets, effective-format reporting, shadow warnings
- storage breakdown (previews vs originals), human-readable API fields
- diagnostics: resolve-plan prediction, per-file generate, ops ring buffer
- isolated dev/staging compose stack + deploy/rollback scripts
Verified on Directus 11.17.4: 40/40 tests, hash-match gate passed,
idempotent backfill, custom fixed-format presets, error surfacing.
2026-10-25 11:19:54 +03:00
|
|
|
|
|
2026-10-25 12:25:19 +03:00
|
|
|
|
The extension is not sandboxed: Directus's sandbox forbids importing anything except `directus:api`, which
|
|
|
|
|
|
this extension needs for storage/transform access.
|