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
264 lines
8.5 KiB
Markdown
264 lines
8.5 KiB
Markdown
# 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
|
||
|
||
### Architecture
|
||
```
|
||
app/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
|
||
```
|
||
|
||
## Build & Run Commands
|
||
|
||
```bash
|
||
# Development
|
||
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)
|
||
- UUIDs as string primary keys: `str(uuid.uuid4())`
|
||
- 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`
|
||
- Check pattern: `config.AUTHELIA_ENABLED and user.password_hash is None`
|
||
|
||
---
|
||
|
||
## Known Issues & Technical Debt
|
||
|
||
### Critical
|
||
|
||
1. **Inactive Notification System**
|
||
- Location: [services/notifications.py](services/notifications.py)
|
||
- Issue: All code implemented but no scheduler integrated
|
||
- TODO: Integrate APScheduler or similar
|
||
|
||
2. **Default SECRET_KEY**
|
||
- Location: [app/config.py:13](app/config.py#L13)
|
||
- Issue: Defaults to "change-me-in-production"
|
||
- Fix: Add startup validation to error if default key used
|
||
|
||
3. **No CSRF Protection**
|
||
- Forms use token auth only, vulnerable to CSRF attacks
|
||
- Fix: Implement CSRF tokens or validate referer header
|
||
|
||
4. **No Rate Limiting**
|
||
- Login endpoint has no brute force protection
|
||
- Fix: Add slowapi or similar middleware
|
||
|
||
### Performance
|
||
|
||
1. **N+1 Query Problems**
|
||
- Location: [app/routes/managers.py:244-259](app/routes/managers.py#L244-L259)
|
||
- Location: [app/routes/presence.py:336-419](app/routes/presence.py#L336-L419)
|
||
- Issue: Loops that query database for each item
|
||
- Fix: Use joins and relationship loading
|
||
|
||
2. **Inefficient Spot Prefix Lookups**
|
||
- Location: [services/parking.py:56-64](services/parking.py#L56-L64)
|
||
- Issue: Repeated DB queries for same data
|
||
- Fix: Cache in request context
|
||
|
||
### Code Quality
|
||
|
||
1. **Duplicated LDAP Check Logic** (4+ locations)
|
||
```python
|
||
# Appears in: users.py:91, 168, 257, 280
|
||
is_ldap_user = config.AUTHELIA_ENABLED and user.password_hash is None
|
||
```
|
||
- Fix: Create `utils.is_ldap_user(user)` helper
|
||
|
||
2. **Inline JavaScript in HTML**
|
||
- 500+ lines embedded across pages
|
||
- Affected: team-rules.html, team-calendar.html, settings.html
|
||
- Fix: Extract to separate JS files
|
||
|
||
3. **Inconsistent Response Formats**
|
||
- Some endpoints return dicts, others Pydantic models
|
||
- Fix: Standardize on Pydantic response schemas
|
||
|
||
4. **God Object: User Model**
|
||
- Location: [database/models.py:11-47](database/models.py#L11-L47)
|
||
- Issue: 27 columns mixing auth, profile, preferences, manager settings
|
||
- Fix: Normalize into UserProfile, UserPreferences, ManagerSettings tables
|
||
|
||
5. **Repetitive CRUD in managers.py**
|
||
- Location: [app/routes/managers.py](app/routes/managers.py)
|
||
- Issue: 4 resources × 3 operations with near-identical code
|
||
- Fix: Create generic CRUD factory or base class
|
||
|
||
6. **Silent Exception Handling**
|
||
- Location: [app/routes/presence.py:135-143](app/routes/presence.py#L135-L143)
|
||
- Issue: Catches all exceptions and only prints
|
||
- Fix: Log properly and propagate meaningful errors
|
||
|
||
---
|
||
|
||
## Areas for Simplification
|
||
|
||
### 1. Consolidate Response Building
|
||
The `user_to_response()` pattern is duplicated in [users.py:76-107](app/routes/users.py#L76-L107) and [users.py:254-274](app/routes/users.py#L254-L274). Create single reusable function.
|
||
|
||
### 2. Generic CRUD Router Factory
|
||
[managers.py](app/routes/managers.py) has 12 nearly identical endpoints. Create:
|
||
```python
|
||
def create_crud_router(model: Type, schema: Type, parent_key: str):
|
||
router = APIRouter()
|
||
# Generate GET, POST, DELETE endpoints
|
||
return router
|
||
```
|
||
|
||
### 3. Frontend State Management
|
||
Each page maintains its own globals (`currentUser`, `currentData`). Consider simple app-level state or Web Components.
|
||
|
||
### 4. Date Handling
|
||
All dates stored as TEXT strings. Could use proper DATE columns for better query performance and validation.
|
||
|
||
### 5. Notification Service Activation
|
||
Create [services/scheduler.py](services/scheduler.py) with APScheduler:
|
||
```python
|
||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||
|
||
scheduler = AsyncIOScheduler()
|
||
scheduler.add_job(process_notification_queue, 'interval', minutes=5)
|
||
```
|
||
|
||
---
|
||
|
||
## Security Improvements Needed
|
||
|
||
| Priority | Issue | Location | Recommendation |
|
||
|----------|-------|----------|----------------|
|
||
| HIGH | Rate limiting | main.py | Add slowapi middleware |
|
||
| HIGH | CSRF protection | All forms | Implement CSRF tokens |
|
||
| HIGH | Secret validation | config.py:13 | Error on default key |
|
||
| MEDIUM | Password validation | auth.py:63-67 | Enforce complexity rules |
|
||
| MEDIUM | Input sanitization | notification emails | Use template library |
|
||
| LOW | CORS configuration | compose.yml | Document production settings |
|
||
|
||
---
|
||
|
||
## Testing Strategy (Missing)
|
||
|
||
No tests currently exist. Recommended structure:
|
||
```
|
||
tests/
|
||
├── conftest.py # Fixtures (test DB, client, auth)
|
||
├── test_auth.py # JWT and Authelia modes
|
||
├── test_parking.py # Fair assignment algorithm
|
||
├── test_presence.py # Bulk operations, team calendar
|
||
├── test_managers.py # CRUD operations
|
||
└── integration/
|
||
└── test_workflows.py # End-to-end scenarios
|
||
```
|
||
|
||
Key test scenarios:
|
||
1. Parking algorithm with varying ratios
|
||
2. Manager closing days affect assignments
|
||
3. Guarantee and exclusion rules
|
||
4. Authelia header authentication flow
|
||
5. LDAP vs local user password handling
|
||
|
||
---
|
||
|
||
## API Quick Reference
|
||
|
||
### Authentication
|
||
- `POST /api/auth/register` - Create user (standalone mode)
|
||
- `POST /api/auth/login` - Get JWT token
|
||
- `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)`
|
||
|
||
### Database Migrations
|
||
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
|
||
|
||
### Frontend Page Pattern
|
||
```html
|
||
<script type="module">
|
||
import api from '/js/api.js';
|
||
import { initNav } from '/js/nav.js';
|
||
|
||
document.addEventListener('DOMContentLoaded', async () => {
|
||
await api.checkAuth(true); // Redirect to login if not auth'd
|
||
initNav();
|
||
// Page-specific logic
|
||
});
|
||
</script>
|
||
```
|
||
|
||
---
|
||
|
||
## 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) |
|
||
| Auth middleware | [utils/auth_middleware.py](utils/auth_middleware.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) |
|