Key changes: - Removed office-centric model (deleted offices.py, office-rules) - Renamed to team-rules, managers are part of their own team - Team calendar visible to all (read-only for employees) - Admins can have a manager assigned
220 lines
6.1 KiB
Markdown
220 lines
6.1 KiB
Markdown
# Deployment Guide for parking.rocketscale.it
|
|
|
|
## Prerequisites
|
|
|
|
- org-stack running on rocky@rocketscale.it
|
|
- Git repository on git.rocketscale.it (optional, can use rsync)
|
|
|
|
## Directory Structure
|
|
|
|
Parking is deployed as a **separate directory** alongside org-stack:
|
|
|
|
```
|
|
~/
|
|
├── org-stack/ # Main stack (Caddy, Authelia, LLDAP, etc.)
|
|
│ ├── compose.yml
|
|
│ ├── Caddyfile
|
|
│ ├── authelia/
|
|
│ └── .env
|
|
│
|
|
└── org-parking/ # Parking app (separate)
|
|
├── compose.yml # Production compose (connects to org-stack network)
|
|
├── .env # Own .env with PARKING_SECRET_KEY
|
|
├── Dockerfile
|
|
└── ...
|
|
```
|
|
|
|
## Step 1: Deploy to Server
|
|
|
|
Option A - Using rsync (recommended for development):
|
|
```bash
|
|
# From development machine
|
|
rsync -avz --exclude '.git' --exclude '__pycache__' --exclude '*.pyc' \
|
|
--exclude '.env' --exclude 'data/' --exclude '*.db' --exclude '.venv/' \
|
|
/path/to/org-parking/ rocky@rocketscale.it:~/org-parking/
|
|
```
|
|
|
|
Option B - Using git:
|
|
```bash
|
|
ssh rocky@rocketscale.it
|
|
cd ~
|
|
git clone git@git.rocketscale.it:rocky/parking-manager.git org-parking
|
|
```
|
|
|
|
## Step 2: Create Production compose.yml
|
|
|
|
Create `~/org-parking/compose.yml` on the server:
|
|
|
|
```yaml
|
|
services:
|
|
parking:
|
|
build: .
|
|
container_name: parking
|
|
restart: unless-stopped
|
|
volumes:
|
|
- parking_data:/app/data
|
|
environment:
|
|
- SECRET_KEY=${PARKING_SECRET_KEY}
|
|
- DATABASE_PATH=/app/data/parking.db
|
|
- AUTHELIA_ENABLED=true
|
|
- ALLOWED_ORIGINS=https://parking.rocketscale.it
|
|
- SMTP_HOST=${SMTP_HOST:-}
|
|
- SMTP_PORT=${SMTP_PORT:-587}
|
|
- SMTP_USER=${SMTP_USER:-}
|
|
- SMTP_PASSWORD=${SMTP_PASSWORD:-}
|
|
- SMTP_FROM=${SMTP_FROM:-}
|
|
networks:
|
|
- org-network
|
|
healthcheck:
|
|
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 10s
|
|
|
|
volumes:
|
|
parking_data:
|
|
|
|
networks:
|
|
org-network:
|
|
external: true
|
|
name: org-stack_org-network
|
|
```
|
|
|
|
## Step 3: Create .env File
|
|
|
|
Create `~/org-parking/.env` with a secret key:
|
|
|
|
```bash
|
|
cd ~/org-parking
|
|
python3 -c "import secrets; print(f'PARKING_SECRET_KEY={secrets.token_hex(32)}')" > .env
|
|
```
|
|
|
|
**Note**: Each directory needs its own `.env` file since docker compose only reads from the current directory.
|
|
|
|
## Step 4: Add to Caddyfile
|
|
|
|
Add to `~/org-stack/Caddyfile`:
|
|
|
|
```
|
|
# Parking Manager - Protected by Authelia
|
|
parking.rocketscale.it {
|
|
import auth
|
|
reverse_proxy parking:8000
|
|
}
|
|
```
|
|
|
|
## Step 5: Add Authelia Access Control Rule
|
|
|
|
**Important**: Authelia's `access_control` must include parking.rocketscale.it or you'll get 403 Forbidden.
|
|
|
|
Edit `~/org-stack/authelia/configuration.yml` and add to the `access_control.rules` section:
|
|
|
|
```yaml
|
|
access_control:
|
|
default_policy: deny
|
|
rules:
|
|
# ... existing rules ...
|
|
|
|
# Parking Manager - require authentication
|
|
- domain: parking.rocketscale.it
|
|
policy: one_factor
|
|
```
|
|
|
|
After editing, restart Authelia:
|
|
```bash
|
|
cd ~/org-stack
|
|
docker compose restart authelia
|
|
```
|
|
|
|
## Step 6: Create LLDAP Group
|
|
|
|
In lldap (https://ldap.rocketscale.it):
|
|
|
|
1. Create group: `parking_admins`
|
|
2. Add yourself (or whoever should be admin) to this group
|
|
|
|
**Role Management:**
|
|
- `parking_admins` group → **admin** role (synced from LLDAP on each login)
|
|
- **manager** role → assigned by admin in the app UI (Manage Users page)
|
|
- **employee** role → default for all other users
|
|
|
|
The admin can promote users to manager and assign offices via the Manage Users page.
|
|
|
|
## Step 7: Build and Deploy
|
|
|
|
```bash
|
|
# Build and start parking service
|
|
cd ~/org-parking
|
|
docker compose build parking
|
|
docker compose up -d
|
|
|
|
# Reload Caddy to pick up new domain (from org-stack)
|
|
cd ~/org-stack
|
|
docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile
|
|
|
|
# Check logs
|
|
cd ~/org-parking
|
|
docker compose logs -f parking
|
|
```
|
|
|
|
## Step 8: Verify
|
|
|
|
1. Go to https://parking.rocketscale.it
|
|
2. You should be redirected to Authelia for login
|
|
3. After login, you should see the parking app
|
|
4. Your user should be auto-created with `admin` role (if in parking-admins group)
|
|
|
|
## Troubleshooting
|
|
|
|
### 403 Forbidden from Authelia
|
|
- Authelia's `access_control` doesn't have a rule for parking.rocketscale.it
|
|
- Add the domain to `~/org-stack/authelia/configuration.yml` (see Step 6)
|
|
- Restart Authelia: `docker compose restart authelia`
|
|
|
|
### 401 Unauthorized
|
|
- Check Authelia headers are being passed
|
|
- Check `docker compose logs authelia`
|
|
|
|
### Login redirect loop (keeps redirecting to /login)
|
|
- Frontend JS must use async auth checking for Authelia mode
|
|
- The `api.requireAuth()` must call `/api/auth/me` endpoint (not check localStorage)
|
|
- Ensure all page JS files use: `currentUser = await api.requireAuth();`
|
|
|
|
### User has wrong role
|
|
- **Admin role**: Verify user is in `parking_admins` LLDAP group (synced on each login)
|
|
- **Manager role**: Must be assigned by admin via Manage Users page (not from LLDAP)
|
|
- **Employee role**: Default for users not in `parking_admins` group
|
|
|
|
### Database errors
|
|
- Check volume mount: `docker compose exec parking ls -la /app/data`
|
|
- Check permissions: `docker compose exec parking id`
|
|
|
|
## Architecture Notes
|
|
|
|
### Authelia Integration
|
|
The app supports two authentication modes:
|
|
1. **JWT mode** (standalone): Users login via `/login`, get JWT token stored in localStorage
|
|
2. **Authelia mode** (SSO): Authelia handles login, passes headers to backend
|
|
|
|
When `AUTHELIA_ENABLED=true`:
|
|
- Backend reads user info from headers: `Remote-User`, `Remote-Email`, `Remote-Name`, `Remote-Groups`
|
|
- Users are auto-created on first login
|
|
- Roles are synced from LLDAP groups on each request
|
|
- Frontend calls `/api/auth/me` to get user info (backend reads headers)
|
|
|
|
### Role Management
|
|
|
|
Only the **admin** role is synced from LLDAP:
|
|
```python
|
|
AUTHELIA_ADMIN_GROUP = "parking_admins" # → role: admin
|
|
```
|
|
|
|
Other roles are managed within the app:
|
|
- **manager**: Assigned by admin via Manage Users page
|
|
- **employee**: Default role for all non-admin users
|
|
|
|
This separation allows:
|
|
- LLDAP to control who has admin access
|
|
- App admin to assign manager roles and office assignments without LLDAP changes
|