218 lines
8.2 KiB
Markdown
218 lines
8.2 KiB
Markdown
# 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 '</head>' │
|
|
│ '<link rel="stylesheet" href="/spr/sidebar.css"> │
|
|
│ <script src="/spr/sidebar.js" defer></script></head>'; │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ 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 `</head>` instead of `</body>`?
|
|
|
|
Next.js and other streaming SSR frameworks may not include `</body>` in the initial HTML response. The `</head>` 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/<room>/soleprint
|
|
docker compose -f docker-compose.yml -f docker-compose.nginx.yml up -d
|
|
```
|
|
|
|
The nginx config in `cfg/<room>/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 '</head>' '<link rel="stylesheet" href="/spr/sidebar.css"><script src="/spr/sidebar.js" defer></script></head>';
|
|
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 `</body>`, use `</head>` injection instead
|