# CLAUDE.md - Project Intelligence ## Project Overview **Org-Parking** is a manager-centric parking spot management system for organizations. It features fair parking assignment based on presence/parking ratio, supporting both standalone JWT authentication and Authelia/LLDAP SSO integration. ### Technology Stack - **Backend:** FastAPI + SQLAlchemy + SQLite - **Frontend:** Vanilla JavaScript (no frameworks) - **Auth:** JWT tokens + Authelia SSO support - **Containerization:** Docker + Docker Compose - **Rate Limiting:** slowapi ### Architecture ``` app/ ├── config.py → Configuration with logging and validation └── routes/ → API endpoints (auth, users, managers, presence, parking) services/ → Business logic (parking algorithm, auth, notifications) database/ → SQLAlchemy models and connection frontend/ → Static HTML pages + JS modules utils/ ├── auth_middleware.py → JWT/Authelia authentication └── helpers.py → Shared utility functions ``` ## Build & Run Commands ```bash # Development SECRET_KEY=dev-secret-key python -m uvicorn main:app --reload --host 0.0.0.0 --port 8000 # Docker docker compose up -d # Dependencies pip install -r requirements.txt # Initialize test database python create_test_db.py ``` ## Code Style & Conventions ### Python - FastAPI async patterns with `Depends()` for dependency injection - Pydantic models for request/response validation - SQLAlchemy ORM (no raw SQL) - Use `generate_uuid()` from `utils.helpers` for UUIDs - Use `config.logger` for logging (not print statements) - Dates stored as TEXT in "YYYY-MM-DD" format ### JavaScript - ES6 modules with centralized API client (`/js/api.js`) - Token stored in localStorage, auto-included in requests - Utility functions in `/js/utils.js` - Role-based navigation in `/js/nav.js` ### Authentication - Dual mode: JWT tokens (standalone) or Authelia headers (SSO) - LDAP users have `password_hash = None` - Use helper: `is_ldap_user(user)` from `utils.helpers` ### Utility Functions (`utils/helpers.py`) ```python from utils.helpers import ( generate_uuid, # Use instead of str(uuid.uuid4()) is_ldap_user, # Check if user is LDAP-managed is_ldap_admin, # Check if user is LDAP admin validate_password, # Returns list of validation errors format_password_errors, # Format errors into user message get_notification_default # Get setting value with default ) ``` --- ## Configuration (`app/config.py`) Configuration is environment-based with required validation: ### Required - `SECRET_KEY` - **MUST** be set (app exits if missing) ### Security - `RATE_LIMIT_REQUESTS` - Requests per window (default: 5) - `RATE_LIMIT_WINDOW` - Window in seconds (default: 60) ### Email (org-stack pattern) - `SMTP_ENABLED` - Set to `true` to enable SMTP sending - When disabled, emails are logged to `EMAIL_LOG_FILE` - Follows org-stack pattern: direct send with file fallback ### Logging - `LOG_LEVEL` - DEBUG, INFO, WARNING, ERROR (default: INFO) - Use `config.logger` for all logging --- ## Notifications (`services/notifications.py`) Simplified notification service following org-stack pattern: ```python from services.notifications import ( send_email, # Direct send or file fallback notify_parking_assigned, # When spot assigned notify_parking_released, # When spot released notify_parking_reassigned, # When spot reassigned send_presence_reminder, # Weekly presence reminder send_weekly_parking_summary, # Friday parking summary send_daily_parking_reminder, # Daily parking reminder run_scheduled_notifications # Called by cron/scheduler ) ``` ### Email Behavior 1. If `SMTP_ENABLED=true`: Send via SMTP 2. If SMTP fails or disabled: Log to `EMAIL_LOG_FILE` 3. Never throws - always returns success/failure --- ## Recent Improvements ### Security Enhancements - **Required SECRET_KEY**: App exits if not set - **Rate limiting**: Login/register endpoints limited to 5 req/min - **Password validation**: Requires uppercase, lowercase, number, 8+ chars - **Proper logging**: All security events logged ### Performance Optimizations - **Fixed N+1 queries** in: - `list_users()` - Batch query for manager names and counts - `list_managers()` - Batch query for managed user counts - `get_manager_guarantees()` - Batch query for user names - `get_manager_exclusions()` - Batch query for user names ### Code Consolidation - **Utility functions** (`utils/helpers.py`): - `generate_uuid()` - Replaces 50+ `str(uuid.uuid4())` calls - `is_ldap_user()` - Replaces 4+ duplicated checks - `validate_password()` - Consistent password validation - **Simplified notifications** - Removed queue system, direct send ### Logging Improvements - Centralized logging via `config.logger` - Replaced `print()` with proper logging - Security events logged (login, password change, etc.) --- ## API Quick Reference ### Authentication - `POST /api/auth/register` - Create user (rate limited) - `POST /api/auth/login` - Get JWT token (rate limited) - `GET /api/auth/me` - Current user (JWT or Authelia) ### Presence - `POST /api/presence/mark` - Mark single day - `POST /api/presence/mark-bulk` - Mark multiple days - `GET /api/presence/team` - Team calendar with parking ### Parking - `POST /api/parking/manual-assign` - Manager assigns spot - `POST /api/parking/reassign-spot` - Reassign existing spot - `GET /api/parking/eligible-users/{id}` - Users for reassignment ### Manager Settings - `GET/POST/DELETE /api/managers/closing-days` - `GET/POST/DELETE /api/managers/weekly-closing-days` - `GET/POST/DELETE /api/managers/guarantees` - `GET/POST/DELETE /api/managers/exclusions` --- ## Development Notes ### Adding a New Route 1. Create file in `app/routes/` 2. Use `APIRouter(prefix="/api/...", tags=["..."])` 3. Register in `main.py`: `app.include_router(...)` 4. Add auth dependency: `current_user: User = Depends(get_current_user)` 5. Use `config.logger` for logging 6. Use `generate_uuid()` for new records ### Database Changes No migration system (Alembic) configured. Schema changes require: 1. Update [database/models.py](database/models.py) 2. Delete SQLite file or write manual migration 3. Run `create_test_db.py` for fresh database ### Email Testing With `SMTP_ENABLED=false`, check email log: ```bash tail -f /tmp/parking-emails.log ``` ### Running Scheduled Notifications Add to cron or systemd timer: ```bash # Every 5 minutes */5 * * * * cd /path/to/org-parking && python -c " from database.connection import get_db from services.notifications import run_scheduled_notifications db = next(get_db()) run_scheduled_notifications(db) " ``` --- ## File Quick Links | Purpose | File | |---------|------| | Main entry | [main.py](main.py) | | Configuration | [app/config.py](app/config.py) | | Database models | [database/models.py](database/models.py) | | Parking algorithm | [services/parking.py](services/parking.py) | | Notifications | [services/notifications.py](services/notifications.py) | | Auth middleware | [utils/auth_middleware.py](utils/auth_middleware.py) | | Utility helpers | [utils/helpers.py](utils/helpers.py) | | Frontend API client | [frontend/js/api.js](frontend/js/api.js) | | CSS styles | [frontend/css/styles.css](frontend/css/styles.css) | | Docker config | [compose.yml](compose.yml) | | Environment template | [.env.example](.env.example) | --- ## Deployment Notes ### Remote Server - Host: `rocketscale.it` - User: `rocky` - SSH: `ssh rocky@rocketscale.it` - Project path: `/home/rocky/org-parking` - Related project: `/home/rocky/org-stack` (LLDAP, Authelia, etc.) ### Environment Variables Copy `.env.example` to `.env` and configure: ```bash # Generate secure key openssl rand -hex 32 ``` ### Production Checklist - [ ] Set strong `SECRET_KEY` - [ ] Configure `ALLOWED_ORIGINS` (not `*`) - [ ] Set `AUTHELIA_ENABLED=true` if using SSO - [ ] Configure SMTP or check email log file - [ ] Set up notification scheduler (cron/systemd)