Commit Graph
100 Commits
Author SHA1 Message Date
SnapOtter 507fc1c356 Merge branch 'feat/tool-first-workflow'
Resolved conflict in worker.ts: kept remote refactored worker
(v2 process, scratch paths, extra outputs, metrics) and re-applied
the auto-save removal from the feature branch.
2026-06-15 14:55:58 +08:00
SnapOtter 7858130e7f Merge branch 'feat/tool-first-workflow'
Tool-first workflow redesign: replace sidebar with top nav, rewrite
home page as tool browser with search/modality tabs/collapsible sections,
refactor tool page into 5-state flow (dropzone/settings/processing/review),
modality-based URLs, audio waveform player, server-side preview generation
for non-native formats, explicit Save to Files, file library improvements.
2026-06-15 14:53:34 +08:00
SnapOtter 857aa75562 fix: apply processedFileName fallback everywhere processedFilename is used
Three places were using currentEntry?.processedFilename directly
without the processedFileName fallback (which parses the download URL):
- Video-to-image output detection (gif/webp check)
- Browser previewable check (canBrowserPreview)
- Non-native format detection (already fixed in prior commit)

All now consistently use processedFileName as fallback when
processedFilename is empty on the file entry.
2026-06-15 14:52:02 +08:00
SnapOtter 14ded6fbd0 fix: use processedFileName fallback for non-native format detection after processing 2026-06-15 14:49:30 +08:00
SnapOtterandGitHub 3fb8164fa5 feat: add OpenTelemetry distributed tracing (enterprise) (#232)
* feat(tracing): add OpenTelemetry dependencies and --import preload flag

* feat(enterprise): add distributed_tracing feature gate

* feat(tracing): add SDK bootstrap with enterprise gating

* fix(tracing): correct test coverage for enterprise-unavailable path and prevent double-init

Test 2 now mocks @snapotter/enterprise to throw an import error, exercising
the catch block in the preload. Test 3 imports with no endpoint so the preload
is a no-op, avoiding leaked SDK from double-initialization. Added idempotency
guard to initTracing() as a safety net.

* feat(tracing): add Pino trace mixin and shared logger

When OTel tracing is active, every Pino log line now includes traceId,
spanId, and traceFlags fields for log-to-trace correlation. The mixin
is a no-op when no SDK is registered (community users).

* feat(tracing): add _otel to ToolJobData and inject trace context at enqueue

Add optional _otel carrier field to ToolJobData for W3C trace context
propagation across BullMQ job boundaries. When an active OTel span exists,
propagation.inject() writes traceparent/tracestate into the job data before
queue.add(). When no SDK is registered (community edition), the carrier
stays empty and _otel remains undefined -- zero overhead.

* feat(tracing): extract trace context and create spans in BullMQ worker

* feat(tracing): inject trace context into Python sidecar calls

* feat(tracing): add trace context extraction to Python sidecar

* feat(tracing): add shutdownTracing to graceful shutdown sequence

* feat(tracing): enrich HTTP spans with tool_id and user_id attributes

* docs: add OpenTelemetry env var documentation to .env.example

* test(tracing): add lifecycle integration tests for trace propagation

* fix(tracing): inject trace context into pipeline and batch flow jobs

* fix(tracing): add sidecar.execute Node-side span and remove unnecessary comment

Wraps PythonDispatcher.run() with a sidecar.execute span on the Node
side so traces show the full round-trip (Node span -> Python span).
Also removes an obvious comment from logger.ts.
2026-06-15 12:53:06 +08:00
SnapOtter abaae18669 fix: always show NonNativePreview for non-native formats, even after processing 2026-06-15 12:41:42 +08:00
SnapOtter b93dc95bca fix: preview generation works for processed results too
NonNativePreview now accepts either a File (pre-processing) or a src
URL (post-processing). After processing a non-native video (e.g.,
muting an AVI), the preview button fetches the processed result from
its URL and sends it for transcoding, instead of showing the broken
MediaPlayerView.
2026-06-15 12:21:12 +08:00
SnapOtter 6090688708 fix: send auth token with preview generation request 2026-06-15 12:17:26 +08:00
SnapOtter d3cd4e41b5 feat: on-demand preview generation with progress messages for non-native formats 2026-06-15 12:04:30 +08:00
SnapOtter 511e941da0 fix: clean file info cards for non-native video/media formats on tool page
Instead of showing a broken video player error for formats like AVI,
MKV, WMV that browsers can't play, show a clean file info card with
icon, filename, format, and size. Message says "Preview will be
available after processing" which is accurate -- the processed output
is typically in a browser-friendly format.

Also cleaned up the post-processing non-previewable card to match
the same visual style.
2026-06-15 11:57:27 +08:00
SnapOtter ed458d4bbd feat: redesign review panel action hierarchy
- Download stays primary (full-width button)
- Save to Files demoted to subtle text link below download
- "Adjust settings" and "New file" are side-by-side compact buttons
- "Back to Tools" faded to very subtle (breadcrumb handles this)
- Clearer visual hierarchy: primary -> secondary -> tertiary actions
2026-06-15 11:52:22 +08:00
SnapOtter e7126f9293 feat: All tab groups tools by modality with collapsible sections
The All tab now shows tools organized by modality (Image, Video,
Audio, Documents, Data) with each section showing a colored icon,
tool count, and collapse toggle. Categories are nested within each
modality. Collapse state persists in localStorage so enterprise
users can hide modalities they don't use.

Modality tabs still show the flat category view for focused browsing.
2026-06-15 11:47:58 +08:00
SnapOtter 52693fc843 feat: arrow key navigation in Files list
ArrowUp/ArrowDown to move between files, Home/End to jump to
first/last. Selected file scrolls into view automatically.
List container is focusable and keyboard-navigable.
2026-06-15 11:42:59 +08:00
SnapOtter 3decfaae3e feat: clean preview button with progress bar, add document preview support 2026-06-15 11:41:11 +08:00
SnapOtter 412a21ee4d feat: server-side preview generation for non-native video/audio formats 2026-06-15 11:33:45 +08:00
SnapOtter a70b13ad32 feat: modality-aware file preview in Files library
- Video files: inline video player with controls
- Audio files: waveform visualization with playback (wavesurfer.js)
- Image files: thumbnail preview (existing behavior)
- Documents/data: file type icon with format label
2026-06-15 11:14:51 +08:00
SnapOtter 3a5d55cf26 fix: grey out incompatible files in library instead of hiding them
When importing from Files for a specific tool, all files are shown
but incompatible ones (wrong modality) are greyed out and unclickable.
This gives users full visibility of their library while making it
clear which files can be used.
2026-06-15 11:10:35 +08:00
SnapOtter adfa53246a fix: enforce file type filtering using tool's acceptedInputs
When registryEntry.accept is undefined, fall back to tool.acceptedInputs
to derive the file filter. This ensures image tools reject audio/video
files in the file picker, and vice versa.
2026-06-14 23:42:20 +08:00
SnapOtter 826f1efc08 feat: filter files by modality when importing from tool page
When clicking "Import from Files" on an image tool, only image files
are shown in the library. Video tools see only videos, audio tools
only audio. Uses MIME type prefix filtering (image/, video/, audio/).
Document and data tools see all files since their MIME types vary.
2026-06-14 23:26:38 +08:00
SnapOtter aff9159ae0 feat: move theme toggle and language selector to top nav bar
Theme toggle (sun/moon) and language selector (globe + dropdown) now
live in the top nav's right section alongside help and avatar.
Removed the floating Footer component from the home page since its
contents are now in the nav bar.
2026-06-14 23:13:38 +08:00
SnapOtter 6bcee1e073 fix: file library upload accepts all file types, not just images
The upload endpoint was rejecting non-image files via validateImageBuffer.
Now non-image files skip validation and use the MIME type from the
multipart upload. Width/height are null for non-image files.
2026-06-14 23:11:38 +08:00
SnapOtter f4061fe4b0 fix: Save to Files shows green success state with checkmark after saving 2026-06-14 23:10:20 +08:00
SnapOtter d0795b69ea feat: disable auto-save, add Save to Files button, accept all file types in library
- Disabled auto-save of processed files to library (worker no longer
  calls autoSaveToLibrary)
- Added "Save to Files" button in the review panel for explicit saving
- Files library upload area now accepts all file types, not just images
- Removed "Drop images here" hardcoded text, replaced with i18n
- Removed image-only file filter from library upload
2026-06-14 21:53:42 +08:00
SnapOtter b8a6b2018e feat: Import from Files works, remove pipeline button from files page
- Import from Files now navigates back to the tool page with the
  selected file loaded (uses selectForTool router state)
- Tool page skips file store reset when arriving from the library
- Removed "Open in Pipeline" button from file details and file list
- Button text changes to "Select File" when in file-selection mode
2026-06-14 21:43:58 +08:00
SnapOtter b5eef644e3 feat: clean tool page nav, add Import from Files on dropzone
- Removed Automate/Editor/Files links from top nav on tool pages,
  keeping only the breadcrumb + help + avatar for a cleaner look
- Added "Import from Files" link on the tool dropzone that navigates
  to the file library, letting users import previously processed files
2026-06-14 21:37:15 +08:00
SnapOtter f32b3bd51f fix: clear file store when navigating between tools
Changed undoProcessing() to reset() on toolId change so files don't
persist when navigating from one tool to another. Previously a video
file from a video tool would carry over to an image tool.
2026-06-14 21:32:27 +08:00
SnapOtter 90acd5bcd9 fix: show image viewer for video-to-gif/webp results instead of broken video player
When a video tool produces a non-video output (gif, webp, png), the
media player can't render it. Now detects the processed file extension
and switches to ImageViewer for image outputs while keeping the video
player for the input preview.
2026-06-14 21:30:54 +08:00
SnapOtter 69300b5c83 fix: use media-player for all video/audio tools that had no-comparison or side-by-side
video-to-webp, video-to-frames, auto-subtitles, extract-subtitles,
video-metadata, and audio-metadata were using display modes that
render via ImageViewer, which can't display video/audio files
(shows "Preview not available"). Changed to media-player so they
get proper video player or waveform visualization.
2026-06-14 21:27:26 +08:00
SnapOtter d8a8e05590 feat: add audio waveform visualization with wavesurfer.js
Replace the basic HTML5 audio element with an interactive waveform
player for audio tool pages. Video tools continue using the existing
MediaPlayerView component.
2026-06-14 21:25:34 +08:00
SnapOtter 5241ccd124 fix: show tool-specific accepted formats in dropzone, remove privacy note
The inner Dropzone was showing generic "Images, Videos, Audio, PDFs..."
text. Now it shows the tool's actual accepted formats (e.g., "MP4, MOV,
WEBM, MKV..." for video tools) via the acceptDescription prop.
Removed the privacy note text per user feedback.
2026-06-14 21:18:45 +08:00
SnapOtter 207c1c348b fix: make search bar full-width to align with tabs and grid 2026-06-14 21:09:42 +08:00
SnapOtter 43b190134e fix: top nav respects dark mode theme
Changed bg-white to bg-background so the top nav bar adapts to the
app's dark/light theme via CSS variables instead of being hardcoded
to white.
2026-06-14 21:08:34 +08:00
SnapOtter ade79c53a5 feat: polish home page UI -- card borders, colored icons, 3-col grid, Cmd+K hint
- Tool cards: subtle border, modality-colored icon backgrounds, better hover
- Category headers: bottom border separator for visual structure
- Grid: 3 columns on lg screens instead of 2
- Search bar: Cmd+K keyboard shortcut hint, tighter styling
- Tabs: softer styling, count separated from label
- Recent pills: subtler border style instead of bright orange
- Tighter vertical spacing throughout
- Wider max-width (max-w-6xl) for better use of screen space
2026-06-14 21:03:52 +08:00
SnapOtter fd26fa3586 fix: remove all legacy redirects from routes
Removed /fullscreen redirect, old color tool redirects, and legacy
/:toolId redirect. Clean route table with only active routes.
2026-06-14 20:56:46 +08:00
SnapOtter ea83e1c8b4 feat: modality-based URLs (/image/resize, /video/compress-video)
Tool routes now include the modality prefix: /image/crop, /video/trim-video,
/audio/convert-audio, /document/merge-pdf, /data/csv-json. Old URLs
(e.g. /resize) redirect to the new modality-prefixed path automatically.

Routes are computed by prepending MODALITY_URL_SLUG to each tool's route
in the TOOLS array at module init. The App.tsx route changed from
/:toolId to /:modality/:toolId, with a LegacyToolRedirect fallback.
2026-06-14 20:54:29 +08:00
SnapOtter 914ff1b1aa fix: prevent double scrollbar by locking html/body overflow
Added overflow: hidden to html and body, height: 100% to html/body/#root.
Only the <main> element inside AppLayout scrolls. This prevents the
browser's default body scroll from creating a second scrollbar alongside
the app's own scroll container.
2026-06-14 20:47:19 +08:00
SnapOtter ae959760e3 fix: remove double scrollbar on home page
Main element in AppLayout now handles scrolling (overflow-y-auto).
Removed redundant overflow-y-auto from home page wrapper.
2026-06-14 20:44:17 +08:00
SnapOtter b166f47de5 fix: show all tools on All tab, remove Popular and Browse sections
All tab now shows the full tool list grouped by category (same as
modality tabs). Removed Getting Started, Popular, and Browse by
Category sections for a simpler, direct experience.
2026-06-14 20:40:45 +08:00
SnapOtter 287f6f15de fix: remove redundant Getting Started section from home page
The Getting Started section duplicated tools already shown in
Popular. Now first-time users (no recents) go straight to Popular
and Browse by Category.
2026-06-14 20:37:11 +08:00
SnapOtter cf543f6976 fix: fetch settings on home page mount so tools are visible
The old home page called fetchSettings() on mount. The rewrite
dropped that call, so the settings store never loaded and
visibleTools was always empty (0 tools shown).
2026-06-14 20:34:59 +08:00
SnapOtter f68fcb8ca6 fix: cache useSyncExternalStore snapshot to prevent infinite re-render
getSnapshot() was calling JSON.parse() on every invocation, returning
a new array reference each time. useSyncExternalStore uses Object.is
comparison, so it saw a "new" value on every render and triggered
an infinite update loop. Fix by caching the parsed result and only
re-parsing when the raw localStorage value changes.
2026-06-14 20:31:49 +08:00
SnapOtter f387e98fff feat: tool-first workflow polish -- fix tests, lint cleanup
- Remove obsolete fullscreen redirect test (route deleted in Task 3)
- Update dropzone test strings to match new i18n values
- Fix categories count in i18n locale test (12 -> 24)
- Add chart-maker to no-dropzone exempt list in tool-registry test
- Auto-fix import ordering (Biome) in popular tools route and landing config
2026-06-14 19:44:58 +08:00
SnapOtter bc3ad2b008 feat: copy-primary for data tools, download-all label for multi-output, batch failure display 2026-06-14 19:38:47 +08:00
SnapOtter 896acda7ed feat: mobile layout for tool-first flow 2026-06-14 19:32:04 +08:00
SnapOtter 433d59c726 feat: tiered processing feedback and upload progress indicator 2026-06-14 19:27:59 +08:00
SnapOtter 56cd843482 feat: inline error messages with recovery actions 2026-06-14 19:24:12 +08:00
SnapOtter 619f61b470 fix: reassign 4 tools to correct display modes
bulk-rename: before-after -> no-comparison (renaming has no visual change)
image-to-pdf: before-after -> no-comparison (format conversion, not visual)
video-to-gif: side-by-side -> media-player (input is video)
chart-maker: no-comparison -> no-dropzone (generator tool)

8 tools targeting custom-results skipped: ocr, barcode-read, favicon,
auto-subtitles, video-to-frames, color-palette, info, histogram lack
ResultsPanel components and would crash at runtime.
2026-06-14 19:20:23 +08:00
SnapOtter 7c9ca85952 feat: add Cmd+Enter (process) and Cmd+S (download) keyboard shortcuts 2026-06-14 19:17:34 +08:00
SnapOtter 6ea515ccdd feat: add page-level drop overlay and paste handler on tool page 2026-06-14 19:09:36 +08:00
SnapOtter 45121c1a1e feat: add settings slide-in, review panel with size delta, start-over preserving settings 2026-06-14 19:03:13 +08:00
SnapOtter f2ac05747e feat: add full-width dropzone state and tool branding to tool page 2026-06-14 18:55:06 +08:00
SnapOtter 0d751b7d14 feat: rewrite home page as tool-first browser with search and modality tabs 2026-06-14 18:46:16 +08:00
SnapOtter 110700520c feat: add GET /api/v1/tools/popular endpoint 2026-06-14 18:36:32 +08:00
SnapOtter 6c61ae47f4 feat: add recent tools tracking via localStorage 2026-06-14 18:36:28 +08:00
SnapOtter f9ebb304db feat: remove /fullscreen route, add dynamic page titles 2026-06-14 18:34:32 +08:00
SnapOtter 71fef439a0 feat: replace sidebar with top nav bar across all pages
Remove the Sidebar and ToolPanel components, replacing them with the
TopNav bar integrated into AppLayout. Strip the home page to a minimal
placeholder (Task 6 will rebuild it as a tool browser). Add breadcrumb
navigation to tool pages showing modality and tool name.
2026-06-14 18:30:32 +08:00
SnapOtter f79958a622 feat: add TopNav and AvatarDropdown components 2026-06-14 18:25:02 +08:00
SnapOtter 5af1ac4dcf fix: update privacy policy to reference PostgreSQL instead of SQLite 2026-06-14 18:16:54 +08:00
SnapOtter 112df13957 test: add enterprise feature test coverage
Tests for SCIM, GDPR lifecycle, legal hold, audit export/archival,
encryption, SIEM forwarding, MFA endpoints, IP allowlist, config
export/import, webhook management, and license validation.
2026-06-14 16:50:35 +08:00
SnapOtter 8403222d08 feat(landing): migrate from Next.js to Astro 5
Zero client JS, 165 static pages, self-hosted fonts, Otter Orange
design system. Includes tool SEO data for all 157 tools, enterprise
page, and updated e2e landing tests.
2026-06-14 16:50:20 +08:00
SnapOtter f9ed5d125a Merge branch 'feat/enterprise-on-prem' 2026-06-14 16:10:15 +08:00
SnapOtter 2ceaeae09f Merge branch 'worktree-prebuilt-ai-bundles' 2026-06-14 15:11:59 +08:00
SnapOtter 171b3b3fdf Merge branch 'feat/web-otter-orange-design' 2026-06-14 15:11:56 +08:00
SnapOtter ccf5875f2f chore: add .astro build cache to gitignore 2026-06-14 14:59:43 +08:00
SnapOtter 73b277db48 chore: add .astro build cache to gitignore 2026-06-14 14:57:31 +08:00
SnapOtter e7bef34918 style: apply Biome formatting to enterprise files 2026-06-14 14:53:44 +08:00
SnapOtter 954cfb01a6 fix: test server registration gaps, Redis subscriber cleanup, atomic settings upsert 2026-06-14 14:30:35 +08:00
SnapOtter bcd59c20c7 feat(web): apply Otter Orange design system and UI improvements
Adopt the landing page's Otter Orange (#E07832) design system across
the web app, replacing the generic blue (#2563eb) theme. Light mode
uses warm cream/stone neutrals, dark mode uses deep brown tones.

UI changes:
- Remove Quick Actions section from home page
- Add modality filter tabs to file-uploaded view
- Remove colored left-border accents from tool panels and grid cards
- Use standard dropzone for pdf-to-image (custom-results mode)
- Fix PDF document viewer to use ArrayBuffer instead of blob URL
- Fix missing FileImage import in dropzone
2026-06-14 14:06:25 +08:00
SnapOtter 29dd675f42 fix(enterprise): SCIM token generation, GDPR job cancellation, MFA replay, config redaction, encryption validation, SCIM rate limit
- Add POST/DELETE /api/v1/enterprise/scim/token for SCIM bearer token management
- Cancel active BullMQ jobs via requestCancel() before GDPR purge deletes DB rows
- Reject duplicate MFA enrollment when a pending (unverified) secret exists
- Add webhook_destinations to config export REDACTED_KEYS (contains auth headers)
- Validate DATA_ENCRYPTION_KEY and DATA_ENCRYPTION_KEY_PREVIOUS are 64-char hex at startup
- Add 1000 req/min Redis counter rate limit to SCIM auth middleware
- Document SIEM/webhook system coexistence in siem-forward.ts
2026-06-14 14:03:47 +08:00
SnapOtter d86e4585e4 feat(enterprise): add unified webhook system with admin alerts 2026-06-14 12:21:46 +08:00
SnapOtter 0f883fe853 feat(enterprise): add configuration export/import with dry-run and dependency validation 2026-06-14 12:17:40 +08:00
SnapOtter 03e12e6f0b feat(enterprise): add upgrade management endpoints (version, migrations, readiness) 2026-06-14 12:14:52 +08:00
SnapOtter 2aea351f70 feat: add backup status tracking endpoints 2026-06-14 12:11:23 +08:00
SnapOtter 239f85f098 feat: add per-user rate limiting and concurrent job limits
Redis sliding window (sorted set) enforces per-user request rate limits
via the rateLimitPerUser DB setting. Concurrent job limits checked at
the HTTP layer before enqueue via maxConcurrentJobsPerUser setting.
Both default to 0 (unlimited).
2026-06-14 12:09:25 +08:00
SnapOtter fb14f41512 feat: expand Prometheus metrics with request duration, storage, and auth counters 2026-06-14 12:06:06 +08:00
SnapOtter 6237684ec8 feat: extend health endpoints with disk space, S3, storage, and backup checks 2026-06-14 12:01:29 +08:00
SnapOtter 7f62b1bf12 fix: resolve 6 bugs from enterprise audit (SIEM cursor, legal hold join, SCIM role, GDPR self-purge, export OOM, quota check) 2026-06-14 11:04:44 +08:00
SnapOtter b9dac59736 fix: correct SAML idpCert property name for @node-saml/node-saml v5 2026-06-13 23:02:20 +08:00
SnapOtter d397f57667 feat(web): add MFA login prompt and security settings UI
Handle MFA challenge in login flow (TOTP code input after password
verification, recovery code hint, back navigation). Add admin security
settings section with session idle timeout, max sessions per user,
MFA policy selector, SSO enforcement toggle with break-glass username,
and password policy controls. Propagate new i18n keys to all 21 locales.
2026-06-13 23:00:33 +08:00
SnapOtter db6f7bf38a feat(enterprise): add IP allowlisting with CIDR matching and Redis cache
Enterprise-gated onRequest hook that restricts API access to
configured CIDR ranges using Node 22's native BlockList.

- Plugin (ip-allowlist.ts): builds a BlockList from the ipAllowlist
  setting, caches in-process, syncs across instances via Redis pub/sub.
  Exempt paths for health probes, SCIM, SAML/OIDC callbacks.
  Handles IPv4-mapped IPv6 (::ffff:x.x.x.x) transparently.
- Admin API (enterprise/ip-allowlist.ts): GET/PUT endpoints gated by
  security:manage permission and ip_allowlist feature flag.  Validates
  CIDRs, prevents self-lockout, emits IP_ALLOWLIST_UPDATED audit event.
- 32 unit tests covering CIDR matching, validation, exempt paths, IPv6,
  and edge cases (/0, /32, mapped addresses).
2026-06-13 22:54:06 +08:00
SnapOtter 1787be35fe feat(enterprise): add TOTP MFA with enrollment, verification, and recovery codes 2026-06-13 22:49:00 +08:00
SnapOtter a1b5c6d2c3 feat(enterprise): add SCIM 2.0 provisioning (Users + Groups) 2026-06-13 22:42:55 +08:00
SnapOtter 0c4468a004 feat(enterprise): add SSO enforcement mode with break-glass admin 2026-06-13 22:32:16 +08:00
SnapOtter 54132d1833 feat(enterprise): add SAML 2.0 SSO with SP-initiated login
Implements SAML SSO using @node-saml/node-saml, gated behind
SAML_ENABLED env var and the saml_sso enterprise license feature.

- SAML env vars (entity ID, callback URL, IdP SSO URL, IdP cert,
  auto-create/auto-link users, default role, provider name,
  username/email attribute mapping) with validation in superRefine
- SAML plugin with three routes: metadata (GET), login (GET),
  and ACS callback (POST with form-urlencoded content type parser)
- Callback uses the shared external-auth resolver for user
  resolution (same pattern as OIDC: match/link/create/deny)
- Auth config endpoint exposes samlEnabled and samlProviderName
- Session loginMethod detection updated for SAML auth provider
- Frontend login page shows SAML SSO button when enabled
- i18n strings for SAML error messages across all 21 locales
2026-06-13 22:27:56 +08:00
SnapOtter 6920035f5a refactor: extract external auth resolver from OIDC for SAML reuse
Move user resolution logic (match by externalId, auto-link by email,
auto-create with user limit check) into a shared module that both
OIDC and SAML callbacks can use. Includes sanitizeUsername and
findUniqueUsername helpers. Preserves all existing OIDC behavior
and audit events.
2026-06-13 22:20:22 +08:00
SnapOtter 8b349b0341 feat: make password policy configurable via admin settings 2026-06-13 22:12:38 +08:00
SnapOtter 9fa23f4543 feat: add per-tool permission model with category and per-tool modes 2026-06-13 22:11:03 +08:00
SnapOtter 3cc4ef6895 feat: add session idle timeout and concurrent session limit
Idle timeout: reads `sessionIdleTimeoutMinutes` from settings, tracks
last activity in Redis (with Postgres fallback on cache miss), and
invalidates sessions that exceed the configured idle window.

Concurrent session limit: reads `maxSessionsPerUser` from settings
and evicts oldest sessions (FIFO) when a new login exceeds the cap.

Both features are opt-in (disabled when value is 0 or absent).
2026-06-13 22:07:31 +08:00
SnapOtter 7ed043ec53 feat(db): add identity columns (lastActivity, toolPermissions, TOTP) 2026-06-13 22:04:14 +08:00
SnapOtter 8eefa46981 chore: install @node-saml/node-saml and otpauth for Phase 3 identity 2026-06-13 22:04:03 +08:00
SnapOtter c7e9b1ddc6 feat(web): add storage dashboard and team retention settings
Add per-team storage breakdown table to the usage section (from
GET /api/v1/admin/usage teamStorage data). Add storage quota (MB)
and retention (hours) inline fields to the teams section, saving
via PUT /api/v1/teams/:id. Add i18n keys to all 21 locale files.
2026-06-13 21:20:12 +08:00
SnapOtter dd2a50799a feat(enterprise): add GDPR user/team data purge with audit redaction 2026-06-13 21:13:22 +08:00
SnapOtter b6a8226837 feat(enterprise): add GDPR user data export (async) 2026-06-13 21:07:28 +08:00
SnapOtter fa7da7ce0c feat(enterprise): add legal hold with cleanup bypass 2026-06-13 21:01:04 +08:00
SnapOtter b60f550b3f feat(enterprise): add per-team retention overrides with deleteAfter
Compute a deleteAfter timestamp on job creation when the enterprise
team_retention_overrides feature is enabled. The cleanup sweep now
deletes storage for jobs past their deleteAfter deadline, running
independently of the global TTL setting.
2026-06-13 20:55:17 +08:00
SnapOtter d064286559 feat: add team-level storage quotas with enforcement on upload and save 2026-06-13 20:49:28 +08:00
SnapOtter b1e94bd988 fix: build script venv handling and lint fixes
- Use /opt/venv directly when --entrypoint bash bypasses entrypoint.sh
- Use sys.executable for all pip calls (not bare pip)
- Override entrypoint in CI workflow to avoid startup banner
- Fix Biome formatting (template literals, try/catch blocks)
2026-06-13 20:48:27 +08:00
SnapOtter aaa8a37c9b feat: add storage usage tracking with DB counters and reconciliation
Increment users.storageUsed on file upload/save, decrement on delete
(per-user via GREATEST to prevent negatives). Add per-team storage
breakdown to GET /api/v1/admin/usage. Weekly reconciliation job
(3 AM Sunday) recomputes counters from actual userFiles sums.
2026-06-13 20:43:52 +08:00
SnapOtter 7e5843cd40 feat(db): add data lifecycle columns (deleteAfter, legalHold, quotas, retention) 2026-06-13 20:39:38 +08:00
SnapOtter 3b7f44e50e fix: sort enterprise exports for Biome lint compliance 2026-06-13 17:07:49 +08:00