P8: Invalidate all Redis sessions when is_system_admin changes - Added is_system_admin to UserUpdate schema and UserResponse - Added invalidate_all_user_sessions call in users.py route - Added is_system_admin param to user_service.update_user P9: Remove no-op permission resolution strategies - Only highest_wins supported, others removed as no-ops - Updated tenant.py CheckConstraint to only allow highest_wins - Added KI-Kommentar in permissions.py P10: Remove legacy check_permission from auth.py - Removed duplicate check_permission and filter_fields_by_permission - Fixed ai_copilot_service.py to use permissions.check_permission - Updated ai_copilot route to pass resolved permissions dict P11: Verified — no guest_users remnants found P12: Migrate ContactFolderPermission to EntityPermission - contact_folder_permission_service now delegates to entity_permission_service - contact_folder_service uses EntityPermission queries - Removed ContactFolderPermission from models/__init__.py - Created migration 0114 to migrate data and drop table P13: Added RLS migration history comment in alembic/env.py P14: Verified — services already apply visibility_filter - saved_filters/views filter by user_id (personal data) - workspaces are UI context only - notifications already filter by entity access P15: Split entity_permission_service.py (932 lines) into 4 modules - permission_resolver.py: get_effective_access, get_visible_ids, etc. - permission_cache.py: Redis caching functions - permission_audit.py: Audit logging helpers - entity_permission_service.py: CRUD operations + re-exports P16: Centralize PERM_RANK in permissions.py - Single source: app.core.permissions.PERM_RANK - Updated all services to import from permissions.py P17: Fix MIGRATION_DATABASE_URL to use crm_migration - docker-compose.yaml defaults changed from crm_user to crm_migration - .env.docker.example updated - prestart.sh comment updated
LeoCRM v1.0
Self-hosted CRM for small sales teams (5–25 sales reps). Stack: FastAPI + SQLAlchemy (async) + PostgreSQL + Redis + React 18 + TypeScript + Vite + TanStack Query + Zustand + Tailwind + Docker + Coolify
Quick Start (Development)
1. Clone and Setup
git clone <repo-url> leocrm
cd leocrm
# Create virtual environment
python3 -m venv .venv
source .venv/bin/activate
# Install dependencies
pip install -r requirements.txt -r requirements-dev.txt
2. Configure Environment
cp .env.example .env
# Generate a secure SECRET_KEY (min 32 chars)
python3 -c "import secrets; print('SECRET_KEY=' + secrets.token_urlsafe(48))" >> .env
# Edit .env and set DATABASE_URL, REDIS_URL, SECRET_KEY
nano .env
3. Initialize Database
# Apply migrations
alembic upgrade head
4. Run Server
# Development with auto-reload
uvicorn app.main:app --reload --port 8000
# Start ARQ worker (for background jobs)
arq app.core.jobs.WorkerSettings
Open:
- API: http://localhost:8000
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
- Health: http://localhost:8000/api/v1/health
- Metrics: http://localhost:8000/api/v1/metrics (admin-only)
Production Setup
Docker Compose
cp .env.example .env
# Edit .env — set DATABASE_URL, REDIS_URL, SECRET_KEY, CORS_ORIGINS
# Set ENVIRONMENT=production, SESSION_COOKIE_SECURE=true
docker compose up -d
# Run migrations
docker compose exec api alembic upgrade head
Manual (without Docker)
pip install -r requirements.txt
alembic upgrade head
# Start API server (2 workers)
uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 2
# Start ARQ worker (separate process)
arq app.core.jobs.WorkerSettings
See docs/admin-guide.md for detailed deployment, backup, and troubleshooting instructions.
API
Key Endpoints
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/api/v1/health |
GET | No | Health check (DB, Redis, storage, worker) |
/api/v1/metrics |
GET | Admin | Prometheus metrics (text/plain) |
/api/v1/auth/login |
POST | No | Login |
/api/v1/contacts |
GET | Yes | List contacts (paginated, max page_size=100) |
/api/v1/contacts/export |
GET | Yes | Stream contacts as CSV |
/api/v1/companies |
GET | Yes | List companies (paginated, max page_size=100) |
/api/v1/companies/export |
GET | Yes | Stream companies as CSV |
Pagination
All list endpoints support pagination with page and page_size parameters.
page_size is capped at 100 — values >100 return HTTP 422.
CSV Export
Contacts and companies support streaming CSV export via /export?format=csv.
Uses StreamingResponse — does not buffer the entire file in memory.
Swagger UI
Interactive API documentation: http://localhost:8000/docs
See docs/api-overview.md for the full endpoint summary.
Monitoring
Health Check
curl http://localhost:8000/api/v1/health
Returns JSON with overall status (healthy/degraded) and individual checks for
database, redis, storage, and worker.
Prometheus Metrics
# Requires admin authentication
curl -b "leocrm_session=<session>" http://localhost:8000/api/v1/metrics
Available metrics:
leocrm_http_requests_total— Total HTTP requestsleocrm_http_request_duration_seconds— Request duration histogramleocrm_db_pool_connections— Database connection pool sizeleocrm_arq_jobs_total— Total ARQ background jobs
Structured Logging
LeoCRM uses structlog for structured JSON logging. All API requests are logged with:
timestamp, level, event, method, path, status, duration_ms, tenant_id.
Environment Profiles
| Profile | ENVIRONMENT |
Use Case |
|---|---|---|
| Development | development |
Local dev (auto-reload, verbose logging) |
| Testing | testing |
Test suite (separate test DB, minimal logging) |
| Production | production |
Docker/Coolify deployment (JSON logging, secure cookies) |
See docs/admin-guide.md for profile details.
Testing
# Run all tests
pytest -v --tb=short
# Run specific test suites
pytest tests/test_monitoring.py tests/test_performance.py tests/test_health.py -v
# Run with coverage
pytest --cov=app --cov-report=term-missing
Environment Variables
See .env.example for all variables and docs/admin-guide.md for detailed descriptions.
Key Variables
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
✅ | PostgreSQL async connection URL |
REDIS_URL |
✅ | Redis connection URL |
SECRET_KEY |
✅ | Secret key for signing (≥32 chars in prod) |
CORS_ORIGINS |
✅ | Comma-separated allowed origins (no wildcards) |
ENVIRONMENT |
❌ | development | testing | production |
STORAGE_PATH |
❌ | File storage path (default: /tmp) |
SMTP_HOST |
❌ | SMTP server hostname |
Performance Testing
# Seed 200k contacts for performance testing
python scripts/seed_perf_data.py --count 200000
# Verify database indexes
python scripts/check_indexes.py
# Test pagination performance
# GET /api/v1/contacts?page=1&page_size=25 — should be <500ms with 200k records
Project Structure
leocrm/
├── app/
│ ├── main.py # FastAPI entry point with logging middleware
│ ├── config.py # Pydantic settings
│ ├── core/
│ │ ├── monitoring.py # Prometheus metrics + structured logging + health checks
│ │ ├── db.py # Async database engine
│ │ ├── middleware.py # CSRF middleware
│ │ └── ...
│ ├── routes/
│ │ ├── health.py # Health endpoint
│ │ ├── metrics.py # Prometheus metrics endpoint (admin-only)
│ │ ├── contacts.py # Contact CRUD + streaming CSV export
│ │ ├── companies.py # Company CRUD + streaming CSV export
│ │ └── ...
│ ├── models/ # SQLAlchemy models
│ ├── schemas/ # Pydantic schemas
│ ├── services/ # Business logic
│ └── plugins/ # Plugin system
├── scripts/
│ ├── seed_perf_data.py # Performance test data seeding
│ └── check_indexes.py # Database index verification
├── tests/ # Test suite (pytest + pytest-asyncio)
├── docs/
│ ├── admin-guide.md # Admin guide (deploy, backup, restore, troubleshooting)
│ └── api-overview.md # API endpoint summary
├── alembic/ # Database migrations
├── requirements.txt # Production dependencies
├── requirements-dev.txt # Test/lint dependencies
├── .env.example # Environment template
├── docker-compose.yml # Docker Compose
└── README.md # This file
Documentation
- Admin Guide — Deployment, backup, restore, env vars, troubleshooting
- API Overview — Full endpoint reference
- Coolify Setup — Coolify deployment instructions
- Swagger UI — Interactive API docs (auto-generated)
License
Internal project – proprietary.