Internal
Public Access
API.md now lists the supported custom RRULE patterns (every Wednesday, every other Wednesday, every second Tuesday, every 15th, etc.) matching the newly implemented dateutil.rrule evaluation. CLAUDE.md's Task model reference also gets corrected: it listed recurrence_type/recurrence_interval fields that never existed on the model. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
447 lines
16 KiB
Markdown
447 lines
16 KiB
Markdown
# 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, biweekly, monthly, yearly, or custom RRULE for day-of-week/nth-weekday/day-of-month control)
|
|
- 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` (none/daily/weekly/biweekly/monthly/yearly/custom), `recurrence_rule` (RRULE string, used when `recurrence='custom'`, parsed via `dateutil.rrule`), `recurrence_end_date`
|
|
- 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/<id>/` - Task detail operations
|
|
- `POST /api/tasks/<id>/start-timer/` - Start time tracking
|
|
- `POST /api/tasks/time-entries/<id>/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/<id>/resolve/` - Resolve conflict
|
|
|
|
**Notifications:**
|
|
- `GET /api/notifications/` - List notifications
|
|
- `POST /api/notifications/<id>/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, which calls `calculate_next_due_date()`. For `recurrence='custom'`, the next date is computed by evaluating `recurrence_rule` (an RRULE string) via `dateutil.rrule`; the web UI's Custom recurrence builder (in `templates/tasks/_recurrence_fields.html` and `static/js/app.js`) generates these strings for day-of-week, nth-weekday, and day-of-month patterns. Celery task `process_recurring_tasks()` runs as safety net.
|
|
|
|
**Time Entry Workflow:**
|
|
1. `POST /api/tasks/<id>/start-timer/` creates TimeEntry with start_time
|
|
2. Running timer tracked in UI
|
|
3. `POST /api/time-entries/<id>/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
|
|
```
|