# Sidebar Injection Architecture ## Overview The soleprint sidebar is injected into managed app pages using a hybrid nginx + JavaScript approach. This allows any frontend framework (React, Next.js, static HTML) to receive the sidebar without code modifications. ## How It Works ``` ┌─────────────────────────────────────────────────────────────────┐ │ Browser Request │ │ http://room.spr.local.ar/ │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Nginx │ │ │ │ 1. Routes /spr/* → soleprint:PORT (sidebar assets + API) │ │ 2. Routes /* → frontend:PORT (app pages) │ │ 3. Injects CSS+JS into HTML responses via sub_filter │ │ │ │ sub_filter '' │ │ ' │ │ '; │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Browser Renders │ │ │ │ 1. Page loads with injected CSS (sidebar styles ready) │ │ 2. sidebar.js executes (deferred, after DOM ready) │ │ 3. JS fetches /spr/api/sidebar/config (room name, auth, etc) │ │ 4. JS creates sidebar DOM elements and injects into page │ │ 5. Sidebar appears on left side, pushes content with margin │ └─────────────────────────────────────────────────────────────────┘ ``` ## Key Design Decisions ### Why nginx sub_filter instead of modifying app code? - **Framework agnostic**: Works with any frontend (Next.js, React, Vue, static HTML) - **No app changes needed**: The managed app doesn't need to know about soleprint - **Easy to disable**: Just access `room.local.ar` instead of `room.spr.local.ar` ### Why inject into `` instead of ``? Next.js and other streaming SSR frameworks may not include `` in the initial HTML response. The `` tag is always present, so we inject both CSS and JS there using `defer` to ensure JS runs after DOM is ready. ### Why JavaScript injection instead of iframe? - **No iframe isolation issues**: Sidebar can interact with page if needed - **Better UX**: No double scrollbars, native feel - **Simpler CSS**: Just push content with `margin-left` ## Nginx Configuration ### For Local Development (system nginx) Each room needs entries in `/etc/nginx/sites-enabled/`: ```nginx # room.spr.local.ar - app with sidebar server { listen 80; server_name room.spr.local.ar; # Soleprint assets and API location /spr/ { proxy_pass http://127.0.0.1:SOLEPRINT_PORT/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # Backend API (if applicable) location /api/ { proxy_pass http://127.0.0.1:BACKEND_PORT/api/; # ... headers } # Frontend with sidebar injection location / { proxy_pass http://127.0.0.1:FRONTEND_PORT; proxy_set_header Host $host; proxy_set_header Accept-Encoding ""; # Required for sub_filter proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # Inject sidebar sub_filter '' ''; sub_filter_once off; sub_filter_types text/html; } } # room.local.ar - app without sidebar (direct access) server { listen 80; server_name room.local.ar; # ... same as above but without sub_filter } ``` ### For Docker/AWS (nginx container) The nginx config lives in `cfg//soleprint/nginx/local.conf` and uses docker service names instead of localhost ports: ```nginx location /spr/ { proxy_pass http://soleprint:8000/spr/; } location / { proxy_pass http://frontend:3000; # ... sub_filter same as above } ``` ## Port Allocation Each room should use unique ports to allow concurrent operation: | Room | Soleprint | Frontend | Backend | |--------|-----------|----------|---------| | amar | 12000 | 3000 | 8001 | | dlt | 12010 | 3010 | - | | sample | 12020 | 3020 | 8020 | ## Sidebar Assets The sidebar consists of two files served by soleprint: - `/sidebar.css` - Styles for the sidebar (dark theme, positioning) - `/sidebar.js` - Self-contained JS that fetches config and renders sidebar Source location: `soleprint/station/tools/sbwrapper/` ## Sidebar Config API The sidebar JS fetches configuration from `/spr/api/sidebar/config`: ```json { "room": "amar", "soleprint_base": "/spr", "auth_enabled": true, "tools": { "artery": "/spr/artery", "atlas": "/spr/atlas", "station": "/spr/station" }, "auth": { "login_url": "/spr/artery/google/oauth/login", "logout_url": "/spr/artery/google/oauth/logout" } } ``` ## Troubleshooting ### Sidebar not appearing 1. Check if soleprint is running: `curl http://room.spr.local.ar/spr/sidebar.js` 2. Check browser console for `[Soleprint]` messages 3. Verify nginx has `Accept-Encoding ""` set (required for sub_filter) 4. Hard refresh (Ctrl+Shift+R) to clear cached HTML ### Wrong room config showing Each room needs its own docker network (`room_network`) to isolate services. Check `NETWORK_NAME` in `.env` files and ensure containers are on correct networks. ### sub_filter not working - Ensure `proxy_set_header Accept-Encoding ""` is set - Check that response is `text/html` (sub_filter_types) - Streaming SSR may not have ``, use `` injection instead