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
| Layer | Choice |
|---|---|
| UI | React 19, Bootstrap 5, React Bootstrap, SCSS |
| Build | Vite 6 |
| Routing | React Router DOM 7 (src/app/**/page.jsx convention — not Next.js) |
| State | Redux Toolkit + redux-persist; React Context for chat/calls/layout |
| HTTP | Axios (authHttpClient) + endpoints.js |
| Realtime | SignalR → ChatWorker / WebSocketWorker hubs |
| Calls | LiveKit |
| Forms | react-hook-form + yup |
| CAPTCHA | Cloudflare Turnstile |
| PWA | Service 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
| Group | Examples |
|---|---|
| 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/* |
| Settings | account, privacy, tickets, messaging |
Self-host vs official site homepage
| Deploy | VITE_SHOW_MARKETING_LANDING | Guest / |
|---|---|---|
| dockerPublish (customer community) | false | Sign-in |
| dockerProd (bellamybook.com) | true | Marketing 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
| Concern | Hub host | Path |
|---|---|---|
| Chat / presence | ChatWorker | /hubs/chat, /hubs/chat-online |
| Notifications / online users | WebSocketWorker | /hubs/notification, /hubs/onlineusers |
| Voice/video | LiveKit rooms via api/Call | — |
The API is primarily HTTP REST. SignalR hubs run on workers, not on the API process.