Commit Graph
280 Commits
Author SHA1 Message Date
ashim-hq be4155d257 fix: replace require() with dynamic import() for analytics modules
PostHog and Sentry were silently disabled in production Docker builds.
The app runs as ESM ("type": "module") via tsx, where require() is not
defined. The catch blocks swallowed the ReferenceError, leaving both
clients as null. Switch to await import() and store the Sentry module
reference for use in the error handler.
2026-04-23 21:45:10 +08:00
ashim-hq 8aea77b4be docs: remove hardcoded pipeline/batch limits, default to unlimited
Pipeline steps and batch size are now unlimited by default. The old
"20 steps" and "200 images" figures had no basis in the actual code
(MAX_BATCH_SIZE already defaulted to 0/unlimited). Both remain
configurable via MAX_PIPELINE_STEPS and MAX_BATCH_SIZE env vars.

Also includes updated hardware requirements and sidebar nav from
prior documentation audit.
2026-04-23 20:50:38 +08:00
AshimandGitHub 97938bdc47 feat: API sync and documentation audit - 100% endpoint coverage (#94)
Code quality:
- Add Zod validation to 14 route handlers that used raw JSON.parse
  (favicon, find-duplicates, barcode-read, upscale, blur-faces,
  erase-object, colorize, enhance-faces, red-eye-removal,
  remove-background/effects, auth, api-keys, roles, teams,
  analytics, settings, user-files)
- Standardize error responses to safeParse + formatZodErrors pattern
- Replace unsafe `as` type casts with schema validation

OpenAPI spec (89 -> 115 operations):
- Add 14 missing tool endpoints (adjust-colors, sharpening,
  optimize-for-web, image-enhancement, noise-removal, red-eye-removal,
  restore-photo, passport-photo, colorize, enhance-faces, image-to-base64)
- Add 12 missing non-tool endpoints (analytics, features, audit-log,
  roles, admin-health)
- Add typed error schemas for 401/403/409 responses
- Add descriptions to all path parameters
- Bump version from 0.9.0 to 1.15.9

Documentation:
- Fix 8 incorrect env var defaults in configuration guide
- Add 15 undocumented env vars to configuration guide
- Fix tool ID mismatch (color-adjustments -> adjust-colors)
- Add 4 new API sections (Roles, Audit Log, Analytics, Features)
- Add image-enhancement to AI engine reference
- Update AI tool count from 13 to 14 across all docs
- Add 6 missing doc links to README
2026-04-23 20:26:58 +08:00
AshimandGitHub 136a4dd641 Merge pull request #93 from ashim-hq/test/comprehensive-coverage
test: comprehensive test coverage expansion (+965 tests, 48/48 tools)
2026-04-23 17:12:53 +08:00
ashim-hq babca4cf97 test: comprehensive test coverage expansion (+965 tests)
Add 42 new test files covering all untested tool routes, image engine
internals, AI sidecar bridge, Zustand stores, and cross-format
compatibility. Expand e2e-docker suite with 7 spec files covering all
48 tools against a real Docker container.

Unit tests:
- Image engine: format detection, MIME mapping, metadata parsing, pipeline
- AI bridge: sidecar lifecycle, all 11 tool functions (mocked)
- Web stores: 14 Zustand stores (collage, settings, features, analytics, etc.)
- API helpers: format decoders, page range, file validation

Integration tests:
- 25 tool routes that had zero dedicated tests
- Cross-format matrix: 17 input formats x 3 tools
- Edge cases: zero-byte files, corrupted headers, path traversal, XSS, SQL injection
- Concurrent request handling and pipeline edge cases

E2E-Docker (Playwright against real container):
- 7 spec files: essential, adjustment, conversion, creative, utility, AI, pipeline
- Custom buildMultipart helper for multi-file tool uploads
- AI tools gracefully skip when sidecar not installed

Fixtures:
- Organized test media: formats/ (18 formats) + content/ (17 content types)
- Reduced from 3.1 GB unorganized samples to 33 MB structured fixtures

Bug fix:
- color-adjustments: gamma exposure used invalid single-param gamma() for
  positive values; fixed to use two-param gamma(gammaIn, gammaOut) form
2026-04-23 17:12:02 +08:00
ashim-hq 7047ce5fae fix: prevent admin escalation when AUTH_ENABLED=false
When auth was disabled, users could log out, reach the login page,
and authenticate with the default admin/admin credentials to gain
full admin privileges — defeating the purpose of AUTH_ENABLED=false.

Defense-in-depth fix across five layers:
- Skip ensureDefaultAdmin() when auth is disabled (no admin user seeded)
- Return 403 from POST /api/auth/login when auth is disabled
- Return synthetic anonymous user from GET /api/auth/session when auth is disabled
- Hide logout button in settings when auth is disabled
- Redirect /login and /change-password to / via AuthGuard when auth is disabled

Closes #90
2026-04-23 14:45:04 +08:00
ashim-hq 3ef52d0aa9 feat: set PostHog and Sentry default keys for analytics 2026-04-23 00:29:22 +08:00
ashim-hq c2130148c4 fix: add statement-breakpoint separators to analytics migration 2026-04-22 19:15:38 +08:00
ashim-hq 9f66ad85bf feat: add Sentry error tracking with PII scrubbing 2026-04-22 19:10:28 +08:00
ashim-hq a3f707a361 feat: instrument tool_used, pipeline_executed, ai_bundle_action events 2026-04-22 19:07:17 +08:00
ashim-hq 075f017dbf feat: add backend analytics wrapper, config/consent API routes 2026-04-22 19:03:23 +08:00
ashim-hq 4904e8d140 feat: add analytics env vars, DB schema columns, instance ID generation 2026-04-22 19:00:15 +08:00
AshimandGitHub 5a45bcbc8f feat: production-grade RBAC with editor role, custom roles, API key scoping, and audit log (#89)
* feat(rbac): add editor role, 3 new permissions, ownership helper

* feat(rbac): add audit_log table, apiKeys.permissions column, editor role to schema

* feat(rbac): wire requirePermission into all routes, add editor role support

* refactor(rbac): replace ad-hoc role checks with permission-based ownership

* feat(rbac): add audit log DB writes + query endpoint

Dual-write audit events to stdout (existing) and SQLite audit_log table.
Add GET /api/v1/audit-log with pagination, action filter, and date range
filtering, gated behind audit:read permission.

* feat(rbac): add API key permission scoping with ceiling enforcement

* feat(rbac): add escalation prevention and last-admin protection

* feat(rbac): add editor role to UI, API key permission scoping in settings

* test(rbac): add full permission matrix integration test

* test(rbac): add editor role E2E tests

* feat(rbac): add custom roles with CRUD API and DB-backed permission lookup

* feat(rbac): add API key expiration

* feat(rbac): add roles management UI and API key expiration to settings

* feat(rbac): add audit log UI to settings

* fix: remove any cast in API key permission validation

* test(rbac): add unit tests for username validation rules

* test(rbac): add unit tests for effective permissions and ownership

* test(rbac): add comprehensive route permission matrix (all routes × all roles)

* test(rbac): add auth route edge case tests (login failures, session expiry, password side effects)

* test(rbac): add escalation prevention tests (register, update, self-demote, last-admin)

* test(rbac): add ownership enforcement tests (files, pipelines, editor access, cross-user isolation)

* test(rbac): add API key edge cases (name validation, delete behavior, key revocation)

* test(rbac): add audit log edge cases (all events, pagination clamping, structure)

* test(rbac): add custom roles edge case tests (validation, CRUD, functional permissions)

* test(rbac): add comprehensive E2E tests (roles UI, audit log, custom role, API key scoping)
2026-04-22 18:10:04 +08:00
AshimandGitHub 2d7a61c18f feat: allow multi-file selection for automation pipeline (#88)
* feat: allow multi-file selection for automation pipeline

Add two ways to import server-stored files into the pipeline:

1. Files page: "Pipeline" bulk action button and "Open in Pipeline"
   button in file details panel — navigates to /automate with selected
   file IDs via React Router state.

2. Automate page: "Import from Library" button opens a modal with
   thumbnails, search, and multi-select checkboxes to pick files from
   the user's server-stored library.

Both paths download the selected files and load them into the existing
useFileStore, reusing the batch pipeline processing infrastructure.

Closes #35

* fix: resolve 8 pre-existing test failures across unit and integration suites

- file-validation.ts: Return valid:false when Sharp fails to read
  metadata for standard formats (PNG, JPEG, BMP) instead of silently
  accepting corrupt buffers. CLI-decoded formats already skip Sharp.

- pipeline.ts: Enforce hard cap of 20 steps via .max() instead of
  relying on MAX_PIPELINE_STEPS env var (default 0 = unlimited).
  Tighten name limit to 100 chars and description to 500 chars to
  match test expectations.

- env.ts: Change MAX_LOGO_SIZE_KB default from 2048 to 500 to match
  the branding upload size limit the tests verify.
2026-04-22 00:02:00 +08:00
ashim-hq 9a015c8501 fix: AVIF sidecar crash, edit-metadata silent no-op, passport batch blank images, color-palette hex overflow, OCR log noise
- Convert all AI bridge inputs to PNG before writing to disk so PIL can
  read AVIF/WebP/TIFF (7 bridge files; face-detection and OCR already
  had this pattern)
- Add title/author aliases to edit-metadata schema so common field names
  actually write EXIF tags instead of being silently stripped by Zod
- Port extend/pad crop logic from passport-photo single endpoint to the
  batch pipeline so crop regions extending beyond the image get filled
  with background color instead of producing all-white output
- Clamp quantized color channels to 255 in color-palette to prevent
  Math.round(255/16)*16=256 from producing invalid hex like #100100100
- Compare OCR fallback warning against expected engine name per tier
  instead of comparing engine name against tier name (always mismatch)
2026-04-21 23:54:25 +08:00
AshimandGitHub 6fcf43016d Merge pull request #86 from ashim-hq/fix/issue-72-auth-false-admin
fix: prevent admin escalation when AUTH_ENABLED=false
2026-04-21 23:42:18 +08:00
ashim-hq bf73150301 fix: prevent admin escalation when AUTH_ENABLED=false
When auth was disabled, the backend middleware attached the first admin
user from the database to every request, and the frontend granted all 12
permissions. This gave every unauthenticated visitor full admin access
to user management, settings, teams, branding, and feature installation.

Now both layers use role "user" with user-level permissions so tools,
files, and pipelines still work without login while admin-only routes
correctly return 403.

Closes #72
2026-04-21 23:38:42 +08:00
AshimandGitHub ba26ea4bc7 feat: add AVIF output format support across 6 remaining tools (#85)
Closes #73

AVIF was already supported in the core engine, convert, compress,
optimize-for-web, upscale, erase-object, svg-to-raster, and
pdf-to-image tools. This adds AVIF as an output format option to
the 6 tools that were missing it: split, collage, stitch,
image-to-base64, noise-removal, and red-eye-removal.

For each tool, both the frontend format selector (with quality
slider for AVIF's lossy encoding) and the backend Zod schema +
Sharp .avif() encoding were updated. AVIF defaults: quality from
the user slider, effort 4 (balanced encode speed).

Also fixes pre-existing Biome formatting violations in 5 files
that were blocking a clean lint pass.
2026-04-21 23:34:48 +08:00
ashim-hq 7920fbfd20 chore: fix pre-existing biome formatting issues 2026-04-21 23:25:41 +08:00
ashim-hq 77a60b24cc fix: resolve 5 bugs found during comprehensive tool testing
1. split batch 404: register split tool in batch registry via
   registerToolProcessFn() so /api/v1/tools/split/batch works

2. CodeFormer crash: inference_app() expects a file path, not a numpy
   array. Save to temp file before calling, read result back.

3. OCR fallback chain: fix case-sensitive "Segmentation fault" match
   that prevented PaddleOCR crash from triggering Tesseract fallback.
   Also add "process crashed" check. Upgrade ARM paddlepaddle to >=3.2.1.

4. blur-faces large images: downscale to 1920px max before MediaPipe
   detection, scale coordinates back. Also add rotation retry for
   portrait-oriented images where BlazeFace misses faces. Applied to
   detect_faces.py, enhance_faces.py, and restore.py.

5. color-adjustments tool ID: fix mismatch in index.ts registration
   array (was "color-adjustments", should be "adjust-colors").
2026-04-21 22:25:06 +08:00
AshimandGitHub 8f6dbeca32 Merge pull request #83 from ashim-hq/feat/extended-format-support
feat: extended image format support (JXL, RAW, ICO, TGA, PSD, EXR, HDR)
2026-04-21 10:55:32 +08:00
ashim-hq 4ccde70dad fix: info tool fails for CLI-decoded formats (PSD, TGA, EXR, HDR, ICO)
The info tool reads metadata directly via Sharp without going through
the format decoder pipeline. Added CLI format detection and decoding
before metadata read, matching the pattern used by all other tools.
2026-04-21 10:34:10 +08:00
ashim-hq dc9160746e fix: ICO needs CLI decode, AVIF compress missing options, remove JXL output
- ICO: Sharp cannot decode ICO files. Added ImageMagick-based ICO decoder
  that extracts the largest embedded image. Added ICO to CLI_DECODED_FORMATS
  and SERVER_PREVIEW_EXTENSIONS. Removed from BROWSER_PREVIEWABLE sets.
- AVIF compress: Sharp's AVIF encoder requires effort option. Added
  formatOpts() helper that supplies effort:4 for AVIF format.
- JXL output: Docker's bundled libvips lacks the JXL encoder plugin.
  Removed JXL as a convert output target to avoid guaranteed failures.
  JXL remains fully supported as an input format.
2026-04-21 10:20:08 +08:00
AshimandGitHub 4c9dc6e38e fix: Docker hardening, security, and deployment readiness for V1 (#82)
Phase 1 — Docker Artifact Optimization:
- Replace broad `COPY . .` with targeted frontend source copies (API/Python
  changes no longer bust the frontend build cache)
- Replace build-essential with gcc/g++ (leaner runtime)
- Fix LOG_LEVEL=debug → info for production
- Harden .dockerignore (exclude worktrees, IDE, CI, test artifacts)

Phase 2 — State & Persistence:
- Add PUID/PGID support in entrypoint.sh for bind mount compatibility
- Guard against PUID=0/PGID=0 to prevent accidental root execution
- Evict conflicting system users (e.g. node:1000) before UID remap

Phase 3 — Security:
- Always register @fastify/rate-limit so login brute-force protection
  works even when global rate limit is disabled (RATE_LIMIT_PER_MIN=0)
- Add trustProxy support (TRUST_PROXY env var, default true) so rate
  limiting and audit logs use real client IPs behind reverse proxies
- Strip stack traces from 500 error responses in production
- Fix FSTDEP022 deprecation: maxParamLength → routerOptions
- Add multi-file guard on single-file tool endpoint with clear error
  message pointing to the /batch endpoint

Phase 4 — Graceful Degradation:
- Add consolidated hardware detection startup banner (GPU, rate limit,
  upload limit, proxy status)
- Add ConnectionMonitor component with health polling and reconnecting
  overlay that auto-dismisses when the server comes back

Phase 5 — Deployment Docs:
- Rewrite deployment.md with copy-paste CPU and GPU compose templates
- Add hardware requirements table (minimum, recommended, heavy workloads)
- Add PUID/PGID bind mount documentation
- Add complete env var reference table
- Add reverse proxy guides for Nginx, Nginx Proxy Manager, Traefik,
  and Cloudflare Tunnels
2026-04-21 10:19:08 +08:00
ashim-hq 2df0fe026e fix: use ImageMagick for RAW decoding instead of dcraw_emu
dcraw_emu is not included in libraw-dev (only libraries). ImageMagick
already delegates to LibRaw for all major RAW formats (DNG, CR2, NEF,
ARW, ORF, RW2). Moved ImageMagick helpers above RAW decoder to fix
declaration order.
2026-04-21 10:03:18 +08:00
ashim-hq 2aadb66031 feat: add support for JXL, Camera RAW, ICO, TGA, PSD, EXR, HDR image formats
Extends the platform to handle 7 new image format families alongside
the existing AVIF support gap-fill. Uses the established HEIC decoder
pattern (CLI decode → PNG → Sharp) for formats Sharp can't handle
natively: Camera RAW via dcraw_emu/LibRaw, PSD/TGA/EXR/HDR via
ImageMagick. JXL and ICO are Sharp-native. Adds server-side preview
for non-browser-displayable formats and JXL as a new convert output
target. All 27 validateImageBuffer callers updated with filename for
extension-based format detection.
2026-04-21 09:59:57 +08:00
AshimandGitHub 502d6b1c56 Merge pull request #80 from ashim-hq/feat/unlimited-by-default
feat: Unlimited by Default — remove all artificial limits
2026-04-21 00:05:41 +08:00
ashim-hq 1807a4ddfd fix: raise maxParamLength to 500 for long filenames
Fastify's find-my-way router defaults maxParamLength to 100 chars.
Files with long names (e.g., stock photo filenames >100 chars) would
return 404 on download because the :filename param exceeded the limit.
2026-04-20 23:43:29 +08:00
ashim-hq 795965ed43 fix: truncate long filenames to prevent filesystem ENAMETOOLONG errors 2026-04-20 22:14:14 +08:00
ashim-hq c738f16107 fix: support AVIF compress and fix hardcoded content type 2026-04-20 22:13:53 +08:00
ashim-hq 6746989aa1 feat: make all hardcoded limits configurable via env vars
- bodyLimit: conditional on MAX_UPLOAD_SIZE_MB (0 = 1GB practical max)
- rate limiting: disabled when RATE_LIMIT_PER_MIN=0
- shutdown timeout: 8s → 30s
- upload plugin: no fileSize/files cap when env=0
- session duration: configurable via SESSION_DURATION_HOURS (default 168h)
- login attempts: configurable via LOGIN_ATTEMPT_LIMIT
- batch/pipeline/svg-to-raster: skip guard when MAX_BATCH_SIZE=0
- pipeline steps: configurable via MAX_PIPELINE_STEPS (0 = unlimited)
- user-files: remove 200 hard cap
- stitch canvas: configurable via MAX_CANVAS_PIXELS (0 = unlimited)
- PDF pages: configurable via MAX_PDF_PAGES (0 = unlimited)
- SVG size: configurable via MAX_SVG_SIZE_MB (0 = unlimited)
- logo size: configurable via MAX_LOGO_SIZE_KB (default 2048)
- worker threads: auto-detect via resolveWorkerThreads (0 = auto)
- megapixels: skip validation when MAX_MEGAPIXELS=0
- seam carving: remove 1200px dimension cap
- concurrency: auto-detect via resolveConcurrency (0 = auto)
2026-04-20 21:50:17 +08:00
ashim-hq be254f9ca6 feat: dynamic timeouts — scale with image size, respect PROCESSING_TIMEOUT_S
Create timeout.ts utility for dynamic timeout computation.
Replace hardcoded timeouts across the stack:
- tool-factory worker: 30s → dynamic based on megapixels
- Python bridge default: 300s → 600s (or env override)
- background-removal: fixed → dynamic based on image size
- OCR: fixed 600s → dynamic based on image size
- seam-carving: 120s → dynamic based on image size
- ExifTool: 30s → 60s
- HEIC converter: 30s → 120s
- SQLite busy_timeout: 5s → 10s
2026-04-20 21:46:07 +08:00
ashim-hq ce477a0dbf feat: widen all Zod schema constraints for power users
Raise all artificially low max values on tool settings:
- stitch: grid→100, gap→1000, border/radius→500
- split: grid→100x100
- collage: zoom→10, gap→500, radius→500
- watermark: fontSize→1000
- border: width→2000, radius→2000, shadow→200
- qr: size→10000
- gif: dimensions→16384
- passport: dpi→1200
- sharpening: amount→1000
- pdf/svg: dpi→2400, dimensions→65536
- vectorize: precision→16
- bulk-rename: pattern→1000
- image-to-pdf: margin→500
- pipeline: name→255, description→2000
2026-04-20 21:40:06 +08:00
ashim-hq 7daf2377f0 feat: unlimited by default — env vars default to 0 (unlimited)
Add 10 new configurable env vars (MAX_WORKER_THREADS, PROCESSING_TIMEOUT_S,
MAX_PIPELINE_STEPS, MAX_CANVAS_PIXELS, MAX_SVG_SIZE_MB, MAX_LOGO_SIZE_KB,
MAX_SPLIT_GRID, MAX_PDF_PAGES, SESSION_DURATION_HOURS, LOGIN_ATTEMPT_LIMIT).

Change existing defaults: MAX_UPLOAD_SIZE_MB=0, MAX_BATCH_SIZE=0,
MAX_MEGAPIXELS=0, RATE_LIMIT_PER_MIN=0, MAX_USERS=0, CONCURRENT_JOBS=0.

Zero means unlimited/auto-detect throughout the application.
2026-04-20 21:35:15 +08:00
ashim-hq f67a03bb36 fix: resolve all audit findings — e2e coverage, feature system hardening, visual baselines
- Add 8 new E2E specs for AI tools (upscale, enhance-faces, colorize,
  restore-photo, erase-object, smart-crop, passport-photo, red-eye-removal)
  closing all HIGH/MEDIUM coverage gaps from the test matrix audit
- Fix ensureAiDirs() crash on non-Docker environments by gating on
  isDockerEnvironment() — prevents ENOENT when /data doesn't exist
- Bump torch 2.6.0→2.7.0 and torchvision 0.21.0→0.22.0 in feature
  manifest for broader Python version compatibility
- Add Python 3.14 version guard warning in install_feature.py
- Remove duplicate torchvision shims from upscale.py and enhance_faces.py
  (dispatcher.py already handles this at startup)
- Remove orphaned tools.batch i18n key and dead pipeline-builder filter
- Regenerate 4 visual regression baselines for current UI state
- Add data-testid to passport-photo generate button for E2E testability
2026-04-20 18:47:59 +08:00
ashim-hq e7eea34080 fix: resolve basicsr/torchvision shim bug, lint warnings, and code formatting
The torchvision compatibility shim for basicsr 1.4.2 was missing the
parent-package binding and only proxied a single attribute, causing
upscale and enhance-faces to fail at import time. The fix adds a
__getattr__ proxy for all attributes, binds the shim to the parent
package, and installs it in the dispatcher at startup for defense-in-depth.

Also removes unused anyInstalling variable, redundant `as any` cast,
and applies Biome formatting fixes across the codebase.
2026-04-20 17:03:17 +08:00
ashim-hq ac5fdfb841 fix: update e2e tests for current UI, increase timeouts for CPU environments, and fix auth bypass bug
- Fix SKIP_MUST_CHANGE_PASSWORD not affecting login/session API responses,
  causing frontend redirect even when the env var was set after user creation
- Increase Docker Playwright timeouts (test: 600s, expect: 60s, AI processing: 300s)
  to support CPU-only self-hosted environments
- Increase default rate limit from 100 to 50000 req/min for self-hosted deployments
- Fix OCR tests: use filechooser pattern (Dropzone has no static file input),
  correct enhance checkbox default, rewrite for actual fixture behavior
- Fix remove-bg tests: update quality labels (Balanced→HD, Best→Max)
- Fix noise-removal skip guard: use waitFor() instead of instant isVisible()
- Fix automate pipeline save test: clean up stale E2E pipelines before assertion
2026-04-20 15:03:56 +08:00
AshimandClaude Opus 4.6 6edb92c242 feat: add output filename suffixes, CPU fallback for GPU packages, and fix e2e tests
- Add tool-specific suffix to output filenames so downloads don't overwrite originals (batch & single-tool routes)
- Skip deleting shared models when uninstalling a bundle that shares models with another installed bundle
- Auto-detect NVIDIA GPU and swap GPU-only pip packages (onnxruntime-gpu, paddlepaddle-gpu) for CPU equivalents
- Refactor docker-compose with YAML anchors and explicit cpu/gpu profiles
- Add libheif-plugin-x265 to Dockerfile
- Fix install-all queue logic to handle concurrent individual installs and clear stale errors
- Unify playwright docker config to use same test dir with API_URL env var
- Fix flaky e2e selectors, rename Strip Metadata → Remove Metadata, handle collage custom dropzone, improve fallback test image generation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 10:56:47 +08:00
ashim-hq 8c83d7efcd fix: handle HEIC images in blur-faces and red-eye-removal, show warning when no faces detected 2026-04-19 19:52:23 +08:00
ashim-hq bb241d6044 feat: add per-cell fit/fill toggle to collage tool
Adds objectFit property to CellTransform (cover/contain). When set to
"contain", the entire image is shown within the cell with background
color fill. Toggle button in the cell controls toolbar switches between
modes. Server-side rendering handles both modes via Sharp.
2026-04-19 16:41:35 +08:00
ashim-hq 12c4d4de6f fix: update tool installation checks and refactor stdout JSON parsing in AI modules 2026-04-19 12:13:26 +08:00
ashim-hq 546cf52222 fix: install lock used server PID causing permanent stale lock
The lock file stored process.pid (Node server PID) which is always
alive. When releaseInstallLock wasn't called (e.g., error path),
the lock persisted forever, making the bundle stuck in "installing"
and blocking all future installs.

Fix: remove PID from lock entirely. Use simple file existence as
mutex. On server startup, unconditionally delete any lock file.
2026-04-18 11:39:20 +08:00
ashim-hq 46739a135e fix: use absolute path for install script (cwd is /app/apps/api in Docker) 2026-04-18 10:58:37 +08:00
ashim-hq 3eab3d5391 fix: tool page race condition on refresh and install error reporting
- Subscribe to features store reactively in ToolPage so refresh shows
  install prompt correctly instead of the tool UI
- Show loading state while features are being fetched for AI tools
- Capture stdout from install script for better error messages
- Keep last 20 stderr lines for error context instead of just exit code
- Clear install progress on success (was leaving stale state)
- Pass PIP_CACHE_DIR to install subprocess
2026-04-18 10:55:22 +08:00
ashim-hq 2a94cdc024 fix: add FEATURE_NOT_INSTALLED guard to all 10 remaining AI tool routes
The guard was only present on restore-photo.ts and the createToolRoute
factory. All other AI tools used custom route handlers that bypassed
the check entirely, allowing requests to reach the Python sidecar even
when the feature bundle was not installed. This adds an early 501
response before any multipart parsing or file processing.
2026-04-18 10:23:14 +08:00
ashim-hq 3c3aa74e98 feat: add FEATURE_NOT_INSTALLED guards to API tool routes
Return 501 with structured error when an AI tool's feature bundle
is not installed, preventing Python ImportError crashes. Guards
added to tool-factory, batch, pipeline (both validation loops),
and restore-photo custom route.
2026-04-18 02:40:16 +08:00
ashim-hq 7ffbd5e3c6 feat: add Python install script for on-demand AI feature bundles
Reads the feature manifest, installs pip packages (common + arch-specific),
downloads models in parallel with atomic rename, and writes installed.json.
Includes disk space pre-check, NCCL conflict handling, retry logic, and
progress reporting via stderr JSON lines. Also updates the feature route
to pass manifestPath and modelsDir as CLI arguments.
2026-04-18 02:37:43 +08:00
ashim-hq cadb0f8071 feat: add feature install/uninstall API routes with SSE progress 2026-04-18 02:34:21 +08:00
ashim-hq bf01f7169a feat: add backend feature status service for AI bundle tracking
Tracks which AI feature bundles are installed via /data/ai/installed.json
with atomic writes, in-memory caching, file-based install locks, and
startup recovery for interrupted installs.
2026-04-18 02:30:16 +08:00
ashim-hq ae411f2d72 chore(release): 1.15.9 [skip ci] 2026-04-17 23:26:26 +08:00