Commit Graph
123 Commits
Author SHA1 Message Date
SnapOtter ca2ef5b3f4 feat: add OIDC/SSO authentication (#3)
Add OpenID Connect (OIDC) authentication alongside existing
username/password login. Users can log in via any standards-compliant
OIDC provider (Keycloak, Authentik, Authelia, Google, Azure AD, Okta)
while preserving full backward compatibility.

- OIDC Fastify plugin with lazy discovery, PKCE, cookie-based sessions
- Login page OIDC button, auth hook updates, settings dialog badges
- 28 integration tests, OIDC setup guide with provider examples
- Fix pre-existing test failures (content-aware-crop, watermark, SVGZ)
- WAL checkpoint fix for SQLite test stability

Closes #3

# Conflicts:
#	apps/api/src/lib/env.ts
#	apps/api/src/routes/tools/watermark-image.ts
#	pnpm-lock.yaml
#	tests/integration/color-palette.test.ts
#	tests/integration/compare.test.ts
#	tests/integration/watermark-image.test.ts
2026-05-14 22:31:26 +08:00
SnapOtter 4e64ee2779 fix(security): comprehensive security audit and hardening
Auth: login rate limit 30/min (was 500), global rate limit 1000/min (was
unlimited), password/username max lengths on all Zod schemas, session
invalidation on role change, API key legacy scan bounded to 100 keys.

SVG: hardened regex sanitizer with CDATA stripping, XML entity decoding,
set/animate/iframe/embed blocking, comprehensive data: URI blocking,
use element external href blocking. 11 attack payload fixtures added.

SSRF: fixed DNS rebinding TOCTOU by pinning resolved IPs via custom
HTTP/HTTPS agents. Added 6to4 and NAT64 to blocked IPv6 ranges.

Docker: capability dropping (cap_drop ALL + minimal cap_add), resource
limits (4g/8g mem, 512/1024 pids), healthcheck timeout, password
removed from startup banner, default password warning comments.

Network: CSP and HSTS applied in all environments (not just production),
stack traces removed from all error responses, internal paths stripped
from error details, per-route rate limits on uploads (60/min) and URL
fetches (200/hour).

Files: exclusive temp file creation (O_EXCL), disk space circuit
breaker, per-user storage quotas, settings payload 64KB size guard.

Python sidecar: script name allowlist in dispatcher, minimal environment
for subprocess spawns.

Dependencies: fixed 6 production CVEs (drizzle-orm, fastify, fast-uri,
@fastify/static, next, archiver/lodash). Pinned all GitHub Actions to
SHA hashes.

114 security tests added. Full OWASP Top 10 penetration test matrix
verified against production Docker container (30/30 pass after
hardening).
2026-05-13 21:33:50 +08:00
SnapOtter 2f41629a14 fix: resolve pre-existing test failures for content-aware-crop removal and watermark validation
- Replace content-aware-crop with ai-canvas-expand in TOOLS[], AI_TOOL_IDS,
  and FEATURE_BUNDLES (matching the already-updated tool-registry.tsx and
  feature-manifest.json from commit c6a5d3f)
- Fix trailing syntax error in features.ts (extra closing brace)
- Add ai-canvas-expand-settings mock to tool-registry test files
- Update watermark-image tests to expect 400 (validation rejection) instead
  of 422 (processing failure) for corrupted image buffers, matching the
  actual route behavior where validateImageBuffer catches them first
2026-05-13 21:15:44 +08:00
SnapOtter 693b441aa0 refactor(ai): remove mode, add colorizeStrength to restore options 2026-05-13 16:55:38 +08:00
SnapOtter e1ca06cd95 fix: update unit tests for DecodedPreview return type and outpaint tier arg 2026-05-13 15:53:09 +08:00
SnapOtter 648c8c12f5 fix: convert input buffer to PNG before passing to face landmarks Python sidecar
AVIF (and other Sharp-native formats) were written as raw bytes to a
.png temp file, causing PIL to fail with "cannot identify image file".
Every other AI module wrapper already converts via sharp().png().toBuffer()
before writing; face-landmarks was the only one that skipped this step.
2026-05-13 14:18:49 +08:00
SnapOtter bf01dcd47e fix: make beautify live preview reflect all settings in real time
Background images, device frames, custom shadows, and watermark text
were not rendering in the right-pane preview. The preview now updates
in real time for all settings: gradient/solid/image backgrounds, macOS/
Windows/Browser frame chrome, iPhone/MacBook/iPad frame indicators,
custom shadow parameters, and watermark text overlay.

Also fixes a React StrictMode effect-ordering race where the parent
tool-page reset cleared preview state set by the child Settings
component on initial mount.
2026-05-13 10:28:44 +08:00
SnapOtter 7a2ea2069b fix: clean up crop mode toggle, extend timeouts for long-running tools
- Remove content-aware crop mode toggle from tool-page (now handled internally)
- Add content-aware-resize/crop to LONG_RUNNING_TOOLS for extended XHR timeout
- Remove unused onModeChange prop from CropProps
- Bump HEIC converter test timeouts to 60s to fix flaky failures
2026-05-11 22:48:05 +08:00
SnapOtter 095e3d9488 feat: add URL-based image import (single + bulk)
Add a fourth image ingestion path: importing images by URL.

Backend:
- POST /api/v1/fetch-urls endpoint with SSRF protection, image validation,
  preview generation, and p-queue concurrency
- SSRF utility blocking private IPs, validating redirect hops, with
  comprehensive IPv4/IPv6 range coverage

Frontend:
- Always-visible URL input in the dropzone for quick single-image import
- Bulk URL import modal with smart URL parsing (lists, markdown, HTML),
  per-URL progress tracking, retry on failure, and batch add
- useUrlImport hook managing the full fetch lifecycle

Tests: 48 new tests (23 SSRF unit, 10 URL parser unit, 15 integration)
2026-05-11 22:41:47 +08:00
SnapOtter 04e7b21f31 test: improve coverage for URL import feature 2026-05-11 22:39:37 +08:00
SnapOtter b06906025c refactor: improve tool processing, dropzone, seam carving, and format encoding
- Refactor use-tool-processor and use-pipeline-processor hooks
- Enhance dropzone component with improved UX
- Improve seam carving with better error handling and tests
- Add JXL format encoding support to format-encoders
- Update tool routes for consistent format handling
- Add dropzone unit tests
2026-05-11 21:57:40 +08:00
SnapOtter 485a2d72d3 fix: harden SSRF validation with missing IP ranges and safeFetch tests 2026-05-11 21:29:58 +08:00
SnapOtter a2be47bd68 feat: add smart URL parser for bulk import 2026-05-11 21:18:45 +08:00
SnapOtter 488ed95686 feat: add SSRF validation utility for URL fetch 2026-05-11 21:18:23 +08:00
SnapOtter b1d189d325 test: add unit tests for outpainting bridge 2026-05-11 21:16:35 +08:00
SnapOtter 7d9612784e test: expand exotic format coverage across batch, validation, and detection
Batch processing (batch.test.ts):
- 15 format tests (PBM, PGM, PPM, TIFF, QOI, JP2, SVGZ, DDS, DPX, EPS,
  TGA, PSD, HDR, ICO, CUR) verifying batch compress produces valid ZIP
- 2 delegate-dependent tests (FITS, EXR) that accept 200 or 422
- Mixed-format batch test (PBM + TIFF + QOI in one request)

Validation (utilities.test.ts):
- 15 new tests for validateImageBuffer covering PBM, PGM, PPM, DDS, DPX,
  FITS, JP2, QOI, SVGZ, EPS, CUR, HEIF, APNG, DNG + PDF rejection

Format detection (detect.test.ts):
- 8 fixture-based detection tests (PPM, DNG, JP2, SVGZ, PBM, PGM, HDR, TGA)
- 4 synthetic magic byte tests (PPM P3/P6, JP2 box, J2K codestream)
2026-05-11 17:45:42 +08:00
SnapOtter e6a35265ef fix: target-size compression now respects the target
The binary search found the right quality but sharp(buffer).toBuffer()
re-encoded at default quality 80, inflating the output (e.g. 50KB target
producing 90KB). Replaced buffer-wrapping with proper Sharp pipelines
that include .toFormat() with the proven quality. Also added progressive
dimension reduction when quality alone cannot reach the target, and
tightened tolerance to only accept at-or-below-target results.
2026-05-11 16:19:40 +08:00
SnapOtter 172c0fa772 fix: update tool count to 51, refresh hero copy and login page
- Update stale tool counts (48/49/50) to 51 across all apps, docs, and tests
- Add Meme Generator to landing page bento grid (was missing)
- Add 12 SEO-friendly rotating phrases to hero typing cursor
- Change landing hero subtitle to "Every image tool you need."
- Replace outdated login page copy with branded hero + rotating phrases
2026-05-10 22:18:11 +08:00
SnapOtter a0556772e8 test: expand coverage across all layers -- 1,268 new tests, fix replace-color div-by-zero
14-agent parallel test expansion covering integration, unit, E2E, E2E-Docker,
cross-format matrix, adversarial, GUI navigation/tools/settings/visual/a11y/perf.

- Integration: expand 23 tool test files with HEIC, stress, batch, edge cases
- Unit: close coverage gaps in image-engine, stores, lib (metadata, auto-enhance,
  connection-store, lazy-with-retry, collage/file-store HEIC preview)
- AI bridge: 141 new tests for dispatcher buffering, crash recovery, OOM/segfault
- Cross-format: 794 parameterized tests (16 formats x 12 tools + no-crash matrix)
- Adversarial: memory stress (50x large file), zero-byte, corrupted headers, unicode
- E2E-Docker: expand 8 spec files with dimension verification, pipeline chains
- GUI E2E: tool UI settings/interactions for all 47 tools, remove all test.skip,
  RBAC per-role verification, visual screenshot naming, cross-browser smoke tests,
  a11y ARIA/focus/contrast, performance budgets, 15-tool stability test
- Fix: replace-color.ts tolerance=0 caused division-by-zero producing NaN pixels

Total: 8,958 tests passing across 202 files. Zero failures, zero skips.
2026-05-09 18:02:58 +08:00
SnapOtter 9f97c466b3 fix: resolve flaky cleanup test race condition in CI
The subdirectory removal test used setImmediate ticks to wait for async
cleanup, which was insufficient on slow CI runners. Replaced with real
setTimeout delays (100 x 10ms) to give async readdir/stat/rm I/O time
to complete.
2026-05-09 13:12:11 +08:00
SnapOtter 1fa45f3272 fix: tolerate missing ImageMagick for all format decoder/encoder tests in CI
Wrap BMP, ICO, TGA, PSD, EXR, HDR, JXL, JP2, DDS, CUR, DPX, FITS,
PPM, PGM, PBM decoder tests and BMP encoder tests to gracefully pass
when ImageMagick is not installed in the CI environment.
2026-05-09 13:03:57 +08:00
SnapOtter 266d1553f9 fix: tolerate missing ImageMagick for ICO and JP2 tests in CI
Same pattern as the BMP test fix: catch "No ImageMagick found" errors
and pass gracefully instead of failing the test suite.
2026-05-09 12:26:22 +08:00
SnapOtter f944cc5411 test: add darkening regression tests for image enhancement
Tests verify well-exposed and bright-but-normal images are not
darkened, all modes produce valid output, and intensity slider
produces visibly different results.
2026-05-09 11:33:24 +08:00
SnapOtter b61be9e438 fix: replace enhancement pipeline with CLAHE + normalise + gamma
CLAHE provides adaptive local contrast, normalise stretches the
histogram, and gamma adjusts exposure perceptually. Replaces the old
modulate/linear pipeline that compounded errors and darkened images.
Preset multipliers now include clahe and normalise entries.

Key fixes beyond the spec:
- maxSlope rounded to integer (Sharp requirement)
- White balance uses linear() instead of recomb() to avoid float-cast
  that breaks CLAHE in the libvips pipeline
- CLAHE tile size adapts to image dimensions (1x1 for tiny images)
- Gamma clamped to Sharp's valid range (1.0-3.0)
- Normalise lower/upper correctly mapped to percentile cutoffs
2026-05-09 11:24:00 +08:00
SnapOtter 4bcdc67aa0 fix: make dead zone test deterministic and remove unused parameter
Replace Math.random() with deterministic modular arithmetic to prevent
flaky CI failures. Remove unused index parameter from Array.from callback.
2026-05-09 11:13:04 +08:00
SnapOtter 5434762acc fix: recalibrate enhancement scoring with dead zones
Contrast score now uses linear stdevLum/1.2 centered at 50 (was
miscalibrated 25-75 range centered at 75). Corrections use dead zones
(score 40-60 = zero) so well-exposed images get near-zero adjustments
instead of being darkened.
2026-05-09 11:07:36 +08:00
SnapOtter 649ad5db9e test: massive test coverage expansion (+1,437 tests, 22 new files)
Expand test coverage across all layers via 14 parallel agents:

Unit tests (3,378 total, +534):
- First-ever AI sidecar tests (157 tests covering bridge lifecycle, all 12 tool modules)
- API route infrastructure (auth, pipeline, batch, settings, teams, roles, audit, api-keys, files, docs)
- Lib coverage improvements (audit 7%->95%, worker-pool 33%->100%)
- Web store/lib gap fills (features-store, tool-registry)

Integration tests (4,403 total, +903):
- Expanded 19 tool test files with parameter variations, format edge cases, boundary values
- Cross-format matrix: 290 tests covering 14 tools x 17 formats
- Adversarial/edge cases: 63 tests for extreme inputs, concurrent requests, corrupted files

E2E-Docker (125 new tests):
- Expanded 8 spec files + 1 new file covering all 49 tools
- Added HEIC/format handling, auth failures, download verification

GUI E2E (expanded 28 spec files):
- Navigation, responsive layout, keyboard shortcuts
- All 51 tool UIs with settings, processing, display modes
- Batch/pipeline workflows, settings/RBAC, visual regression
- Resilience, accessibility (ARIA, contrast, focus), performance budgets
2026-05-09 09:02:29 +08:00
SnapOtter 4f0fbade6d test: expand unit test coverage (+437 tests, 17 new files)
Add comprehensive unit tests for previously uncovered API lib modules,
web stores, and plugin functions. Fix 2 pre-existing editor-store test
failures (invertSelection mask values).

Coverage: 30.3% -> 36.85% stmts (unit), 57.71% stmts (integration).
2026-05-09 07:35:27 +08:00
SnapOtter e3e5fd5890 Merge branch 'testing/image-editor' into main
Merges all image editor work: RAW decode improvements, expanded format
export (AVIF/TIFF/GIF/JXL/PSD), Photoshop-style menu bar, custom Konva
filters, smart guides, clone stamp, dodge/burn, eyedropper, pixel brush,
selection and transform tool overlays, autosave blob URL persistence,
and 49+ bug fixes across editor canvas and E2E tests.

# Conflicts:
#	apps/api/src/routes/tool-factory.ts
#	apps/api/src/routes/tools/convert.ts
#	apps/web/src/components/editor/common/export-dialog.tsx
#	apps/web/src/components/editor/editor-canvas.tsx
2026-05-08 21:37:52 +08:00
SnapOtter a1a71e507f fix: 8 image editor bugs found during Docker-based E2E testing
- fix WebP export silently producing PNG when background is non-transparent
- fix autosave not converting blob: URLs inside image-type canvas objects
- fix project load not resetting selection/crop/clipboard state
- fix rotateCanvas not updating object rotation attributes
- fix flipCanvas not negating object rotation attributes
- fix line shadow props overridden by effect spread ordering
- fix "outside" stroke position rendering same as "center"
- add missing pencil tool keyboard shortcut (N)
- remove misleading resample dropdown from image resize dialog
- fix E2E autosave tests for production builds (no Vite dynamic imports)
- fix color picker test case sensitivity (CSS uppercase vs DOM text)
- add 4 unit tests for rotation attribute transforms
2026-05-08 20:27:06 +08:00
SnapOtter aa773addb4 merge: resolve conflict with main branch in tool-registry.tsx 2026-05-08 18:55:28 +08:00
SnapOtter e118717337 merge: resolve conflict with main branch in README.md 2026-05-08 17:32:16 +08:00
SnapOtter dd73a8a50a fix: resolve 41 bugs and wire 21 unimplemented features in image editor
Canvas rendering:
- Fix Konva filter application order (filters before cache)
- Implement 6 missing filters (motionBlur, radialBlur, surfaceBlur, vignette, grain, sharpen)
- Implement exposure, vibrance, warmth adjustments as custom Konva filters
- Apply layer blend modes via globalCompositeOperation
- Apply object effects (drop shadow, outer glow, stroke) to all shapes
- Mount SmartGuidesOverlay during move tool drag
- Clip pixel grid to visible viewport (200-line cap for performance)

Store logic:
- resizeImage now scales all objects proportionally (points, radii, fontSize)
- rotate/flip/trim handle line/arrow points arrays and center-based objects
- applyCrop creates cropped source image via offscreen canvas
- invertSelection creates mask from bounds when no mask exists
- cutObjects uses single atomic set() to prevent race conditions
- sendToBack respects layer ordering in multi-layer documents
- Add batchNudge() and commitHistory() for undoable nudge operations
- Add updateLayerThumbnail() method

Tool hooks:
- Fix clone stamp/dodge/burn perf (toDataURL only on mouseUp, not every move)
- Fix magic wand zoom/pixelRatio with explicit stage.toCanvas() viewport
- Fix eyedropper sampling with unzoomed canvas export
- Fix selection tool stale closure via isDrawingRef
- Implement polygonal lasso (click-to-place vertices, double-click to close)
- Implement selection subtract mode (geometric and mask-based)
- Implement gradient live preview during drag
- Fix transform/move tool to persist changes and handle ellipse/polygon/star

UI wiring:
- Mount rulers and guidelines in editor page
- Wire histogram with live canvas imageData
- Wire autosave recovery with blob-to-dataURL conversion
- Wire fill dialog to Shift+Backspace shortcut
- Wire eyedropper and transform options to options bar
- Fix history panel undo/redo button reactive state via useSyncExternalStore
- Fix layer row name click to select layer (timer-based click/dblclick)
- Fix zoom animation coordinate drift with progressive store sync
- Fix copy merged to use Konva stage composite export

Tests:
- 49 new unit tests (store fixes + konva filters)
- 8 new E2E test files with 39 test cases
2026-05-08 16:43:27 +08:00
SnapOtter 3d9324b82a chore: update tool count to 50 across all references 2026-05-08 16:33:12 +08:00
SnapOtter 53b717243b fix(meme-generator): add opentype.js types, fix no-dropzone registry test 2026-05-08 16:29:44 +08:00
SnapOtter 136a4a0752 Merge branch 'feat/format-support-expansion'
# Conflicts:
#	README.md
#	apps/web/src/components/layout/app-layout.tsx
2026-05-08 16:21:11 +08:00
SnapOtter 69e107fbab feat(beautify): add device frame assets and PNG frame compositing 2026-05-08 16:12:46 +08:00
SnapOtter d8c1fdf5c2 feat(beautify): add SVG frame generators (macOS, Windows, browser) 2026-05-08 16:09:31 +08:00
SnapOtter 39358524cd feat(beautify): add shadow rendering (extracted from border.ts pattern) 2026-05-08 16:06:37 +08:00
SnapOtter b13df56273 feat(beautify): add background generation (solid, gradient, image, transparent) 2026-05-08 16:04:34 +08:00
SnapOtter 3d103d5092 test(meme-generator): add template manifest validation tests 2026-05-08 16:03:21 +08:00
SnapOtter 9dff59c400 feat(meme-generator): add text renderer with opentype.js SVG path generation 2026-05-08 16:01:46 +08:00
SnapOtter 5deb25c9e3 test: expand format decoder and detection unit tests for all new formats 2026-05-08 15:18:04 +08:00
SnapOtter 93a246ce23 test: add QOI codec unit tests and E2E GUI format support tests
- QOI codec: 16 tests covering encode/decode round-trips, error handling, fixture validation
- E2E: 28 tests for format upload/resize, convert new outputs, dropdown verification,
  HEIC-to-JPG flow, and exotic format upload acceptance
2026-05-08 15:16:06 +08:00
SnapOtter 0b6a970912 feat: add color blindness simulation image-engine operation
Add colorBlindness() operation with 8 simulation matrices (Vienot/Machado)
for protanopia, deuteranopia, tritanopia, protanomaly, deuteranomaly,
tritanomaly, achromatopsia, and blue cone monochromacy.
2026-05-08 15:11:36 +08:00
SnapOtter e99bae428a fix: resolve 50+ bugs across editor store, canvas, panels, and tools
- Store: loadImage resets state, applyCrop updates sourceImageSize,
  setTool clears stale cropState, layer ordering preserved across
  layers, rotateCanvas/flipCanvas account for object dimensions,
  trimCanvas implemented, resizeCanvas supports anchor positioning
- Canvas: crop overlay interactive, move tool drag events wired,
  hand tool panning, stage ref effect mount-only, checkerboard
  tracks pan offset, filter cache clearing
- Panels: hslToHex color fix for hue 240-360, adjustments single
  setAdjustment call, curves draggingIndex after sort, history
  panel fresh temporal reads, export transparent setting, canvas
  resize dialog re-sync, hex input respects picker target
- Tools: text tool fixed positioning and event cleanup, shape tool
  deferred addObject, selection mode wired to store, magic wand
  tolerance controlled, eyedropper coordinate transform
- Options: dodge-burn/shape/clone/fill/gradient/pixel-brush use
  proper store actions instead of raw setState
- Added 20+ store actions for tool settings
- Updated tests for corrected rotation/flip/trim behavior
- Excluded e2e-editor from vitest config
2026-05-07 23:47:08 +08:00
SnapOtter 1669e34909 Merge fix/129-face-enhancement-mediapipe: add mediapipe to upscale-enhance bundle
fix: add mediapipe to upscale-enhance bundle for face enhancement

The enhance-faces tool requires MediaPipe for face detection, but the
upscale-enhance feature bundle did not include mediapipe in its pip
packages. Added mediapipe to both amd64 and arm64 package lists.

Added 25 regression tests validating bundle dependency completeness.

Closes #129
2026-05-07 22:24:03 +08:00
SnapOtter 6fc767523c fix: add mediapipe to upscale-enhance bundle for face enhancement
The enhance-faces tool requires MediaPipe for face detection, but the
upscale-enhance feature bundle did not include mediapipe in its pip
packages. Users who installed only the upscale-enhance bundle got
"Face detection requires MediaPipe" errors. Added mediapipe to both
amd64 and arm64 package lists, matching the pattern used by the
face-detection and photo-restoration bundles.

Also added feature-manifest.test.ts with 25 tests validating bundle
dependency completeness to prevent similar missing-dependency bugs.

Closes #129
2026-05-07 22:21:45 +08:00
SnapOtter 14810c3c78 fix: prevent repeated redirect when switching from grid to sidebar view
The default-view redirect in HomePage fired on every mount, not just the
initial page load. A module-level flag now gates the redirect so it only
applies once per session, allowing users to switch to sidebar view when
grid is the default.

Closes #128
2026-05-07 22:16:26 +08:00
SnapOtter 16af9c573a feat: remove app name and logo customization feature
Users can no longer customize the app name or logo. The branding API
endpoints, permission, frontend UI, env vars (APP_NAME, MAX_LOGO_SIZE_KB),
and all related tests are removed. Includes a migration to clean up
branding data from existing databases.
2026-05-07 19:41:30 +08:00