Migrate settings, teams, branding, and user management routes to use
the new permission-based authorization system. Remove requireAdmin
function entirely.
- Replace OpenCV Haar Cascades with MediaPipe for face detection, using
short-range model first with full-range fallback for better accuracy
- Add auto-orient to remove-background route for EXIF-rotated photos
- Change default background removal model from u2net to birefnet-general-lite
- Fix flaky test by setting SQLite busy_timeout before journal_mode pragma
Co-authored-by: Siddharth Kumar Sah <siddharth123sk@gmail.com>
Batch progress was broken because JobProgress events lacked a `type`
field. The frontend checks `data.type === "batch"` to distinguish batch
from single-file SSE events, so batch progress was silently discarded
and multi-file processing appeared stuck at 15%.
Also improves the processing UX for non-AI (Sharp-based) tools: the
progress bar now pulses during the server processing phase and shows
a "This may take a moment" hint after 10 seconds.
Co-authored-by: Siddharth Kumar Sah <siddharth123sk@gmail.com>
AI routes (remove-background, erase-object, ocr, blur-faces, upscale)
were silently swallowing errors - failures returned HTTP 422 to the
client but never appeared in server logs. This made it impossible for
self-hosters to diagnose issues like 504 timeouts from reverse proxies.
Adds request.log.info() at processing start (tool name, image size, key
settings) and request.log.error() in catch blocks, matching the existing
tool-factory pattern.
Co-authored-by: Siddharth Kumar Sah <siddharth123sk@gmail.com>
* feat: add resolveOutputFormat utility for input format preservation
* fix: preserve file order in batch processing with X-File-Results header
Collect all results before streaming the ZIP to guarantee upload order.
Replace X-File-Order with index-based X-File-Results header that maps
each upload index to its processed filename, handling failures and
duplicate filenames correctly.
Closes#13
* fix: use X-File-Results for index-based batch file matching
The frontend now matches processed files to entries by upload index
instead of fragile name/position matching.
* feat: preserve input format in smart-crop with quality control
Smart crop now outputs in the same format as the input (JPG in, JPG out)
instead of always converting to PNG. Adds an optional quality setting
(default 95) for lossy formats.
Closes#14
* feat: add output quality slider to smart crop settings UI
* feat: preserve input format in crop tool
* feat: preserve input format in color adjustment tools
Applies to brightness-contrast, saturation, color-channels, and
color-effects tool routes.
* refactor: avoid double encode in smart-crop content mode
For the simple trim path (no pad-to-square), chain .toFormat() on the
trim pipeline directly instead of creating a second Sharp instance.
This eliminates a redundant intermediate encode that degraded quality
for lossy formats. Also use trimmed.info dimensions instead of a
separate metadata() call for the pad-to-square path.
---------
Co-authored-by: Siddharth Kumar Sah <siddharth123sk@gmail.com>
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.
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.
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.
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.
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.
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.
Extract clientJobId from multipart form data in all 5 AI route handlers
(remove-background, upscale, blur-faces, erase-object, ocr) and forward
progress callbacks to the SSE system via updateSingleFileProgress.