Skip to main content

Frontend Architecture

End-user social app for Bellamy Book (self-hosted social network). Members can also use the Mobile App (store download → enter your API URL) against the same API.

Admin is separate

Site operators use the Admin Panel, not routes inside this SPA.

Technology stack​

LayerChoice
UIReact 19, Bootstrap 5, React Bootstrap, SCSS
BuildVite 6
RoutingReact Router DOM 7 (src/app/**/page.jsx convention — not Next.js)
StateRedux Toolkit + redux-persist; React Context for chat/calls/layout
HTTPAxios (authHttpClient) + endpoints.js
RealtimeSignalR → ChatWorker / WebSocketWorker hubs
CallsLiveKit
Formsreact-hook-form + yup
CAPTCHACloudflare Turnstile
PWAService workers + Web Push

Project structure​

Src/frontend/src/
├── app/ # Pages (plain + social)
├── components/ # Cards, chat, comments, layout, search, social, ads
├── layouts/ # Feed, Social, Profile, Settings
├── routes/ # Route tables + AppRouter
├── services/ # Axios clients, SignalR, LiveKit helpers
├── store/ # Redux (auth, notifications, theme, ui)
├── context/ # Chat, calls, layout
├── hooks/
├── config/environment.js
└── assets/scss/

Route groups​

GroupExamples
Public / marketing/ when VITE_SHOW_MARKETING_LANDING=true, /landing, /pricing, /contact, /blogs, /about
Auth/auth/sign-in, /auth/sign-up, Google callback, impersonate
Feed/feed/home, groups, albums, videos
Social shell/messaging, /notifications, post detail, hashtags
Profile/profile/* (incl. /profile/saved), /social/profile/:userId/*
Settingsaccount, privacy, tickets, messaging
Self-host vs official site homepage
DeployVITE_SHOW_MARKETING_LANDINGGuest /
dockerPublish (customer community)falseSign-in
dockerProd (bellamybook.com)trueMarketing landing

Same SPA image; flag is runtime (.env → /config.js). See Environment.

State management​

  • Redux: auth, notifications (not persisted); theme, ui (persisted)
  • Context: chat UI, call state, layout
  • No React Query / Zustand

API & auth​

  • Base URL from environment / runtime config
  • Header Authorization: Bearer <accessToken>
  • Header X-App-Context: frontend (refresh cookie namespace distinct from admin)
  • Access token in localStorage; refresh via HttpOnly cookie (or localStorage in local mode)
  • Protected routes wait for initializeAuth()

Realtime​

ConcernHub hostPath
Chat / presenceChatWorker/hubs/chat, /hubs/chat-online
Notifications / online usersWebSocketWorker/hubs/notification, /hubs/onlineusers
Voice/videoLiveKit rooms via api/Call—

The API is primarily HTTP REST. SignalR hubs run on workers, not on the API process.