diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..2860536 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,446 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Development Commands + +### Local Development (Non-Docker) + +```bash +# Set environment for development +export DJANGO_SETTINGS_MODULE=config.settings.development + +# Create and activate virtual environment +python3 -m venv venv +source venv/bin/activate # On Windows: venv\Scripts\activate + +# Install dependencies +pip install -r requirements.txt + +# Database operations +python manage.py migrate +python manage.py makemigrations +python manage.py createsuperuser + +# Run development server +python manage.py runserver + +# Run tests +python manage.py test + +# Django shell +python manage.py shell + +# Collect static files (for production) +python manage.py collectstatic --noinput +``` + +### Docker Development + +```bash +# Development with live reload +make dev +make dev-build # Rebuild containers + +# Production mode +make prod +make build # Build production containers +make deploy # Full deployment (pull, build, migrate, restart) + +# Database operations +make migrate +make makemigrations +make createsuperuser +make dbshell # Access database shell +make backup # Backup database + +# Monitoring +make logs # All container logs +make logs-web # Web container logs only +make logs-celery # Celery worker logs +make status # Container status + +# Control +make stop +make restart +make clean # Stop and remove all containers/volumes + +# Shell access +make shell # Django shell +make test # Run tests in container +``` + +### Celery (Background Tasks) + +```bash +# Local development (non-Docker) +# Terminal 1: Run Celery worker +celery -A config worker -l info + +# Terminal 2: Run Celery beat (scheduled tasks) +celery -A config beat -l info + +# In Docker, Celery runs automatically as separate services +``` + +## High-Level Architecture + +### Django Apps + +**users/** - User authentication and management +- Custom User model with email-based authentication (no username) +- Three-tier verification: email verification → admin approval → login +- JWT token authentication with refresh tokens +- Device token management for push notifications +- Rate-limited registration (5/hour) and login (10/hour) + +**tasks/** - Core task management +- Hierarchical tasks with parent/subtask relationships +- Task status (pending, in_progress, completed, cancelled) and priority (low, medium, high, urgent) +- Recurrence patterns (daily, weekly, monthly, yearly, custom RRULE) +- Time tracking with start/stop timer functionality +- Tag system with colors and icons for organization +- Task sharing with permission levels (viewer/editor) +- Soft-delete architecture (is_deleted flag) for sync compatibility + +**sync/** - Mobile app synchronization +- Offline-first bidirectional sync protocol +- Timestamp-based conflict detection with three resolution strategies (local, server, merged) +- Multi-device support with independent sync tokens per device +- Incremental sync (delta updates) and full sync support +- Uses sync_id (UUID) separate from Django id for mobile compatibility +- Soft-delete propagation to enable client-side deletion + +**notifications/** - Email notifications and reminders +- Daily email digest (overdue + due today tasks) +- Timezone-aware scheduling (6-7 AM in user's local time) +- Deduplication (one email per user per day) +- Celery-based background processing +- Recurring task safety net (creates missed recurrences) + +### Key Architectural Patterns + +**UUID Primary Keys** - All models use UUID as primary key for distributed system compatibility and security. + +**Dual ID System** - Each entity has both `id` (Django PK) and `sync_id` (for mobile sync). Sync protocol uses `sync_id` to maintain consistency across devices. + +**Soft Delete Pattern** - All entities have `is_deleted` boolean instead of hard deletes. Dual managers enable filtering: +- `objects` - Default manager (excludes deleted) +- `all_objects` - Includes deleted items (used in sync) + +**Multiple Serializers Per Context** - Different serializers for list/detail/sync endpoints to optimize performance: +- `TaskListSerializer` - Lightweight for list views +- `TaskSerializer` - Full details with nested subtasks +- `TaskSyncSerializer` - Uses sync_id, includes is_deleted + +**Permission System** - Custom `IsOwnerOrReadOnlyIfShared` permission: +- Read access: Owner OR users with shared access +- Write access: Owner only +- Security filtering in serializers prevents tag/task leakage + +**Custom Authentication Backend** - `EmailVerifiedApprovedBackend` enforces: +1. Email verification check +2. Admin approval check +3. Superusers bypass both checks +4. Stores error state in session for web UI + +**Timezone-Aware Operations** - All time calculations use user's timezone: +- Daily emails scheduled in user's local 6-7 AM +- Overdue calculation relative to user's local date +- UTC storage, converted for display and logic + +**Mobile App Support** - `AllowMobileAppFramingMiddleware` detects mobile app via User-Agent and removes X-Frame-Options to allow WebView embedding while maintaining security for web browsers. + +### Sync Protocol Details + +The sync endpoint (`POST /api/sync/`) handles offline-first synchronization: + +**Request Format:** +```json +{ + "device_id": "unique-device-id", + "last_sync_token": "previous-token-or-null", + "changes": { + "tasks": [...], + "tags": [...], + "time_entries": [...] + } +} +``` + +**Response Format:** +```json +{ + "sync_token": "new-sync-token", + "server_time": "2025-01-10T12:00:00Z", + "server_changes": { + "tasks": [...], + "tags": [...], + "time_entries": [...] + }, + "conflicts": [...] +} +``` + +**Conflict Detection:** Compares `updated_at` timestamp between client's last sync and server's current state. If server modified entity since last sync, conflict is flagged. + +**Conflict Resolution Strategies:** +- **local** - Apply client changes (overwrite server) +- **server** - Keep server version (discard client) +- **merged** - Accept manually merged data + +### Celery Task Schedule + +Configured in `config/celery.py`: + +**send_daily_task_email** - Runs every hour +- Fetches users with email notifications enabled +- Converts UTC to user's timezone +- Sends email only if user's local time is 6-7 AM +- Checks for existing notification to prevent duplicates +- Groups tasks: overdue + due today (pending/in_progress only) + +**process_recurring_tasks** - Runs daily at midnight +- Safety net for recurring task creation +- Processes completions from last 48 hours +- Creates next recurrence if missing +- Prevents duplicate recurrences + +### Settings Modules + +**config.settings.development** - Local development +- DEBUG=True +- SQLite database +- Console email backend +- Minimal security + +**config.settings.production** - Production deployment +- DEBUG=False +- PostgreSQL required +- SMTP email backend +- Full security (HTTPS, CSP, HSTS) +- Gunicorn with multiple workers + +**config.settings.selfhosted** - Docker/self-hosted +- Environment-based configuration +- PostgreSQL with health checks +- Redis for Celery +- Configurable workers and timeout +- WhiteNoise for static files + +### Database Models Reference + +**Task Model Fields:** +- Hierarchy: `parent` (ForeignKey to self) +- Status: pending, in_progress, completed, cancelled +- Priority: low, medium, high, urgent +- Recurrence: `recurrence_type`, `recurrence_interval`, `recurrence_rule` (RRULE) +- Timing: `due_date`, `due_time`, `completed_at`, `created_at`, `updated_at` +- Soft delete: `is_deleted` (filters via `SoftDeleteManager`) +- Sync: `sync_id` (UUID for mobile sync) +- Relationships: `tags` (ManyToMany), `time_entries` (reverse FK) + +**User Model Fields:** +- Authentication: `email` (unique), `password`, `first_name`, `last_name` +- Verification: `email_verified`, `is_approved`, `approved_by` +- Preferences: `timezone`, `reminder_minutes_before`, `notifications_enabled`, `email_notifications_enabled` +- UUID primary key for security + +**TimeEntry Model:** +- `start_time`, `end_time`, `duration` (auto-calculated) +- Foreign keys: `task`, `user` +- Soft delete: `is_deleted` + +**TaskShare Model:** +- `shared_by`, `shared_with` (User FKs) +- `permission_level`: viewer, editor +- Can share tasks or tags +- Generic relation pattern + +### API Endpoint Overview + +**Authentication:** +- `POST /api/users/register/` - Create account (rate limited 5/hour) +- `POST /api/users/token/` - Login (rate limited 10/hour) +- `POST /api/users/token/refresh/` - Refresh JWT +- `POST /api/users/verify-email/` - Verify email token +- `POST /api/users/resend-verification/` - Resend verification email + +**Tasks:** +- `GET/POST /api/tasks/` - List/create tasks (supports filtering by status, priority, tags, search) +- `GET/PUT/DELETE /api/tasks//` - Task detail operations +- `POST /api/tasks//start-timer/` - Start time tracking +- `POST /api/tasks/time-entries//stop/` - Stop timer +- `GET/POST /api/tasks/tags/` - Tag management +- `GET/POST /api/tasks/shares/` - Share management + +**Sync:** +- `POST /api/sync/` - Main sync endpoint (rate limited 100/hour) +- `GET /api/sync/conflicts/` - List pending conflicts +- `POST /api/sync/conflicts//resolve/` - Resolve conflict + +**Notifications:** +- `GET /api/notifications/` - List notifications +- `POST /api/notifications//read/` - Mark as read +- `POST /api/notifications/mark-all-read/` - Mark all read +- `GET /api/notifications/unread-count/` - Get unread count + +### Environment Variables + +Key variables (see `.env.example` and `stack.env.example`): + +**Django Core:** +- `SECRET_KEY` - Django secret (generate with `python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"`) +- `DEBUG` - Debug mode (False in production) +- `ALLOWED_HOSTS` - Comma-separated hosts +- `CSRF_TRUSTED_ORIGINS` - HTTPS origins +- `SITE_DOMAIN` - Domain for email links + +**Database:** +- `DATABASE_URL` - Connection string (e.g., `postgresql://user:pass@host:5432/db`) + +**Redis & Celery:** +- `REDIS_URL` - Redis connection (e.g., `redis://localhost:6379/0`) + +**Email:** +- `EMAIL_HOST`, `EMAIL_PORT`, `EMAIL_USE_TLS` +- `EMAIL_HOST_USER`, `EMAIL_HOST_PASSWORD` +- `DEFAULT_FROM_EMAIL` - Sender address +- `EMAIL_VERIFICATION_TOKEN_EXPIRY_HOURS` - Token validity (default: 24) + +**Docker/Gunicorn:** +- `GUNICORN_WORKERS` - Worker processes (default: 3) +- `GUNICORN_TIMEOUT` - Request timeout (default: 60) +- `WEB_PORT` - External port (default: 8000) + +### Testing + +Tests located in each app's `tests.py`. Run with: +```bash +python manage.py test # All tests +python manage.py test users # Specific app +python manage.py test users.tests.TestUserModel # Specific test +``` + +Docker: +```bash +make test # Run tests in container +``` + +### Deployment Notes + +**Docker Deployment:** +- Production uses PostgreSQL (postgres:16-alpine) +- Redis for Celery message broker (redis:7-alpine) +- Web service runs Gunicorn with configurable workers +- Celery worker and beat run as separate services +- Health checks on all services +- Named volumes for persistence (db_data, redis_data, static_files) +- Networks: frontend (web-facing), backend (internal services) + +**Manual Deployment:** +- Use `config.settings.production` settings module +- Requires PostgreSQL (not SQLite) +- Configure SMTP for email +- Use Gunicorn behind Nginx reverse proxy +- Set up systemd services for Gunicorn, Celery worker, Celery beat +- Configure SSL/TLS certificates (Let's Encrypt recommended) +- Enable firewall (UFW) with SSH, HTTP, HTTPS only + +**Production Checklist:** +- Set DEBUG=False +- Use strong SECRET_KEY +- Configure ALLOWED_HOSTS (no wildcards) +- Set CSRF_TRUSTED_ORIGINS for HTTPS +- Use PostgreSQL (not SQLite) +- Configure email backend (SMTP) +- Enable security headers (in settings) +- Set up SSL/TLS certificates +- Configure database backups +- Set up log rotation +- Monitor error logs and uptime + +### Common Development Patterns + +**Creating Recurring Tasks:** When a recurring task is marked complete, the next recurrence is automatically created in `tasks/models.py` `create_next_recurrence()` method. Celery task `process_recurring_tasks()` runs as safety net. + +**Time Entry Workflow:** +1. `POST /api/tasks//start-timer/` creates TimeEntry with start_time +2. Running timer tracked in UI +3. `POST /api/time-entries//stop/` sets end_time and calculates duration + +**Email Verification Flow:** +1. User registers → `email_verified=False`, `is_approved=False` +2. EmailVerificationToken created with 24-hour expiry +3. User clicks link → `email_verified=True`, token marked used +4. Admins notified via email +5. Admin approves in Django admin → `is_approved=True` +6. User notified and can now login + +**Adding New Celery Tasks:** +1. Create task function in app's `tasks.py` with `@shared_task` decorator +2. Add to beat schedule in `config/celery.py` if periodic +3. Import in app's `__init__.py` to ensure task registration +4. Restart Celery worker and beat services + +**Extending User Model:** The User model is in `users/models.py`. Add fields there, create migration, update serializers and admin if needed. + +**Security Filtering:** When adding new endpoints that query related objects, filter by user in viewset's `get_queryset()`. Example from TaskViewSet: +```python +def get_queryset(self): + return Task.objects.filter(user=self.request.user) +``` + +### File Structure Reference + +``` +config/ # Django project configuration + settings/ # Environment-specific settings + base.py # Shared settings + development.py # Local development + production.py # Production deployment + selfhosted.py # Docker/self-hosted + celery.py # Celery configuration + urls.py # Root URL configuration + wsgi.py # WSGI entry point + +users/ # User management app + models.py # User, DeviceToken, EmailVerificationToken + views.py # Registration, login, profile + serializers.py # User serializers + backends.py # EmailVerifiedApprovedBackend + utils.py # Email utilities + +tasks/ # Task management app + models.py # Task, Tag, TimeEntry, TaskShare + views.py # Task CRUD, filtering, timers + serializers.py # Multiple serializers per context + middleware.py # AllowMobileAppFramingMiddleware + permissions.py # IsOwnerOrReadOnlyIfShared + +sync/ # Mobile sync app + models.py # SyncLog, SyncConflict + views.py # Sync endpoint, conflict resolution + serializers.py # SyncSerializer with sync_id + +notifications/ # Notifications app + models.py # Notification, ScheduledReminder + views.py # Notification CRUD + tasks.py # Celery tasks (daily email, recurring tasks) + +templates/ # Django templates + base.html # Base template with dark mode + tasks/ # Task-related templates + users/ # User-related templates + +static/ # Static assets + css/ # Stylesheets + js/ # JavaScript + +docker/ # Docker-related files +docker-compose.yml # Production Docker setup +docker-compose.dev.yml # Development overrides +Dockerfile # Production image +Dockerfile.dev # Development image +Makefile # Docker command shortcuts +```