Adds a new mode that trims uniform-color borders around the subject,
like GIMP's "Crop to Content." Includes configurable tolerance threshold
and optional pad-to-square with target size for e-commerce workflows.
The original attention-based crop is preserved as "Focus Crop" mode.
Closes#7
- Health endpoint returns "healthy" instead of "ok" for consistency
- MAX_USERS now configurable via env var (default 5)
- People API returns team names instead of UUIDs in register/list
- PUT user update accepts team names (name-first lookup, fallback to ID)
- Login rate limit follows global rate limit when RATE_LIMIT_PER_MIN > 1000
- Strip-metadata preserves original format encoding instead of always PNG
- Fix e2e tests: rotate/crop/border button selectors match actual UI
- Fix e2e tests: create Engineering/Design teams in people test setup
- Fix e2e tests: people UI uses select for team field, not text input
- Update visual regression baseline for tablet home page
Worker thread initialization imports the tool registry which reads SQLite.
Under Docker volume filesystems, this can deadlock silently on SQLITE_BUSY,
causing APPLY to spin at 0% forever. Sharp operations complete in milliseconds
and don't need worker offloading. Added 30s AbortSignal timeout as defense
in depth for future re-enablement.
SVG files were rejected by the convert endpoint because
validateImageBuffer only recognized raster magic bytes. This adds
text-based SVG detection, sanitization in the tool factory, and
proper Sharp density handling so SVG-to-raster conversion works
through the standard convert route.
Update all references across docs, workflows, UI components, and config
to point to the new GitHub org (stirling-image/stirling-image) and Docker
Hub account (stirlingimage/stirling-image) ahead of repo transfer.
Add a privacy policy page accessible at /privacy (public, no auth required).
Relax Content-Security-Policy for /api/docs route to allow Scalar's inline
script initialization, fixing blank docs page in production.
Remove DB probe from public health endpoint - it only needs to confirm
the process is alive. Add test for non-admin user getting 403 on admin
health endpoint.
Public GET /api/v1/health now returns only status and version.
Full diagnostics (uptime, storage, database, queue) moved to
GET /api/v1/admin/health which requires admin authentication.
Validation now runs on all entries before any database writes.
Previously, clean entries could be written before a later malicious
entry triggered a 400 response.
PUT /api/v1/settings now returns 400 if any key or value contains HTML
tags. Settings are configuration values - there is no legitimate use
case for HTML in them.
Register remove-background, upscale, and blur-faces in the pipeline
tool registry via registerToolProcessFn(). These tools keep their
custom HTTP routes (with progress callbacks) for direct use, but now
also provide a simple process function for pipeline/batch execution.
Add a search bar to the pipeline tool picker so users can quickly
find tools by name or description. Uses the existing SearchBar
component and the same filtering pattern as the main tool panel.
Update tests to reflect that these 3 AI tools are now pipeline-
compatible (moved from excluded to included assertions).
The pipeline tool picker was showing all tools, but only tools
registered via createToolRoute() support pipeline execution. Tools
with custom routes (remove-background, upscale, ocr, etc.) would
silently fail with "Tool not found" and the empty catch block hid
the error from users.
Add GET /api/v1/pipeline/tools endpoint that returns the IDs of
pipeline-compatible tools. The frontend fetches this list and filters
the tool picker accordingly. Also surface pipeline execution errors
in the UI instead of swallowing them.
Drizzle's migrate() throws when multiple vitest workers race to apply
migrations on the same temp database. The DrizzleError wraps a
SqliteError ("table already exists") in its cause chain. Add a
same-process guard and a catch that checks both the outer message and
cause for "already exists" so the second worker continues safely.
Remove hardcoded --platform=linux/amd64 from Dockerfile so buildx produces
native arm64 images for Apple Silicon and Raspberry Pi. Add audit logging
for auth events, harden file storage with extension whitelists and
double-extension attack prevention, reject null-byte buffers in validation,
add data-testid attributes to all tool settings components, update
deployment docs with architecture notes and correct CI workflow references,
and fix unit test mock to match throwWithMessage error extraction.
- Backend: look up teams by name first (frontend sends name, not ID)
- Frontend: parse response body on API errors instead of showing
generic "API error: 400" — now shows the actual server message
(e.g. "Password must be at least 8 characters...")
Fix face detection failure caused by MediaPipe 0.10.33 removing the
mp.solutions API. Replace with OpenCV Haar cascade which works reliably
in headless Docker. Add autoOrient() call before detection to handle
EXIF-rotated phone photos. Remove technical jargon from UI.
Fix rotated output by calling autoOrient() before passing images to
the Python upscaler, correcting EXIF orientation metadata. Replace
2x/4x buttons with 2x-8x slider plus quick-select buttons (2x, 3x,
4x, 6x, 8x). Remove technical jargon about Real-ESRGAN/Lanczos from
the UI and progress bar.
- Make ensureDefaultAdmin idempotent with onConflictDoNothing (fixes
UNIQUE constraint error when parallel test files share a DB)
- Move duplicate-username check before user-limit check so 409 takes
priority over 403
- Bump MAX_USERS from 5 to 50 (tests create ~15 users across files)
Remove docs/superpowers/, .claude/ config, and PRD.md from version
control (kept locally via .gitignore). Update README, CHANGELOG,
VitePress docs, and .env.example to reflect recent features: Files
page, teams, admin settings, persistent storage, and various API
improvements.
Add feature flag support to skip disabled/experimental tools at startup
by reading disabledTools and enableExperimentalTools from the settings
table. Refactor cleanup.ts to read tempFileMaxAgeHours from DB settings
(with env var fallback) and respect the startupCleanup setting.
Adds optional fileId multipart field; when present, inserts a new versioned
userFiles record after successful processing and returns savedFileId in the
response. Non-fatal — tool processing succeeds even if the save fails.
Implements all 7 routes for the persistent file library:
list with pagination/search, upload (multi-file), details with recursive
version chain CTE, download stream, on-the-fly Sharp thumbnail, bulk delete
of entire version chains, and save-result for tool output versioning.
Registered in index.ts after fileRoutes.
Adds ensureStorageDir, saveFile, deleteStoredFile, and getStoredFilePath
utilities that manage the lifecycle of files on the local filesystem
under FILES_STORAGE_PATH.
Extract EXIF auto-orientation logic into a shared auto-orient module
used by both single-tool and batch routes. This ensures camera photos
display correctly after processing regardless of entry point.
Also expands e2e and integration tests significantly.
- Remove @fastify/swagger and @fastify/swagger-ui (API docs live on GitHub Pages)
- Run typecheck, build, and docker CI jobs in parallel instead of sequentially
Samsung Galaxy phones (and others) can have GPS EXIF tags with null
coordinate values when location is disabled. The server returned
_latitude: null, and the client guard used !== undefined which passed
for null, causing null.toFixed() to crash React with no ErrorBoundary.
- Server: validate GPS array values are actual numbers before computing
- Client: use != null guard (catches both null and undefined)
- App: add ErrorBoundary to prevent white screens from any future crash
Integrates feature/multi-image-ux branch with 20 commits including:
- Multi-image viewer with arrow navigation and filmstrip thumbnails
- Batch processing across all tool settings components
- File store rewrite with FileEntry model for multi-image support
- Resize settings redesigned with tab-based UI (presets, custom, scale)
- Side-by-side comparison for resize results
- Per-file metadata caching in strip-metadata
- Client-side ZIP extraction via fflate
- SSE progress correlation via clientJobId
Consolidate all access to localhost:1349 — Vite dev server serves on 1349
and proxies API calls to an internal dev port (13490). Production API
defaults to 1349. Also includes strip-metadata improvements, UI component
updates, and compress operation fixes.
The /api/v1/jobs/ endpoint was blocked by auth middleware.
EventSource doesn't support custom headers so auth tokens can't be
sent. The jobId is a random UUID (unguessable capability token), same
security model as the download endpoint.