Commit graph

1728 commits

Author SHA1 Message Date
senke
32029d5718 refactor(web): consolidate duplicate services into feature modules
- Migrate 5 files from legacy services/trackService to feature-based
  tracks/services/trackService
- Migrate 1 file from legacy services/playlistService to feature-based
  playlists/services/playlistService
- Add missing functions to feature trackService: search, like, unlike,
  recordPlay, download, upload, getStatus
- Add backward-compatible `trackService` and `playlistService` object
  exports that match legacy API signatures (no call-site changes needed)
- Delete legacy apps/web/src/services/trackService.ts
- Delete legacy apps/web/src/services/playlistService.ts

Addresses audit finding: debt item 6 (duplicate services).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:52:09 +01:00
senke
c62d63fc05 fix(stream): migrate sqlx query! macros to runtime queries
- Convert all sqlx::query!() and sqlx::query_scalar!() compile-time
  macros to runtime sqlx::query() and sqlx::query_scalar() with .bind()
- Affected files: segment_tracker.rs, processor.rs, callbacks.rs
- This removes the dependency on .sqlx/ directory for offline mode
- Update Dockerfile to remove SQLX_OFFLINE=true and .sqlx COPY
- Stream server can now compile without a live database connection

The compile-time macros required either a DATABASE_URL at build time or
a .sqlx directory with cached query metadata (neither was available).
Runtime queries trade compile-time SQL validation for buildability.

Addresses audit finding: debt item 1 (stream server compilation).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:49:30 +01:00
senke
d934eaa763 fix(deps): upgrade jsonwebtoken 9.2 -> 10.x in chat-server
- Upgrade jsonwebtoken from 9.2 to 10.x with aws_lc_rs crypto backend
- The API (encode, decode, Validation, EncodingKey, DecodingKey, Header,
  Algorithm) remains compatible -- the main v10 change is the crypto
  backend selection via features
- Aligns with veza-stream-server which already uses jsonwebtoken 10

Addresses audit finding: A06 (Vulnerable & Outdated Components).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:46:47 +01:00
senke
72b5edb5b5 feat(chat): implement Redis rate limiting for WebSocket messages
- Create security/rate_limiter.rs with Redis-backed sliding window counter
- Falls back to in-memory HashMap when Redis is unavailable
- Per-action rate limits: messages (30/min), reactions (60/min),
  edits (20/min), deletes (10/min), typing (120/min), joins (10/min),
  searches (15/min)
- Integrate rate limiting into handle_incoming_message in WebSocket handler
- Add RateLimiter to WebSocketState, initialized from REDIS_URL env var
- Rate-limited clients receive an Error message, connection stays open
- Includes unit tests for in-memory fallback path
- Remove TODO stub from EnhancedSecurity::validate_request

Addresses audit findings: A04 (Insecure Design), debt item 3.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:45:39 +01:00
senke
8fa866baaa fix(security): harden docker-compose.prod.yml and staging -- sslmode, secrets
Production (docker-compose.prod.yml):
- Change sslmode=disable to sslmode=require on all 3 DATABASE_URLs
- Replace JWT_SECRET fallback defaults with :? syntax (fails if unset)
- Replace DB_PASS default 'password' with :? syntax (fails if unset)
- Separate RABBITMQ_PASS from DB_PASS, require explicit setting

Staging (docker-compose.staging.yml):
- Add sslmode=require to DATABASE_URL
- Replace all default passwords with :? syntax (fails if unset)

docker-compose up with these files will now FAIL if required secrets
are not explicitly provided via environment variables.

Addresses audit findings: A02 (Cryptographic Failures), section 7 (Infra).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:43:09 +01:00
senke
2d3cb18b7c fix(security): restrict CORS origins in stream-server
- Change default ALLOWED_ORIGINS from wildcard (*) to localhost:5173
  in veza-stream-server/docker-compose.yml
- Also fixed local .env (untracked) to use specific dev domains

Previously, the stream-server docker-compose defaulted to ALLOWED_ORIGINS=*
which would allow any origin to access the streaming API.

Addresses audit finding: A05 (Security Misconfiguration) — HIGH.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:42:04 +01:00
senke
db47f203f6 fix(security): implement JWT auth on stream-server WebSocket
- Validate JWT token via AuthManager before accepting WebSocket connections
- Extract user_id from validated token claims instead of trusting query params
- Reject unauthenticated connections with 401 Unauthorized
- Add `authenticated` field to WebSocketConnection struct
- Update websocket_handler_wrapper to handle auth error responses

Previously, the WebSocket handler accepted all connections without
validating the token (comment: "pour l'instant, on accepte la connexion").
Now requires a valid JWT token via ?token= query param or Authorization header.

Addresses audit finding: A01 (Broken Access Control) — CRITICAL.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:41:35 +01:00
senke
af4893e684 fix(ci): harden CI pipeline -- remove || true, fix versions
- Remove all `|| true` from govulncheck, cargo audit, npm audit,
  lint, and format check steps (was masking real failures)
- Remove `continue-on-error: true` from stream-server build step
- Fix Go version mismatch: CI 1.21 -> 1.23 (matches go.mod 1.23.8)
- Upgrade Node.js from 18 to 20 (current LTS)
- Replace deprecated actions-rs/toolchain@v1 with dtolnay/rust-toolchain@stable
- Upgrade all GitHub Actions to v4/v5 (checkout, setup-go, setup-node, cache)
- Make gofmt check fail properly on unformatted files

Addresses audit findings: A05 (Security Misconfiguration), A08 (Software
& Data Integrity), debt item 5 (CI || true everywhere).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:39:40 +01:00
senke
8e03d524cf chore: add .cursor/ to .gitignore
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:20:44 +01:00
senke
c02e30b417 chore(web): update .env.local and .env.storybook for domain config
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:20:31 +01:00
senke
8a0f008345 chore: playwright workflow, docs, rapports audit, visual-tests, tmt unit
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:19:34 +01:00
senke
3c742c3576 test(web): player, playlists, tracks tests; feat(playlists): permissions utils
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:19:24 +01:00
senke
a83a76e942 chore(rust): chat server env, veza-common auth, stream server routes/websocket
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:19:17 +01:00
senke
30f17dfc2a chore(backend): config, router, auth, stream service, sanitizer, tests
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:19:09 +01:00
senke
f53b7f7d8a chore: update docker-compose, make, tmt config
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:18:57 +01:00
senke
41eacaf97d ci: add npm audit and govulncheck to main CI (P3.4)
- Add govulncheck to backend-go job
- Add npm audit --audit-level=high to frontend job
- Both use || true to avoid blocking CI on existing vulns

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:15:22 +01:00
senke
3b2ff9faa8 test(web): add unit tests for chat feature (P3.3)
- ChatMessages: fix mock structure, align with store shape (messages Record, conversations)
- ChatInput: add tests for render, submit, disabled state
- ChatMessage: add tests for content, reactions, addReaction
- fix ChatMessage.tsx: remove stray // ... comment

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:15:13 +01:00
senke
a6bafcbcc5 feat(web): externalize feature flags via VITE_FEATURE_* env vars (P3.2)
- Parse VITE_FEATURE_* from env with fallback to current defaults
- Add all flags to .env.example and ENV_CONFIG.md
- parseFeatureEnv accepts true/1/yes for enabled

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:11:38 +01:00
senke
55fbeb9e48 feat(stream): add JWT revocation persistante Redis (P3.1)
- Add SessionRevocationStore trait with InMemoryRevocationStore and RedisRevocationStore
- Wire Redis store when REDIS_URL in config.cache, fallback in-memory
- Session revocation by session_id persists across restarts when using Redis

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:10:07 +01:00
senke
27722db148 feat(chat): add JWT revocation persistante Redis (P3.1)
- Add JwtRevocationStore trait with InMemoryRevocationStore and RedisRevocationStore
- Wire Redis store when REDIS_URL is set (fallback in-memory if Redis unavailable)
- JWT blacklist persists across restarts when using Redis

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:06:25 +01:00
senke
48ccb8527d fix(chat): restore compilation - add reactions module, imports, request_id param
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 22:04:11 +01:00
senke
bbbe557eca ci: add npm audit, govulncheck, cargo audit to CI
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 21:33:27 +01:00
senke
430cc5eef6 fix(security): validate exec.Command paths in Go services
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 21:32:38 +01:00
senke
816676906a docs: mark veza-mobile as abandoned, document ghost features
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 21:31:22 +01:00
senke
51869a3649 fix(deps): upgrade gin to 1.11
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 21:31:00 +01:00
senke
f52858f14b fix(security): validate OAuth redirect URL against allowlist, require auth for internal transcode endpoint
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 21:28:26 +01:00
senke
ceec16fbd5 fix(security): upgrade axios to fix CVE
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 19:51:39 +01:00
senke
7f63bc6641 fix(security): remove hardcoded credentials from stream server auth
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 19:50:17 +01:00
senke
44ddd3b858 chore(incus): add env template, document setup
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 19:49:01 +01:00
senke
d7bb127920 fix(security): stop tracking veza-stream-server/.env and config/incus env files
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 19:48:51 +01:00
senke
a1ce2d0c9f docs: baseline pré-remédiation
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 19:48:01 +01:00
senke
c458b7c597 fix(tests): cycle 20 – PlaylistForm flaky tests
- fireEvent.change/click au lieu de userEvent pour create/update/custom onSubmit
- description max length: fireEvent pour éviter timeout (2001 chars)
- expect.objectContaining pour assertions plus résilientes
- RAPPORT: cycle 20

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 09:51:44 +01:00
senke
1e2093f79b fix(tests): cycle 19 – playlistService MSW et handlers
- Supprimer handler wildcard playlists* qui masquait les spécifiques
- Réordonner: search et recommendations avant :id (évite id=search/recommendations)
- Handlers: GET recommendations, POST :id/share, search avec query empty
- list items: ajout title
- create: body.title → data.title/name, track_count, like_count
- Tests: addTrack(plId,trackId), removeTrack, createShareLink(plId)
- Assertions: getRecommendations.playlists, update retourne objet
- RAPPORT: cycle 19

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 09:48:36 +01:00
senke
ccc233e1ea fix(tests): cycles 12–18 – corrections services, mocks et design tokens
- chatService: getChannels → getServers
- commerceService: getOrders/getOrderDetails/getSalesStats → getPurchases/getSellerStats
- marketplaceService: mock réponse, params API, getDownloadLink → listOrders
- config/env.test: vi.stubEnv, import dynamique
- useAuth.test: mock useAuthStore
- TrackStatsDisplay, UploadQuota: mock du bon service (analyticsService, uploadService)
- TrackListEmpty, TrackListRow, TrackSearch: design tokens, assertions
- trackDownloadService, chunkedUploadService: MSW/server.use
- trackListService, trackSearchService, trackShareService: assertions
- ErrorBoundary, LoginForm, PlaylistErrorBoundary, PlaylistRecommendations
- RAPPORT_RESOLUTION_TESTS_CYCLE1.md

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-11 09:43:55 +01:00
senke
ef430d9f16 style(ui): pixel-perfect alignment for Sidebar, Header, Player via Spotify/Discord standard
- PlayerBarGlass: use semantic tokens (--player-glass-bg, --player-glass-border)
- Replace arbitrary OKLCH with CSS vars; backdrop-blur-md; rounded-xl
- Transitions: duration-[var(--duration-*)], ease-[var(--ease-out)]
- Sidebar: add border-r border-[var(--sidebar-border)] for depth
- Header: border-[var(--glass-border)] for subtle separation
- index.css: add --player-glass-bg, --player-glass-border (light + dark)
- visual baselines updated (0% diff Playwright)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 23:09:24 +01:00
senke
47f2c7c30b feat(ui): DeveloperDashboard skeleton, EmptyState, ErrorDisplay, Header transitions
- DeveloperDashboardViewSkeleton: premium skeleton for loading state
- EmptyState for API keys when none exist (variant card, Create action)
- ErrorDisplay with retry on fetch failure
- Header: duration-[var(--duration-fast)] on all interactive elements
- DeveloperDashboardView: table row hover, copy button transitions

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 22:59:51 +01:00
senke
c65da4fea6 refactor(ui): Design tokens - gradients, duration, textarea
- Replace cyan/magenta/purple gradients with primary/secondary
- duration-200/300 → duration-[var(--duration-normal)]
- Textarea: min-h-[100px] → min-h-24
- SearchPageHeader, DashboardPage, PlaylistHeader
- UserProfilePageHeader/Hero, PlaylistDetailPageHero
- SocialViewFeedItem, WishlistView, PostCard, ProductCard, CourseCard
- SearchPageResults, MarketplaceHome

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 22:56:30 +01:00
senke
2c6e5fddb1 refactor(ui): SearchPageHeader use primary/secondary tokens
- Replace cyan-400/magenta-500 with from-primary to-secondary
- Add duration token for clear button transition

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 22:53:15 +01:00
senke
149e616183 refactor(ui): Design tokens in PlaylistCard + TrackCard polish
- PlaylistCard: duration tokens, primary/secondary gradient (KŌDŌ)
- TrackCard: hover:-translate-y-0.5, ease-out token
- Remove arbitrary purple-500/pink-500, duration-200/300

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 22:52:57 +01:00
senke
557e254931 feat(ui): Header search navigates to /search on Enter
- Press Enter in header search → navigate to /search?q=query
- Add role=search, aria-label, focus-visible ring
- Use duration token for transition

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 22:52:31 +01:00
senke
95e31646cb feat(ui): Sidebar refactor, premium skeletons, ContentFadeIn transitions
- Sidebar: useSidebarNavigation hook, ARIA, token-based layout
- Layout: lg:ml-main-expanded/collapsed (replace arbitrary ml-64)
- TrackCardSkeleton + PlaylistCardSkeleton: KŌDŌ tokens, min-heights for CLS
- ContentFadeIn: 200ms fade-in with --ease-out
- TrackGrid, PlaylistList, LibraryPage: integrate skeletons + fade-in
- Player: player-bar subcomponents, useAudioAnalyser
- Tests: TrackGrid wrapper (QueryClient, ToastProvider)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 22:51:51 +01:00
senke
0f25d3c551 fix(webhooks): add DB migration and avoid 500 toast on developer portal
Backend:
- Add migrations/075_create_webhooks.sql: webhooks + webhook_failures tables
- Fixes GET /webhooks 500 (relation "webhooks" did not exist)

Frontend:
- Skip toast for 5xx on /webhooks so developer portal shows empty state
  instead of 'Une erreur serveur s'est produite' when table is missing

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 21:11:32 +01:00
senke
ae4e184fad fix(web): silence console for expected failures (CSRF, webhooks 5xx)
- csrf: no log when backend returns HTML (wrong server / not running)
- webhookService: no log for 5xx on list webhooks
- api client: no log for 5xx on /webhooks (main + queued request)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 19:51:20 +01:00
senke
ee3225a75d fix(web): reduce webhook/CSRF console noise
- webhookService: treat parsed error.code (500) as 5xx and log at DEBUG
- csrf: log 'backend may not be running' at DEBUG instead of WARN

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 19:48:48 +01:00
senke
dcaaeec962 fix(web): close Create API Key modal after successful key creation
- handleCreateKey now returns the new key so the modal receives result
- Modal handles undefined result and api_key shape; no more TypeError on result.key
- On error, parent still shows toast and rethrows so modal stays on step 1

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 19:46:32 +01:00
senke
d744715f38 fix(web): rename duplicate status variable in api client error handler
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 19:38:54 +01:00
senke
8f3b562edb fix(web): reduce developer portal console errors
- CSRF: hint uses VITE_BACKEND_PORT instead of hardcoded 8080
- Proxy: add /swagger to Vite dev server for Swagger doc.json (fixes YAMLException)
- playerService: validate media URL before load to avoid Invalid URI errors
- usePlayer: log invalid URL/network audio errors at DEBUG level
- SwaggerUI: log HTML-instead-of-JSON parse errors at DEBUG
- webhookService: log 5xx backend errors at DEBUG
- api client: log 5xx /webhooks errors at DEBUG (reduces duplicate noise)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 19:38:13 +01:00
senke
f979c6fa8f fix(web): reduce console noise when backend unavailable
- Skip retry for ERR_BAD_RESPONSE / HTML instead of JSON (wrong server)
- Log only first API retry attempt instead of all 3
- CSRF: friendly warn when wrong server, avoid duplicate logs
- App init: skip CSRF warn when wrong server (already shown)
- API client: skip CSRF refresh error log when wrong server
- ReactQuerySync: INFO → DEBUG for enable/disable messages

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 19:29:10 +01:00
senke
64fbb81ddf ui(design): Phase 3 - rounded tokens, min-w/min-h, stories, NavigationProgress
- rounded-[var(--radius-xl/md/lg/sm)] → rounded-xl, rounded-md, rounded-lg, rounded-sm
- Timeline: min-w-[200px] → min-w-50
- AddEquipmentView, MetadataForm: min-h-[100px] → min-h-25
- NavigationProgress: shadow-[...] → shadow-button-primary-glow
- Stories: ActivityGraph, StatCard, NotificationBell, LoadingState, ScrollArea, Skeleton, FileUploadZone
- Reduced arbitrary values from ~60+ to 11 (5 files, exceptions documented)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 19:24:07 +01:00
senke
4b9d0a341d ui(design): migrate ImageCropper, PlaybackSummary to layout tokens
- ImageCropper: h-[80vh] → h-layout-modal-sm (80vh)
- PlaybackSummary: h-[200px] → min-h-50 (scale Tailwind)
- Add h-layout-modal-sm utility class

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 14:07:19 +01:00