# 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 There are two options for local development: ### Option 1: Docker Nginx (Recommended for portability) Each room includes a docker-compose.nginx.yml that runs nginx in a container. ```bash # Add to /etc/hosts 127.0.0.1 sample.spr.local.ar sample.local.ar # Start room with nginx cd gen//soleprint docker compose -f docker-compose.yml -f docker-compose.nginx.yml up -d ``` The nginx config in `cfg//soleprint/nginx/local.conf` uses docker service names: ```nginx location /spr/ { proxy_pass http://soleprint:8000/; } location / { proxy_pass http://frontend:80; # ... sub_filter for sidebar injection } ``` **Pros**: Portable, no system dependencies, isolated per room **Cons**: Only one room can use port 80 at a time ### Option 2: System Nginx (For running multiple rooms) If you need multiple rooms running simultaneously, use your system's nginx. 1. Install nginx: `sudo apt install nginx` 2. Add hosts entries for all rooms: ``` # /etc/hosts 127.0.0.1 amar.spr.local.ar amar.local.ar 127.0.0.1 dlt.spr.local.ar dlt.local.ar 127.0.0.1 sample.spr.local.ar sample.local.ar ``` 3. Create config in `/etc/nginx/sites-enabled/spr_local.conf`: ```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/; 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; } # 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 locations but without sub_filter in / block } ``` 4. Reload nginx: `sudo nginx -t && sudo systemctl reload nginx` **Pros**: Multiple rooms on port 80 via different hostnames **Cons**: Requires system nginx, manual config updates ## 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