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.
This commit is contained in:
commit
c8f8bbc5ac
42 changed files with 10005 additions and 0 deletions
48
dev/README.md
Normal file
48
dev/README.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# Local dev / staging stack
|
||||
|
||||
Isolated Directus **11.17.4** with its own Postgres and volumes. Nothing is shared with the prod
|
||||
instance (separate containers, volumes, and port **8057**).
|
||||
|
||||
## Run
|
||||
|
||||
```bash
|
||||
# from the project root
|
||||
./dev/sync-extension.sh # build + copy dist/package.json into dev/extension
|
||||
docker compose -f dev/compose.dev.yml up -d
|
||||
```
|
||||
|
||||
Admin: `http://localhost:8057` — `admin@example.com` / `admin12345`.
|
||||
The extension is loaded from `dev/extension` (bind mount). Check:
|
||||
|
||||
```bash
|
||||
docker logs image-previews-dev-directus-1 | grep -i "loaded extensions"
|
||||
```
|
||||
|
||||
## After changing source
|
||||
|
||||
```bash
|
||||
./dev/sync-extension.sh
|
||||
docker compose -f dev/compose.dev.yml restart directus
|
||||
```
|
||||
|
||||
(`EXTENSIONS_AUTO_RELOAD` is on, but a restart is the reliable path. Never `rm -rf dev/extension` —
|
||||
it breaks the bind mount.)
|
||||
|
||||
## Smoke test
|
||||
|
||||
```bash
|
||||
BASE=http://localhost:8057
|
||||
TOKEN=$(curl -s -X POST "$BASE/auth/login" -H 'Content-Type: application/json' \
|
||||
-d '{"email":"admin@example.com","password":"admin12345"}' | jq -r '.data.access_token')
|
||||
|
||||
curl -s "$BASE/image-previews/status" -H "Authorization: Bearer $TOKEN" | jq .
|
||||
curl -s -X POST "$BASE/files" -H "Authorization: Bearer $TOKEN" -F "file=@dev/seed/test.jpg" | jq '.data | {id,filename_disk}'
|
||||
docker exec image-previews-dev-directus-1 sh -c "ls -1 /directus/uploads | grep '__' | sort"
|
||||
```
|
||||
|
||||
## Stop / reset
|
||||
|
||||
```bash
|
||||
docker compose -f dev/compose.dev.yml down # stop, keep data
|
||||
docker compose -f dev/compose.dev.yml down -v # stop and wipe DB + uploads
|
||||
```
|
||||
51
dev/compose.dev.yml
Normal file
51
dev/compose.dev.yml
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
name: image-previews-dev
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_USER: directus
|
||||
POSTGRES_PASSWORD: directus
|
||||
POSTGRES_DB: directus
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U directus"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 20
|
||||
|
||||
directus:
|
||||
image: directus/directus:11.17.4
|
||||
ports:
|
||||
- "8057:8055"
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
KEY: dev-key
|
||||
SECRET: dev-secret
|
||||
ADMIN_EMAIL: admin@example.com
|
||||
ADMIN_PASSWORD: admin12345
|
||||
DB_CLIENT: pg
|
||||
DB_HOST: db
|
||||
DB_PORT: "5432"
|
||||
DB_DATABASE: directus
|
||||
DB_USER: directus
|
||||
DB_PASSWORD: directus
|
||||
STORAGE_LOCATIONS: local
|
||||
STORAGE_LOCAL_ROOT: /directus/uploads
|
||||
EXTENSIONS_AUTO_RELOAD: "true"
|
||||
CACHE_ENABLED: "false"
|
||||
RATE_LIMITER_ENABLED: "false"
|
||||
ASSETS_TRANSFORM_MAX_CONCURRENT: "2"
|
||||
IMAGE_PREVIEWS_ENABLED: "true"
|
||||
IMAGE_PREVIEWS_CONCURRENCY: "1"
|
||||
LOG_LEVEL: info
|
||||
volumes:
|
||||
- uploads:/directus/uploads
|
||||
- ./extension:/directus/extensions/directus-extension-generate-image-previews
|
||||
|
||||
volumes:
|
||||
db-data:
|
||||
uploads:
|
||||
58
dev/compose.staging.yml
Normal file
58
dev/compose.staging.yml
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# Staging stack template (run on the staging host only — never on prod).
|
||||
#
|
||||
# 1. Restore an anonymized DB dump into the `db` service.
|
||||
# 2. Copy (or rsync) a subset of prod uploads into the `uploads` volume.
|
||||
# 3. Deploy the extension with dev/deploy-staging.sh, then start this stack.
|
||||
#
|
||||
# Keep ports/volumes/network distinct from the prod stack.
|
||||
|
||||
name: land-nest-staging
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_USER: directus
|
||||
POSTGRES_PASSWORD: change-me
|
||||
POSTGRES_DB: directus
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U directus"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 20
|
||||
|
||||
directus:
|
||||
image: directus/directus:11.17.4
|
||||
ports:
|
||||
- "8056:8055"
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
KEY: change-me
|
||||
SECRET: change-me-to-at-least-32-bytes-long
|
||||
ADMIN_EMAIL: staging@example.com
|
||||
ADMIN_PASSWORD: change-me
|
||||
DB_CLIENT: pg
|
||||
DB_HOST: db
|
||||
DB_PORT: "5432"
|
||||
DB_DATABASE: directus
|
||||
DB_USER: directus
|
||||
DB_PASSWORD: change-me
|
||||
STORAGE_LOCATIONS: local
|
||||
STORAGE_LOCAL_ROOT: /directus/uploads
|
||||
EXTENSIONS_AUTO_RELOAD: "true"
|
||||
CACHE_ENABLED: "false"
|
||||
RATE_LIMITER_ENABLED: "false"
|
||||
IMAGE_PREVIEWS_ENABLED: "true"
|
||||
IMAGE_PREVIEWS_CONCURRENCY: "1"
|
||||
LOG_LEVEL: info
|
||||
volumes:
|
||||
- uploads:/directus/uploads
|
||||
- ./extensions:/directus/extensions
|
||||
|
||||
volumes:
|
||||
db-data:
|
||||
uploads:
|
||||
21
dev/deploy-staging.sh
Executable file
21
dev/deploy-staging.sh
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env bash
|
||||
# Deploy the built extension to a STAGING Directus extensions dir. Never target prod.
|
||||
# Usage: ./dev/deploy-staging.sh <ssh-host> <staging-extensions-root>
|
||||
set -euo pipefail
|
||||
|
||||
HOST="${1:?usage: deploy-staging.sh <ssh-host> <staging-extensions-root>}"
|
||||
EXT_ROOT="${2:?usage: deploy-staging.sh <ssh-host> <staging-extensions-root>}"
|
||||
NAME="directus-extension-generate-image-previews"
|
||||
STAMP="$(date +%Y%m%d-%H%M%S)"
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
npm run build
|
||||
|
||||
ssh "$HOST" "mkdir -p '$EXT_ROOT/$NAME' && if [ -d '$EXT_ROOT/$NAME/dist' ]; then cp -a '$EXT_ROOT/$NAME/dist' '$EXT_ROOT/$NAME/dist.bak-$STAMP'; fi"
|
||||
|
||||
rsync -az --delete dist/ "$HOST:$EXT_ROOT/$NAME/dist/"
|
||||
scp package.json "$HOST:$EXT_ROOT/$NAME/package.json"
|
||||
|
||||
echo "deployed to $HOST:$EXT_ROOT/$NAME (backup stamp: $STAMP)"
|
||||
echo "restart staging Directus, then check: docker logs <staging-directus> | grep -i 'loaded extensions'"
|
||||
13
dev/rollback-staging.sh
Executable file
13
dev/rollback-staging.sh
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env bash
|
||||
# Roll back a staging deploy to a previous dist backup.
|
||||
# Usage: ./dev/rollback-staging.sh <ssh-host> <staging-extensions-root> <backup-stamp>
|
||||
set -euo pipefail
|
||||
|
||||
HOST="${1:?usage: rollback-staging.sh <ssh-host> <staging-extensions-root> <backup-stamp>}"
|
||||
EXT_ROOT="${2:?usage: rollback-staging.sh <ssh-host> <staging-extensions-root> <backup-stamp>}"
|
||||
STAMP="${3:?usage: rollback-staging.sh <ssh-host> <staging-extensions-root> <backup-stamp>}"
|
||||
NAME="directus-extension-generate-image-previews"
|
||||
|
||||
ssh "$HOST" "test -d '$EXT_ROOT/$NAME/dist.bak-$STAMP' && rm -rf '$EXT_ROOT/$NAME/dist' && cp -a '$EXT_ROOT/$NAME/dist.bak-$STAMP' '$EXT_ROOT/$NAME/dist'"
|
||||
|
||||
echo "rolled back $HOST:$EXT_ROOT/$NAME to dist.bak-$STAMP"
|
||||
BIN
dev/seed/test-large.jpg
Normal file
BIN
dev/seed/test-large.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 498 KiB |
BIN
dev/seed/test.jpg
Normal file
BIN
dev/seed/test.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
13
dev/sync-extension.sh
Executable file
13
dev/sync-extension.sh
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
npm run build
|
||||
|
||||
mkdir -p dev/extension
|
||||
rm -rf dev/extension/dist
|
||||
cp -r dist dev/extension/dist
|
||||
cp package.json dev/extension/package.json
|
||||
|
||||
echo "synced dev/extension (in place)"
|
||||
Loading…
Add table
Add a link
Reference in a new issue