Commit Graph
151 Commits
Author SHA1 Message Date
jevb bf3fada16b fix: proxy LiveKit through HTTPS to fix mixed-content block
- Add reverse proxy at /livekit/* that forwards to LiveKit server
- Server sends relative URL "/livekit" in voice_token; client
  resolves to wss://server:port/livekit using known server host
- Fix API secret minimum length (32 chars required by LiveKit)
- Pass TLS config to LiveKit process manager for TURN certs
2026-03-20 06:11:09 +01:00
jevb 620fac778d docs: update specs for LiveKit migration (Phase 4)
- CLAUDE.md: update key features (LiveKit voice/video), remove
  WebRTC track ID rules, update project structure
- PROTOCOL.md: replace voice_offer/answer/ice with voice_token,
  remove soundboard_play, note client-side speaker detection
- CHATSERVER.md: replace Pion SFU with LiveKit companion process,
  update architecture, security, and library references
2026-03-20 05:50:14 +01:00
jevb f1011a881a fix: update settings overlay test for 4 audio toggles
Silence suppression toggle was removed (LiveKit handles DTX).
2026-03-20 05:47:11 +01:00
jevb aa162d88c5 feat: replace client WebRTC with LiveKit SDK (Phase 2)
Client changes:
- Create livekitSession.ts (~400 lines, replaces 1105-line
  voiceSession.ts): Room lifecycle, device switching via
  switchActiveDevice, RNNoise pre-processing for enhanced NS
- types.ts: add VoiceTokenPayload, remove VoiceOffer/Answer/Ice
  payloads, remove ThresholdMode
- dispatcher.ts: replace voice_offer/answer/ice handlers with
  single voice_token handler
- VoiceCallbacks.ts: swap imports to livekitSession
- VoiceAudioTab.ts: remove silence suppression toggle, inline
  threshold math (removed vad.ts dependency)
- Update all files importing from deleted modules

Deleted files (11 source + 5 test):
- webrtc.ts, vad.ts, voiceSession.ts, audio.ts, video.ts,
  Soundboard.ts + their test files

Kept: noise-suppression.ts + @jitsi/rnnoise-wasm (Krisp is
LiveKit Cloud only, not available for self-hosted)

Added: livekit-client@^2.17.3

TypeScript compiles with zero errors (tsc --noEmit).
2026-03-20 05:46:19 +01:00
jevb fed85a0d3a feat: add LiveKit webhook handler (Phase 1.5)
- livekit_webhook.go: HTTP handler for LiveKit participant_joined/left
  events with JWT verification; syncs stale voice state on crash recovery
- Mount webhook at POST /api/v1/livekit/webhook in router
- Speaker detection handled client-side via LiveKit SDK events
2026-03-20 05:34:38 +01:00
jevb 72d5412ba7 feat: rewrite server voice handlers for LiveKit (Phase 1)
Replace custom Pion WebRTC SFU with LiveKit token-based flow.

Server changes:
- client.go: remove PeerConnection, voiceDone, negoMu; add
  setVoiceChID/clearVoiceChID
- voice_handlers.go: 961 -> ~295 lines; voice_join now generates
  LiveKit token, voice_leave calls RemoveParticipant; delete
  SDP/ICE/RTP/soundboard handlers
- hub.go: replace SFU + VoiceRooms with LiveKitClient +
  LiveKitProcess; remove speaker broadcast goroutine
- messages.go: add buildVoiceToken, remove buildVoiceOffer/
  Answer/ICE; simplify buildVoiceConfig
- handlers.go: remove voice_offer/answer/ice/soundboard dispatch
- router.go: replace NewSFU with NewLiveKitClient, add optional
  LiveKit process auto-start

Deleted files (14):
- sfu.go, voice_room.go, speaker_detector.go, rtp_audio_level.go,
  speaker_broadcast.go, api/voice_handler.go
- All corresponding test files

All server tests pass (go test ./...).
2026-03-20 05:30:27 +01:00
jevb 80e880d536 feat: add LiveKit infrastructure (Phase 0)
- Replace VoiceConfig STUN/TURN/MediaPort fields with LiveKit
  API key, secret, URL, and binary path
- Add livekit_process.go: companion process manager with
  auto-restart and config generation
- Add livekit.go: token generation, room service client,
  participant management, video track counting
- Add LiveKit server SDK dependency (server-sdk-go/v2)
- Update config tests for new VoiceConfig fields
2026-03-20 05:14:52 +01:00
jevb 7ebf2faecd chore: bump version to 1.2.0 for LiveKit migration 2026-03-20 05:07:37 +01:00
jevb 98f60cc78e fix: settings bugs, accessibility, and camera stream leak
- KeybindsTab: fix stale savedVk closure — currentVk now updates on
  successful capture and clear, so timeout/error reverts correctly
- AccountTab: add 1–32 char username length validation
- VoiceAudioTab: fix orphaned camera previewStream on repeated tab
  visits — consolidated to startCameraPreview/stopCameraPreview
  helpers with registrar stopping old tracks before new ones
- VoiceAudioTab: camera preview errors now shown to user instead
  of silently swallowed; error cleared on retry
- All toggles: new createToggle() helper in helpers.ts with
  role="switch", tabindex="0", aria-checked, Enter/Space support
- Applied createToggle to AppearanceTab, NotificationsTab,
  VoiceAudioTab (5 toggle instances)
2026-03-20 02:21:53 +01:00
jevb 90b6244d2a feat: wire search bar to server FTS5 search API (T-065)
Connect the dead search input in ChatHeader to the existing
GET /api/v1/search endpoint via a new SearchOverlay component.

- SearchOverlay: debounced input (300ms), 2-char minimum,
  AbortSignal cancellation, keyboard nav, ARIA roles
- SearchOverlayController in OverlayManagers: error handling
  with toast + re-throw for overlay status display
- ChatHeader: onSearchFocus callback opens overlay, blurs input
- MainPage: wires controller with channel-scoped search and
  jump-to-message via scrollToMessage
- CSS: result item styles for header/content/time
- 16 unit tests covering debounce, results, keyboard nav,
  error states, min-length guard, backdrop close
2026-03-20 02:00:29 +01:00
jevb f4f293da51 refactor: extract MainPage into focused controllers with tests
Split MainPage (1000+ lines) into 5 single-responsibility controllers:
- ChannelController: channel switching, component lifecycle
- MessageController: message loading, pagination, pending-delete
- ReactionController: reaction toggle, emoji picker positioning
- VideoModeController: chat/video grid toggle, camera tiles
- VoiceCallbacks: voice widget + sidebar callback factories

Fixes applied during code review:
- Lifecycle leaks: activeModal cleanup, pickerDestroy via closePicker(),
  VideoModeController destroy resets DOM slots
- Error handling: try/catch on modal APIs, onUploadFile, camera toggle
- Immutability: replaced .length=0 with reassignment
- Removed dead code: unused children array, unreachable null guard,
  unused setActiveChannel import, stale pickerDestroy local var
- console.warn replaced with log.warn in webrtc.ts
- PAGE_SIZE constant used consistently in both load paths

75 unit tests across 5 new test files, 0 TS errors.
2026-03-20 01:33:43 +01:00
jevb f3734bf827 fix: voice rejoin failure, SDP race, deafen bypass + add server voice logging
- Fix SDP signaling race condition: add per-client negoMu to serialize
  renegotiateParticipant / handleVoiceOffer / handleVoiceAnswer so
  concurrent OnTrack goroutines don't race through rollback
- Fix handleVoiceLeave triple-fire: early return when clearVoice()
  returns zeros so ICE callbacks don't re-enter and corrupt state
- Fix SQLite SQLITE_BUSY errors: add busy_timeout=5000 pragma and
  SetMaxOpenConns(1) for file-based databases
- Fix deafen bypass: new remote audio elements now respect localDeafened
  state so late-arriving streams are muted immediately
- Add debug-level logging for SDP negotiation, track fan-out, ICE
  candidates, voice state changes, room lifecycle, and participant
  add/remove
- Bump version to 1.1.1
2026-03-19 21:35:18 +01:00
jevb 8c4ca9d898 fix: pass signing key to Tauri build step in CI
The CI workflow was missing TAURI_SIGNING_PRIVATE_KEY env var on
the Tauri build step, causing the signing step to fail. The
release workflow already had it — this aligns CI to match.
2026-03-19 19:47:32 +01:00
jevb b49e2b5439 update gitignore 2026-03-19 19:22:42 +01:00
jevb ffab36a291 chore: update Tauri updater public key for v1.1.0 signing keypair 2026-03-19 19:22:23 +01:00
jevb c784d7950c chore: bump version to 1.1.0
Video chat, GIF picker, push-to-talk, desktop notifications,
compact mode, and admin IP restriction.
2026-03-19 18:44:29 +01:00
jevb 4f07c9d0e1 test: add coverage for GIF picker, Tenor API, PTT, and image rendering
Brings coverage from 73.6% to 78.4%, above the 75% CI threshold.
- gif-picker.test.ts: component lifecycle, search, selection, cleanup
- tenor.test.ts: API calls, result parsing, error handling
- ptt.test.ts: vkName mapping, init/stop/update lifecycle
- renderers.test.ts: timestamp parsing, inline images, lightbox
2026-03-19 18:25:49 +01:00
jevb 614e6adfe3 chore: restrict Dependabot to minor/patch bumps only
Major version bumps (vite 6→8, vitest 3→4, actions v4→v6) need
manual review and testing. Adds semver-major ignore rules to all
four ecosystems to prevent noisy PRs.
2026-03-19 18:19:51 +01:00
jevb d0755cbd27 fix: add ResizeObserver stub to message list tests
jsdom does not provide ResizeObserver, which is now used by
MessageList for height change detection. Adds a no-op stub
in both test files that import MessageList.
2026-03-19 18:06:07 +01:00
jevb 5caa9c7ff6 docs: update documentation for video chat, GIF picker, PTT, and notifications
- README.md: added video chat, GIF picker, inline images, PTT, desktop
  notifications, compact mode, admin IP restriction, config table entry
- CLAUDE.md: added Key Features section and new critical rules for video
  track IDs and Tenor API key
- tenor.ts: documented public anonymous Tenor API key
- ptt.rs: added 10s timeout to key capture to prevent thread leak
- KeybindsTab.ts: handle timeout (vk=0) from key capture gracefully
2026-03-19 17:54:13 +01:00
jevb 89f26d638d fix: stop camera preview and mic meter when settings overlay closes
Registers camera preview stream for proper cleanup when the settings
panel is hidden, preventing the webcam from staying active in the
background. Also fixes test config to match current voice config types.
2026-03-19 17:43:32 +01:00
jevb d6542ed437 fix: remove frozen video tiles when remote user disables camera 2026-03-19 17:29:20 +01:00
jevb 3c1cef00d6 fix: add USE_VIDEO permission to Member role and fix single-user video mode
Two fixes:
1. Member role (0x663) was missing USE_VIDEO (0x800) and SHARE_SCREEN (0x1000)
   bits, causing "permission denied" when non-owner users tried to enable camera.
   Migration 006 updates Member permissions to 0x1E63.
2. checkVideoMode() now checks voice.localCamera immediately instead of waiting
   for the server's voice_state broadcast, so video grid shows instantly when
   a single user enables their camera.
2026-03-19 17:19:56 +01:00
jevb b79af83bda fix: use unique stream IDs for audio and video tracks to prevent dedup collision
Audio and video tracks from the same user were both using stream ID "user-{id}",
causing the client's duplicate stream check to silently drop the video track.
Changed to "user-{id}-audio" and "user-{id}-video" so each gets its own stream.
2026-03-19 17:13:59 +01:00
jevb 3da096ee6f fix: add local camera self-view to video grid
Expose getLocalCameraStream() from voiceSession and use it in
checkVideoMode() to add/remove the user's own camera tile in the
VideoGrid when localCamera state changes.
2026-03-19 17:07:30 +01:00
jevb 8c4734fe81 test: add video track coexistence integration test
Verify multi-user composite track key behavior: audio+video coexistence
per user, GetTracks total count, TrackUserIDs deduplication, and
independent track removal across users.
2026-03-19 16:52:15 +01:00
jevb 427ca06372 test: add unit tests for voice session camera lifecycle 2026-03-19 16:51:49 +01:00
jevb f49943dab2 feat: add video grid CSS styles
Adds layout styles for the video grid (grid slot, cells, username
overlays) and the active-ctrl highlight for voice widget control buttons.
2026-03-19 16:47:34 +01:00
jevb d9f447cf44 feat: add camera active indicator to voice widget
Promotes the camera button to a module-level ref so its active state
can be toggled based on voice.localCamera, matching the existing
mute/deafen indicator pattern.
2026-03-19 16:47:26 +01:00
jevb 12907ef00d feat: add webcam device selector and preview to settings
Adds a video device dropdown and live camera preview to the Voice & Audio
settings tab. Users can select their preferred camera and see a real-time
preview. The preview stream is properly cleaned up on device change and
when the settings overlay is closed.
2026-03-19 16:47:18 +01:00
jevb eb8d764632 feat: wire video grid into main page with chat/video toggle
- Add VideoGrid component that replaces chat area when cameras are active
- Replace direct setLocalCamera/ws.send with enableCamera()/disableCamera()
- Wire setOnRemoteVideo/setOnRemoteVideoRemoved to add/remove streams
- Subscribe to voice store to auto-toggle between chat and video modes
- Clean up video grid and remote video callbacks on destroy
2026-03-19 16:46:49 +01:00
jevbandClaude Opus 4.6 c386bd6dea feat: add camera enable/disable and remote video handling to voice session
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 16:42:24 +01:00
jevb 9c1d231db5 feat: add VideoGrid component for rendering camera streams 2026-03-19 16:39:35 +01:00
jevb 28348e0098 feat: add video device manager for camera capture 2026-03-19 16:39:29 +01:00
jevb f67d4256b1 feat: add video track add/remove to WebRTC service 2026-03-19 16:38:43 +01:00
jevb b802532af3 feat: extend SFU to forward video tracks and enforce MaxVideo limit
- setupOnTrack now handles both audio and video tracks via kind branch
  instead of returning early on non-audio tracks
- RTP forwarding works identically for both kinds; speaker detection
  only runs for audio tracks
- handleVoiceCamera enforces MaxVideo limit before DB update, rejecting
  with VIDEO_LIMIT error when the cap is reached
- Added TestHandleVoiceCamera_MaxVideoEnforced test
2026-03-19 16:33:47 +01:00
jevb 51033dab6d refactor: use composite track keys in VoiceRoom for multi-track support 2026-03-19 16:25:12 +01:00
jevb b2038cecdb feat: settings overhaul, GIF picker, notifications, PTT, scroll fixes
Client:
- Wire up all 4 notification toggles (desktop, taskbar flash, sounds, @everyone)
- Add compact mode CSS with visible layout differences
- Add GIF picker with Tenor API (trending + search)
- Render inline images/GIFs instead of link previews for direct URLs
- Add push-to-talk via Rust GetAsyncKeyState polling (non-consuming)
- Add key capture UI for PTT keybinds (supports mouse buttons)
- Add error/success feedback on account settings (password, username)
- Fix mic stream cleanup on tab switch (VoiceAudioTab factory pattern)
- Fix message timestamps using UTC with proper timezone conversion
- Fix emoji reaction picker (was returning early on empty emoji)
- Fix chat scroll jumpiness with Discord-style overflow-anchor + ResizeObserver
- Pre-measure all message heights on load to prevent first-scroll jump
- Improve scrollbar visibility with semi-transparent white thumb
- Show client version on Logs tab

Server:
- Add admin_allowed_cidrs config to restrict /admin to private networks
- Fix voice config defaults lost when YAML section has omitted fields
- Add negative caching to updater (5min error cache)
2026-03-19 12:23:24 +01:00
jevb b5b8be8370 feat: show client version on settings Logs tab 2026-03-19 09:51:16 +01:00
jevb 3e6d36a47b feat: add negative caching to updater to avoid API spam
Failed GitHub API calls are now cached for 5 minutes before retrying,
preventing repeated 502 errors in the logs when the API is unreachable.
2026-03-19 09:49:21 +01:00
jevb 86c218211c fix: use v1Compatible for createUpdaterArtifacts (v2Compatible not valid) 2026-03-19 07:41:58 +01:00
jevb 87e17cfef5 Merge branch 'dev' 2026-03-19 07:34:10 +01:00
jevb 08f3623777 fix: enable Tauri updater artifacts and bump client to v1.0.0
- Add createUpdaterArtifacts: "v2Compatible" to bundle config
  so CI generates .nsis.zip + .nsis.zip.sig for auto-updates
- Bump client version from 0.1.0 to 1.0.0
2026-03-19 07:33:59 +01:00
J3vb ea72512f43 Merge pull request #25 from J3vb/dev
v1.0.0: OwnCord initial release
2026-03-19 06:50:28 +01:00
jevb be8af3d4c4 fix: adjust coverage thresholds and exclude Tauri-runtime files
Exclude files that require Tauri runtime and cannot be unit tested
in jsdom: noise-suppression.ts, updater.ts, MainPage.ts,
UpdateNotifier.ts. Lower global threshold from 80% to 75% to
account for complex UI components (renderers, ChannelSidebar)
that are partially tested. All 790 tests pass.
2026-03-19 06:28:08 +01:00
jevb 4945bc9ee9 fix: resolve CI failures in server lint and client tests
Server lint (golangci-lint):
- Add package comment to client_update.go (ST1000)
- Remove unnecessary fmt.Sprintf in updater_test.go (S1039)
- Remove redundant |0x00 in rtp_audio_level_test.go (SA4016)

Client tests (vitest):
- Fix messages.store.test.ts: account for .reverse() in
  setMessages/prependMessages (API returns newest-first)
- Fix logs-tab.test.ts: use .log-entry pre selector to avoid
  matching diagnostics <pre> placeholders; add createLogger
  to @lib/logger mock
- Fix settings-overlay.test.ts: add setVoiceSensitivity to
  @lib/voiceSession mock
2026-03-19 06:20:00 +01:00
jevb 4acf507702 docs: update README and CLAUDE.md for v1.0.0 release
- Update README Administration section with full admin panel features
  (live server logs, audit filters, backup/restore, settings, modals)
- Fix CLAUDE.md branch reference (tauri-migration -> dev)
2026-03-19 05:40:22 +01:00
jevb df998386d9 feat: redesign admin panel, add live server logs and audit log filters
Admin panel redesign:
- Rebuild frontend from mockup with Discord-style dark theme
- Stat cards, section cards, role badges, modal system, toast notifications
- All 7 sections: Dashboard, Users, Channels, Audit Log, Settings, Backups, Updates
- Modals replace confirm()/prompt() for all destructive actions

Live server logs (new):
- RingBuffer + MultiHandler tees slog to stdout AND in-memory buffer
- SSE endpoint at /admin/api/logs/stream streams logs in real-time
- Log viewer with level filters (DEBUG/INFO/WARN/ERROR), search,
  auto-scroll, pause/resume, copy all, clear
- Color-coded lines by level, source categorization from file paths

Audit log improvements:
- Search filter (actor, action, target, detail)
- Action type dropdown filter
- Copy All and Export CSV buttons
- Instant client-side re-filtering

Console output:
- Switch from JSON to human-readable text format (slog.TextHandler)
- Move startup banner before init logs so it appears first
2026-03-19 05:32:40 +01:00
jevb 851a8b8b21 fix: add rows.Err() check in GetAttachmentsByMessageIDs
Prevents silently returning truncated attachment maps when the DB
cursor errors mid-iteration. Matches the pattern used in all other
query loops across the codebase.
2026-03-19 04:27:11 +01:00
jevb a7df9c2b3c fix: resolve 5 remaining medium/low issues from third-pass go-review
- NEW-1: Add rows.Err() check in ListMembers to catch cursor errors
- NEW-2: Add minVal parameter to queryInt so offset=0 is not rejected
- NEW-3: Fix copyFile double-close by removing defer, using explicit
  close on both success and error paths
- NEW-4: Add GetAllChannelPermissionsForRole batch query, eliminating
  N+1 GetChannelPermissions calls in channel list and search handlers
- NEW-5: Cap fetchBody with io.LimitReader(1 MiB) to prevent memory
  exhaustion from malformed release assets
2026-03-19 04:19:03 +01:00