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
6.1 KiB
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):
# 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:
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:
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:
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:
access_control:
default_policy: deny
rules:
# ... existing rules ...
# Parking Manager - require authentication
- domain: parking.rocketscale.it
policy: one_factor
After editing, restart Authelia:
cd ~/org-stack
docker compose restart authelia
Step 6: Create LLDAP Group
In lldap (https://ldap.rocketscale.it):
- Create group:
parking_admins - Add yourself (or whoever should be admin) to this group
Role Management:
parking_adminsgroup → 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
# 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
- Go to https://parking.rocketscale.it
- You should be redirected to Authelia for login
- After login, you should see the parking app
- Your user should be auto-created with
adminrole (if in parking-admins group)
Troubleshooting
403 Forbidden from Authelia
- Authelia's
access_controldoesn'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/meendpoint (not check localStorage) - Ensure all page JS files use:
currentUser = await api.requireAuth();
User has wrong role
- Admin role: Verify user is in
parking_adminsLLDAP 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_adminsgroup
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:
- JWT mode (standalone): Users login via
/login, get JWT token stored in localStorage - 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/meto get user info (backend reads headers)
Role Management
Only the admin role is synced from LLDAP:
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