# CRM System v1.0 > Self-hosted CRM for small sales teams (5–25 sales reps). > Stack: FastAPI + SQLAlchemy (async) + Alembic + Pydantic v2 + SQLite/PostgreSQL + Alpine.js + Tailwind + Docker + Coolify ## Quick Start (Development) ### 1. Clone and Setup ```bash git clone crm-system cd crm-system # 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 ```bash # Copy template cp .env.example .env # Generate a secure AUTH_SECRET (min 32 chars) python3 -c "import secrets; print('AUTH_SECRET=' + secrets.token_urlsafe(48))" >> .env # Edit .env and set AUTH_SECRET (remove the placeholder line first) ``` ### 3. Initialize Database ```bash # Apply migrations alembic upgrade head # (Optional) Create migration after model changes # alembic revision --autogenerate -m "description" ``` ### 4. Run Server ```bash # Development with auto-reload uvicorn app.main:app --reload --port 8000 # Production-like uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 2 ``` Open: - API: http://localhost:8000 - Swagger UI: http://localhost:8000/docs - ReDoc: http://localhost:8000/redoc - Health: http://localhost:8000/health ### 5. Bootstrap First User ```bash curl -X POST http://localhost:8000/api/v1/auth/register \ -H "Content-Type: application/json" \ -d '{ "email": "admin@example.com", "password": "secure-password-123", "name": "First Admin" }' ``` This creates the first user + a default org. After that, registration is disabled (use admin invite flow in v1.1). ## Project Structure ``` crm-system/ ├── app/ # Application package │ ├── main.py # FastAPI entry point │ ├── core/ # Core modules (config, db, security, deps) │ ├── models/ # SQLAlchemy models │ ├── schemas/ # Pydantic schemas (request/response) │ ├── services/ # Business logic layer │ ├── api/v1/ # API routers (versioned) │ └── webui/ # Static frontend (Phase 4c) ├── alembic/ # Database migrations │ ├── env.py # Async migration environment │ └── versions/ # Migration scripts ├── tests/ # Test suite (pytest + pytest-asyncio) ├── requirements.txt # Production dependencies ├── requirements-dev.txt # Test/lint dependencies ├── pyproject.toml # Tool configuration ├── alembic.ini # Alembic configuration ├── .env.example # Environment template └── README.md ``` ## Testing ```bash # Run all tests pytest -v --tb=short # Run with coverage pytest --cov=app --cov-report=term-missing # Run specific test file pytest tests/test_auth.py -v # Stop on first failure (for debugging) pytest -x ``` ## Environment Variables | Variable | Required | Default | Description | |---|---|---|---| | `AUTH_SECRET` | ✅ | – | JWT signing secret (≥32 chars). Hard-fail if missing. | | `DATABASE_URL` | ❌ | `sqlite+aiosqlite:///./dev.db` | Async DB URL (aiosqlite or asyncpg) | | `JWT_ALGORITHM` | ❌ | `HS256` | JWT algorithm | | `JWT_EXPIRY_HOURS` | ❌ | `24` | Token lifetime | | `BCRYPT_ROUNDS` | ❌ | `12` | Password hashing cost | | `CORS_ORIGINS` | ❌ | `http://localhost:5500,http://localhost:8000` | Allowed origins (comma-separated, NO wildcards) | | `ENVIRONMENT` | ❌ | `development` | `development` or `production` | | `LOG_LEVEL` | ❌ | `INFO` | Python log level | ## Architecture Decisions (ADR) - **JWT Library**: `python-jose[cryptography]==3.3.0` (pattern reuse from wochenplaner) - **Database**: SQLite (aiosqlite) for dev, PostgreSQL (asyncpg) for prod - **Auth**: Stateless JWT in localStorage, bcrypt password hashing (12 rounds) - **Security**: CORS whitelist (no wildcard), CSP middleware, no default admin user - **Async**: All routers/services/DB operations are async (SQLAlchemy 2.0 + aiosqlite) See `/a0/.a0/02-architecture.md` Section 13 for full lockdown decisions. ## Deployment See `/a0/.a0/03-task-graph.json` Phase 4d for Docker + Coolify setup (out of scope for Phase 4a). ## License Internal project – proprietary.