Commit Graph

10 Commits

Author SHA1 Message Date
Sravan 0c3487fdb0 polish(share): picker avatars + target name + pre-warm video poster
Follow-ups from testing the share flow (which now works end-to-end):
- Send-to picker showed only initials — now shows the real profile photo when the
  chat has one (matching the sidebar/forward avatars), coloured initials otherwise.
- During send it only said "Uploading 60%" with no idea WHO to — now the header and
  progress name the target ("Sending to Manasa Rapolu · 60%").
- After sending, a video bubble sat blank (just a timestamp) for a second or two
  while the poster generated on first view. media.js now warms the poster thumbnail
  at UPLOAD (temp-then-rename), and the on-demand /thumbs handler also writes via a
  temp, so the two can't serve a half-written JPEG. The bubble shows its poster
  right away.

Web + server only — live on deploy. Does NOT address the share extension failing to
auto-open the app (an iOS limitation, handled next in the native build).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 16:21:02 +05:30
Sravan 8a5409987c perf(video): stream a capped, faststart rendition instead of the raw upload
THE ANSWER to "why does an already-downloaded video still buffer?" — it was never
the download, and it was not the server. Probing the real uploads on the box:

  d0e49e58…  1920x1080  19.4 Mbps   75 MB / 31 s
  ad929d0b…  1920x1080  19.0 Mbps   27 MB / 11 s
  9f4e0865…   720x1584   3.6 Mbps   14 MB / 31 s

To play a 19 Mbps file the client has to SUSTAIN a 19 Mbps download for the whole
clip. No mobile link does, so the <video> buffer drains every few seconds: buffers,
plays, buffers, plays. Server-side disk read was instant and load was 1.7 on 20
cores throughout — the bottleneck is the media itself, not the delivery path.

Second, independent defect: phone MP4s store `moov` AFTER `mdat` (verified on two
uploads), so the player must fetch the file's tail before it can start at all.

Fix — keep the original bytes untouched (that is what the download button serves,
full quality) and build <id>.web.mp4 beside it: longest side capped at 1280,
~2.5 Mbps ceiling, +faststart. Measured on the 19 Mbps file:

  27.3 MB @ 19.0 Mbps  ->  2.55 MB @ 1.78 Mbps   (10.7x less bandwidth)
  transcode took 2.4 s for an 11.5 s clip

- server/media.js (new): probe, decide, 2-at-a-time background queue. Already
  light + correctly sized + faststart => no rendition at all. Light but wrong atom
  order => remux -c copy (seconds, no re-encode). Otherwise re-encode. A rendition
  that lands bigger than the original is discarded. MP4 box-walker for the
  faststart test is unit-checked against known fast/slow files, both directions.
- /stream/<id> serves the rendition, falling back to the original while it is still
  transcoding, so a video is never unplayable. /files/<id> is unchanged and still
  serves the pristine original for download.
- Renditions are queued at upload, and backfilled 15 s after boot for the videos
  that predate this. Range serving is now one shared helper for both routes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 10:49:04 +05:30
Sravan 2127397408 perf(video): cache attachment auth so Range playback stops stuttering; strict on-demand
Root cause of a downloaded/streaming video buffering repeatedly: every /files Range request
(a playing video fires dozens) re-ran the full attachment authorization, which scans the
messages table by attachment_id (un-indexed) — a per-chunk table scan = stutter. Now the
auth decision is cached per user+attachment for 60s (module-level, bounded), so range
requests after the first are ~free.

Also: preload='none' (nothing about a video downloads until the user taps play — only the
small poster loads), per 'no auto-download'. And the buffering spinner no longer hides on
canplay/loadeddata (they fire mid-buffer), so it reliably spins whenever it's buffering.
build batch159.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 22:27:37 +05:30
Sravan fd15628393 feat(video): server-generated poster thumbnails + HTTP Range streaming
- Dockerfile: add ffmpeg (Alpine).
- static.js: new /thumbs/<id> — ffmpeg extracts the first frame (0.5s), caches it next to
  the file, serves as the video poster (cosmetic; 404s gracefully if ffmpeg unavailable).
- static.js: /files now supports HTTP Range (206 Partial Content) + Accept-Ranges, which
  iOS requires to stream/seek video reliably (fixes the buffer-before-play / multi-tap);
  media (image/video/audio) now served inline, other files still download. Shared
  attachment auth refactored into one helper used by /files and /thumbs.
- home.html: video poster points at /thumbs/<id>. build batch157.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 22:09:32 +05:30
Sravan a9b3533f7a feat(chat): forward messages with multi-select (#1)
- Message action pill gains a Forward button; tapping it enters selection mode
  (tap bubbles to multi-select, footer bar shows count + Forward/Cancel, Esc exits).
- Forward picker lists EXISTING conversations only (DMs + groups from the sidebar),
  searchable, multi-target. POST /api/messages/forward copies body+attachment into
  each target (authorized as participant/member), live-pushed like a normal send.
- /files auth now accepts ANY message carrying an attachment (allByAttachment), so
  forwarded images stay viewable for the new recipients.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:09:02 +05:30
Sravan 899770ed02 feat: Windows download link + self-served update feed + install tracking
Downloads/updates:
- config: DOWNLOADS_DIR (override to a mounted volume in prod).
- static.js: serves /downloads/* (installer, latest.yml, .blockmap) with range support for
  resumable + differential auto-updates; /download/windows redirects to the current .exe
  (stable link). Landing page gets a "Download the Windows desktop app" button (hidden in-app).

Install tracking (who installed the app):
- db app_installs + repos.appInstalls (upsert by install_id, fills in the user on sign-in).
- POST /api/v1/telemetry/install (records install + user once authenticated);
  GET /api/v1/admin/installs (admin: list installs with user/version/os/last-seen).
- desktop main.js: stable per-install id in userData, exposed via preload
  (bizConnectNative.installId/version/os); home.html reportInstall() posts it after login.
- e2e: +2 checks (telemetry recorded, admin sees it). 119/119. build batch20.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 21:28:53 +05:30
Sravan b576ed372a feat(pwa): installable app (Add to Home Screen) for Android + iOS
- manifest.json (standalone display, theme color, maskable icons 192/512).
- generated square icons + apple-touch-icon (180) from the logo.
- apple-mobile-web-app + theme-color meta in home.html.
- sw.js gets a no-op fetch handler so it meets installability criteria (still
  no caching). static.js serves .json/.webmanifest with correct MIME.
- Installing as a PWA also unlocks Web Push on iOS (Apple requires Add to Home Screen).
Build marker -> pwa1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 17:13:01 +05:30
Sravan f4a23ae805 fix(cache): serve HTML with no-store so deploys reach browsers without a hard refresh
Browsers were serving a cached old home.html on normal reloads (only incognito/
hard-refresh got the new one). HTML now sends Cache-Control: no-store; versioned
assets keep ETag revalidation. Bumps build marker to push4.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 16:30:17 +05:30
Sravan 27355cec76 BizGaze Connect: chat, meetings, recordings, mobile, directory + UI fixes
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 16:15:29 +05:30
Sravan ba8bfc3f46 feat: BizGaze Connect home, BizGaze login, modular backend, /api/v1
User-facing
- New post-login home (/home): chat rail + Share/Connect (embedded) + Meeting; login lives here when logged out
- Landing: "Log in with BizGaze" + no-login screen share
- Console replaced by a role-scoped Dashboard (/dashboard): admins see all team sessions, others see only their own; stats + CSV/PDF export
- Recordings saved as MP4 (H.264/AAC) with WebM fallback; old .webm still downloadable
- Fix: duplicate "Sign in" on the login card

Auth / integration
- BizGaze as identity provider: /api/login validates against BIZGAZE_LOGIN_URL (env-gated) and provisions a local user
- Phase 2 start: /api/v1 alias for all /api routes; Authorization: Bearer accepted across HTTP + WS; login returns a token (for native desktop/mobile clients)

Backend refactor (Phase 1, behavior-preserving)
- Split server.js into config/lib/session/presence/routes/static/signaling + repos (data-access) + bizgaze (service)
- All SQL behind repos.js, tenant-scoped (tenantId == team_id for now)
- e2e updated to current flow (21/21 pass before and after)

Docs: ARCHITECTURE.md (target architecture + phased plan), CLAUDE.md repo layout, .env.example BIZGAZE_LOGIN_URL

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 00:40:07 +05:30