32 KiB
Agents Guide - URL Manager
This guide is designed to help AI coding agents understand and work with the URL Manager project effectively.
Project Overview
URL Manager (also known as "Heygo" / 黑狗) is a comprehensive link management system built with Django and available as both a web application and native iOS app. The system provides short link creation, bookmark management with automatic metadata extraction, template URLs with parameters, collections, and advanced search capabilities.
Tech Stack
- Backend: Django 5.1, Django REST Framework, APScheduler
- Frontend: Tailwind CSS, AlpineJS (minimal JS)
- Database: SQLite (default, configurable)
- Task Queue: APScheduler (in-process background scheduler)
- Storage: Local filesystem or AWS S3/Cloudflare R2
- Web Automation: Selenium with Chromium (for screenshots and scraping)
- Mobile: SwiftUI iOS app with Core Data
- Deployment: Kubernetes (k8s manifests included)
Architecture
Core Components
┌─────────────────────────────────────────────────────────────┐
│ Web Interface │
│ (Django Templates + Tailwind CSS) │
└──────────────────┬──────────────────────────────────────────┘
│
┌──────────────────┴──────────────────────────────────────────┐
│ Django Application │
│ ┌──────────────┬──────────────┬──────────────────────┐ │
│ │ Links Module │ Pages Module │ Collections Module │ │
│ │ │ │ (Images, Posts) │ │
│ └──────────────┴──────────────┴──────────────────────┘ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ REST API (DRF ViewSets) │ │
│ └────────────────────────────────────────────────────┘ │
└──────────────────┬──────────────────────────────────────────┘
│
┌──────────────────┴──────────────────────────────────────────┐
│ Background Tasks (APScheduler + Threading) │
│ - Screenshot capture (background threads) │
│ - Page metadata extraction (background threads) │
│ - Periodic task scheduling (APScheduler) │
└─────────────────────────────────────────────────────────────┘
│
┌──────────────────┴──────────────────────────────────────────┐
│ Storage Layer │
│ - Local filesystem (default) │
│ - Cloudflare R2 / AWS S3 (optional) │
└─────────────────────────────────────────────────────────────┘
Data Models
Core Models (links/models.py)
Link
The central model for URL management:
- alias: Unique slug identifier for the short link
- original_url: Target URL (supports templates with
{param,default=value}) - text: Markdown content (for custom pages)
- link_type: LINK (regular URL) or CUSTOM (markdown content)
- click_count: Usage analytics
- tags: Many-to-many relationship with Tag model
- description: Optional description
Template URL Feature: URLs can contain parameters like https://example.com/{query,default=test} that are resolved at access time.
Page
Bookmarked pages with auto-extracted metadata:
- url: Original page URL
- title: Auto-extracted or manual
- summary: Auto-extracted description
- content: Full page content (optional)
- screenshot: Associated Screenshot model
- tags: Many-to-many with Tag
- processing_status: PENDING, PROCESSING, COMPLETED, FAILED
- priority: For async processing queue
Tag
Hierarchical tagging system:
Bookmark
Archived social-media posts (originally x.com / Twitter bookmarks), each saved with full preview data so a viewer page renders offline without further x.com API calls:
- tweet_id: Source post id (unique, indexed)
- url: Canonical x.com post URL
- author_screen_name / author_name / author_profile_image_url: Denormalised author
- text: Full post text (searchable)
- summary: Optional human/agent-written summary (writable via API
PATCH) - tweet_created_at / bookmark_created_at: Parsed source dates (indexed)
- view_count / favorite_count / retweet_count / reply_count / bookmark_count / quote_count: Engagement counters
- has_video / has_photo / has_card / is_quote / is_retweet: Indexed boolean flags for fast filter pages
- media: JSON list of media objects (photo URL variants; video/animated_gif with
poster,best_mp4,duration_ms,variants[]) - card_data: JSON link-preview card (title/description/site/image/url)
- quoted_data: JSON nested quoted post (recursive shape)
- data: The complete original normalised export object (kept for round-tripping / the offline viewer)
- UI:
/ui/bookmarks/(list,BookmarkListView) +/ui/bookmarks/<pk>/(detail). Top-menu item "Bookmarks" intemplates/base.html. - Import:
python manage.py import_x_bookmarks <dir|file.json> [--clear](loadsx-bookmarks-exporterJSON) orPOST /api/bookmarks/import. - No
<video>streaming in the viewer — videos render as poster thumbnails linking to x.com (performance + ToS). - Search: bookmarks are indexed in the Whoosh full-text index (
search_backend.index_bookmark, rebuilt viapython manage.py rebuild_search_index), wired into signals (post_save/post_deleteonBookmarkauto-updates the index — e.g. when an agentPATCHes a summary). They appear in/ui/search/(React app, type filter "Bookmarks"),/search/api/v2/?type=bookmark, and the simple/search/api/?q=lookup. - name: Tag name
- slug: URL-friendly slug
- description: Optional description
- icon: Optional icon
- parent: Self-referential for hierarchy
- color: UI color code
ImageCollection & Image
Image gallery management:
- Collections group related images
- Support for Cloudflare R2 / S3 storage
- Automatic thumbnail generation
- Bulk upload capabilities
Post
Blog-like content management:
- Markdown content support
- Tag categorization
- Publication status tracking
mini_apps
A collection of mini apps which can be located at mini_apps_views.py
Analytics Models
- ClickLog: Individual click tracking with timestamps
- LinkChangeLog: Audit trail for modifications
API Architecture
REST API (Django REST Framework)
Located in various *_views.py files with corresponding *_urls.py:
Image API (api_views.py)
ImageCollectionViewSet: CRUD for image collections- Custom action:
upload_images- Bulk image upload with R2/S3 integration
- Custom action:
ImageViewSet: Image management with descriptionsMusicViewSet: Music file management
Page API (page_views.py)
PageViewSet: Full CRUD for bookmarked pagesscreenshotaction: Trigger screenshot captureextract_metadataaction: Re-extract page metadata
Bookmark API (bookmark_views.py → BookmarkViewSet, mounted at /api/bookmarks)
BookmarkViewSet: Full CRUD for archived social-media bookmarksGET /api/bookmarks: list with filters?q=,?has_video=true,?has_photo=,?has_card=,?is_quote=,?is_retweet=,?author=,?ordering=,?page_size=(default 24, max 200)POST /api/bookmarks: create one (flatBookmark-shaped body)GET/PATCH/DELETE /api/bookmarks/{id}: read / partial-update (e.g. write an AIsummary) / deletePOST /api/bookmarks/import: bulk upsert from{"bookmarks":[ {export obj}, ... ]}— accepts the fullx-bookmarks-exporternormalised object (nested author/media/card/quoted/retweet + x.com-stylecreated_atdate); matches bytweet_id. Returns{created, updated, skipped, total}PUT|POST /api/bookmarks/bulk: bulk upsert from a bare JSON array of flatBookmark-shaped objectsGET /api/bookmarks/stats:{total, has_video, has_photo, has_card, is_quote, is_retweet}(used by viewer filter chips)
Post API (post_views.py)
PostViewSet: Blog post management with markdown rendering
API Documentation
- OpenAPI 3.0.3 spec available at
/static/openapi.yaml(browse at http://localhost:8000/ui/api-docs/ via ReDoc) - Documents all endpoints, request/response schemas, and authentication
- The spec is the contract. Any task that touches an API must keep it in sync (see the Agent Workflow below)
File API (file_views.py → FileUploadViewSet, mounted at /api/files)
FileUploadViewSet: Full CRUD for generic file uploadscreate: Acceptsmultipart/form-datawith afilesfield (one or more files). Returns201with an array ofFileUploadobjectsdestroy: Deletes the record and removes the file from diskdownloadaction: Streams file content. Supports HTTPRangerequests — replies with206 Partial Content+Content-Range/Accept-Rangesheaders so<video>/<audio>can seek. Inline disposition for images, video, and audio;attachmentfor other typestoggle-publicaction: Flipsis_publicand returns{is_public, public_url}set-expiryaction: Sets or clears an ISO-8601 expiry datetime on the public link
- Public access lives at
/public/files/{id}-{filename}(filename is cosmetic; lookup is by UUID only). Internal-network requests serve any file; external requests only serveis_public=Truefiles that haven't expired. Range support applies here too.
Background Task System (APScheduler)
The application uses APScheduler for background task scheduling, running in-process with the Django application. This eliminates the need for separate worker processes and Redis broker.
Tasks (links/tasks.py)
process_page(page_id, retry_count=0)
Asynchronously fetches page metadata:
- Downloads page HTML
- Extracts title, description, meta tags
- Stores content in Page model
- Updates processing status
- Triggers screenshot capture
Features:
- Retry logic with exponential backoff (Fibonacci sequence)
- Timeout protection (20s)
- BeautifulSoup for HTML parsing
- Multiple metadata extraction strategies (title, h1, og:tags)
- Runs in background thread for non-blocking execution
capture_screenshot(page_id, screenshot_id, retry_count=0)
Captures website screenshots:
- Launches headless Chromium via Selenium
- Configures viewport and options
- Takes screenshot
- Uploads to storage (R2/S3 or local)
- Updates Screenshot model
Configuration:
- Headless mode
- Custom user agent
- 60s timeout
- Supports custom viewport sizes
- Error handling and retry logic via APScheduler
- Runs in background thread
schedule_pending_pages()
Periodic maintenance task:
- Runs every 120 seconds
- Checks for pages with PENDING status
- Schedules processing for pending pages
- Respects retry limits and backoff
APScheduler Configuration
# core/scheduler.py
scheduler = BackgroundScheduler(
executors={'default': ThreadPoolExecutor(20)},
job_defaults={
'coalesce': False,
'max_instances': 3,
'misfire_grace_time': 300
},
timezone=settings.TIME_ZONE
)
Initialization in core/apps.py:
- Scheduler starts automatically when Django application starts
- Periodic jobs registered in
CoreConfig.ready()method - No separate worker process needed
Task Execution
Tasks are executed using Python threading for immediate background execution:
from threading import Thread
thread = Thread(target=process_page, args=(page_id,))
thread.daemon = True
thread.start()
For scheduled/delayed tasks, APScheduler is used:
from core.scheduler import scheduler
from datetime import datetime, timedelta
run_date = datetime.now() + timedelta(seconds=delay)
scheduler.add_job(
capture_screenshot,
'date',
run_date=run_date,
args=[page_id, screenshot_id, retry_count + 1]
)
Key Features & Implementation
1. Template URL Processing
File: links/models.py - Link.get_template_parameters()
Extracts and processes URL parameters:
# URL: https://search.com/{query,default=test}&lang={lang,default=en}
# Renders to: https://search.com/python&lang=en (with query="python")
2. Asynchronous Page Processing
Files: links/tasks.py, links/page_views.py
When a page is bookmarked:
- Page record created with
processing_status=PENDING - Background thread started for metadata extraction
- Background worker fetches and parses page
- Title, description, content extracted
- Screenshot capture triggered in separate thread
- Status updated to COMPLETED
3. Search System
File: links/search_views.py
Multi-field search across:
- Link aliases, URLs, descriptions
- Page titles, content, summaries
- Tag names
- Full-text search with filtering
- Tag-based filtering
- Type-based filtering (links, pages, posts)
4. Storage Abstraction
File: links/storage.py - R2Storage
Unified interface for:
- Local filesystem storage
- Cloudflare R2 (S3-compatible)
- AWS S3
Environment variables:
R2_ENDPOINT_URLR2_ACCESS_KEY_IDR2_SECRET_ACCESS_KEYR2_BUCKET_NAME
5. i18n Support
Locales: English (en), Simplified Chinese (zh_Hans)
Translation files in locale/ directory. Uses Django's i18n framework.
Development Guide for AI Agents
Agent Workflow (always follow before coding)
Before writing or modifying any feature, check the existing contract first to avoid breaking it:
- Read the OpenAPI spec. Open
static/openapi.yaml(or browse /ui/api-docs/) and find the paths/schemas the task touches. The spec is the source of truth for endpoint shapes, status codes, and fields. Contrast it with what the request asks for — if they differ, ask the user whether the spec or the request is the intended behaviour. - Verify the implementation against the spec. Inspect the relevant
*_views.py,serializers.py, and model so the code you produce matches (or deliberately updates) the documented contract. Common drift: renamed fields, missing status codes (206,416for media ranges), wrongContent-Disposition, stale path templates (e.g.{token}/vs{id}-{filename}). - Keep the spec in sync. If the code change alters any API behaviour, update
static/openapi.yamlin the same change. Validate withpython -c "import yaml; yaml.safe_load(open('static/openapi.yaml'))". Regenerate or sanity-check any derived docs that reference the spec. - Rebuild Tailwind when you touch templates. New utility classes are only included after
npm run build:css(orjust tailwindin watch mode). Serving stale CSS is a frequent source of "works in one viewport, broken in another" bugs (e.g. duplicate listings, hidden sections). - Run the tests and linters. Use
just test(orpytest) for affected modules. Prefer an integration test that asserts the documented request → response shape, including status code and headers. Confirmblack/isortpass if available. - Don't leave duplicates. Whether it's duplicate DOM rendering (desktop/mobile forks that rely on responsive CSS), duplicate file records, or duplicate submission — each piece of state should have exactly one source of truth. Prefer a single render driven by JS data over copy-pasted server-rendered blocks.
Engineering Best Practices
- Single source of truth for state. Render lists from one data array; never duplicate the same dataset in desktop and mobile markup expecting CSS to hide one.
- APIs must return everything the client needs. If the UI updates dynamically after a mutation (no full reload), the response must include the fields the client renders (e.g.
formatted_size,mime_type,download_url,created_at). - Media must be seekable. Any endpoint serving
<video>/<audio>content must honour HTTPRangerequests and returnAccept-Ranges: bytes,206 Partial Content, andContent-Range. Reuse the_stream_file()helper inlinks/file_views.pyrather than rawFileResponse(open(...)). - Prefer AJAX over full-page reloads for incremental updates (uploads, deletes, toggles). It avoids re-rendering the whole page and prevents re-submission on refresh.
- Responsive design without forking markup. Use flex/grid +
hidden/blocktoggles on breakpoints instead of rendering a whole second table/card tree. - Accessibility. Buttons/links need visible focus rings, tap targets ≥ 44 px on mobile, ARIA labels for icon-only controls, and
sr-onlylabels for visually-hidden text. - Tests are mandatory for API changes. Add or update a test that pins the contract (status code, body, headers). Run
pytestbefore declaring done.
Common Tasks
Adding a New Model Field
- Update model in
links/models.py - Create migration:
python manage.py makemigrations - Apply migration:
python manage.py migrate - Update serializer in
links/serializers.py(if API exposed) - Update forms in
links/forms.py(if form-based) - Update templates in
links/templates/
Adding a New API Endpoint
- Add or update serializer in
links/serializers.py - Create ViewSet (or function view) in the appropriate
*_views.pyfile - Register the route in the matching
*_urls.py - Update
static/openapi.yaml— path, parameters, request schema, every response status code and headers, and any new component schema. Validate the YAML parses. - Write an integration test (
tests/) that asserts the documented request → response shape (status code, body fields, key headers). Add range-media coverage when serving media. - Verify a fresh
just devserver still returns200from/ui/api-docs/and the live endpoint matches the spec
Adding a Background Task
- Define task function in
links/tasks.py(no decorator needed) - For immediate execution, use threading:
from threading import Thread thread = Thread(target=task_function, args=(arg1,)) thread.daemon = True thread.start() - For delayed/scheduled execution, use APScheduler:
from core.scheduler import scheduler scheduler.add_job(task_function, 'date', run_date=run_time, args=[arg1]) - For periodic tasks, add to
core/apps.pyinCoreConfig.ready():scheduler.add_job( task_function, 'interval', seconds=120, id='task_id', replace_existing=True ) - Add logging for debugging
- Implement retry logic if needed
Adding a New Template View
- Create view function in
links/views.pyor create new view file - Add URL pattern to
links/urls.pyor appropriate urls file - Create template in
links/templates/ - Add i18n translation strings
- Update navigation if needed
Code Style Guidelines
- Python: Follow PEP 8, use Black formatter (line length: 100)
- Imports: Use isort with Black profile
- Type hints: Encouraged for new code
- Docstrings: Use for complex functions and classes
- Logging: Use Django's logging framework
import logging
logger = logging.getLogger(__name__)
logger.debug("Debug message")
logger.error("Error message", exc_info=True)
Testing
Framework: pytest with pytest-django
Run tests:
pytest
pytest links/tests/test_models.py
pytest -k "test_link_creation"
Database
Default: SQLite at data/db.sqlite3
Migrations managed with Django migrations. Always create migrations for model changes:
python manage.py makemigrations
python manage.py migrate
Static Assets
Tailwind CSS: Compiled via django-tailwind
Development:
python manage.py tailwind start # Watch mode
Production:
python manage.py tailwind build # Minified build
python manage.py collectstatic # Collect to staticfiles/
Docker Development
docker-compose.yml Services
- web: Django development server (port 8000) with APScheduler running in-process
- node: Tailwind CSS compiler
Note: Redis, Celery worker, and Celery beat services have been removed as they are no longer needed.
Helper Script: docker.sh
./docker.sh build # Build images
./docker.sh start # Start all services
./docker.sh stop # Stop all services
./docker.sh logs # View logs
./docker.sh migrate # Run migrations
./docker.sh shell # Django shell
Environment Variables (.env)
Create .env file:
DJANGO_SETTINGS_MODULE=core.settings
SECRET_KEY=your-secret-key
DEBUG=True
ALLOWED_HOSTS=*
# Optional: R2/S3 Storage
R2_ENDPOINT_URL=https://...
R2_ACCESS_KEY_ID=...
R2_SECRET_ACCESS_KEY=...
R2_BUCKET_NAME=...
iOS App (Heygo)
Architecture
- SwiftUI for declarative UI
- Core Data for local persistence
- MVVM pattern with Combine
- Charts framework for analytics
Syncing Strategy
Currently standalone (no backend sync). Future enhancement could:
- Use Django REST API
- Implement OAuth authentication
- Sync via background refresh
- Conflict resolution strategy
Key Files
mobile/Heygo/HeygoApp.swift: App entry pointmobile/Heygo/Views/: SwiftUI viewsmobile/Heygo/ViewModels/LinkViewModel.swift: Business logicmobile/Heygo/Models/: Core Data models
Deployment
Production Dockerfile
Multi-stage build:
- Builder stage: Install dependencies, compile static assets, build translations
- Production stage: Slim image with only runtime dependencies
Runtime requirements:
- Python 3.12
- Chromium + ChromeDriver (for screenshots)
Kubernetes
Manifests in k8s/manifest.yaml:
- Deployment for web service (includes APScheduler)
- Service for load balancing
- ConfigMap for configuration
- PersistentVolumeClaim for data
Note: Worker deployment is no longer needed as tasks run in-process.
Environment Setup
Production checklist:
- Set
DEBUG=False - Configure
SECRET_KEY(strong random value) - Set
ALLOWED_HOSTSto actual domains - Configure database (PostgreSQL recommended for production)
- Configure R2/S3 for media storage
- Set up SSL/TLS termination
- Configure backup strategy
- Set up monitoring and logging
Common Troubleshooting
Screenshots Not Generating
- Check Chromium installation:
which chromium - Check Django application logs for task errors
- Verify APScheduler is running (check startup logs)
- Review application logs for screenshot task errors
- Verify storage configuration (R2/S3 credentials)
Metadata Extraction Failing
- Check website accessibility (some sites block bots)
- Verify timeout settings (increase if needed)
- Check for JavaScript-heavy sites (may need Selenium instead of requests)
- Review error logs in application logs
Migration Issues
- Check for unapplied migrations:
python manage.py showmigrations - Look for migration conflicts
- Use
python manage.py migrate --fake-initialcautiously - For complex issues, may need to squash migrations
Performance Issues
- Add database indexes for frequently queried fields
- Implement caching (Django cache framework)
- Tune APScheduler thread pool size if needed (default: 20 threads)
- Use database connection pooling
- Enable query optimization (select_related, prefetch_related)
File Structure Guide
Core Django App Structure
core/
├── settings.py # Django settings, installed apps, middleware
├── urls.py # Main URL routing
├── scheduler.py # APScheduler configuration
├── apps.py # App configuration and scheduler initialization
└── middleware.py # Custom middleware (locale, etc.)
links/
├── models.py # Data models (Link, Page, Tag, etc.)
├── views.py # Main template views
├── api_views.py # REST API viewsets (Image, Music)
├── page_views.py # Page-specific views and API
├── post_views.py # Post/blog views and API
├── collection_views.py # Collection management
├── search_views.py # Search functionality
├── tag_views.py # Tag management
├── forms.py # Django forms
├── serializers.py # DRF serializers
├── tasks.py # Background task functions (APScheduler)
├── storage.py # Storage abstraction (R2/S3)
├── urls.py # Links app URL routing
├── *_urls.py # Feature-specific URL routing
└── templates/ # Django templates
new_theme/
├── static/ # Tailwind compiled CSS
├── static_src/ # Tailwind source files
└── templates/ # Theme-specific templates
templates/
├── base.html # Base template with navigation
└── base_blank.html # Minimal base template
Key Configuration Files
pyproject.toml: Python dependencies (managed by uv)uv.lock: Locked dependency versionspackage.json: Node.js dependencies (Tailwind)tailwind.config.js: Tailwind configurationdocker-compose.yml: Development environmentDockerfile: Production imageDockerfile.local: Development image
API Authentication (Currently no auth is enabled, don't need to consider)
Currently, the API may not have authentication enabled. To add:
- Add Django REST framework token authentication:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
]
}
- Generate tokens for users:
from rest_framework.authtoken.models import Token
token = Token.objects.create(user=user)
Performance Optimization Tips
Database Queries
# Use select_related for foreign keys
pages = Page.objects.select_related('screenshot').all()
# Use prefetch_related for many-to-many
links = Link.objects.prefetch_related('tags').all()
# Add indexes to models
class Page(models.Model):
url = models.URLField(db_index=True) # Add index
Caching
from django.core.cache import cache
# Cache expensive operations
result = cache.get('key')
if result is None:
result = expensive_operation()
cache.set('key', result, timeout=3600)
APScheduler Task Optimization
# Configure thread pool size in core/scheduler.py
executors = {
'default': ThreadPoolExecutor(20), # Adjust based on workload
}
# Add job with proper configuration
scheduler.add_job(
my_task,
'interval',
seconds=60,
max_instances=3, # Limit concurrent instances
id='unique_job_id',
replace_existing=True
)
# For one-time delayed tasks
from datetime import datetime, timedelta
run_date = datetime.now() + timedelta(seconds=300)
scheduler.add_job(my_task, 'date', run_date=run_date, args=[arg1])
Security Considerations
- CSRF Protection: Enabled by default, ensure templates use
{% csrf_token %} - SQL Injection: Use Django ORM (parameterized queries)
- XSS: Django auto-escapes templates, use
|safefilter cautiously - File Upload: Validate file types, scan for malware, limit sizes
- API Rate Limiting: Implement throttling with DRF throttle classes
- Secrets Management: Use environment variables, never commit secrets
Monitoring and Logging
Application Logging
# Configure in settings.py
LOGGING = {
'version': 1,
'handlers': {
'file': {
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': '/app/logs/django.log',
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'INFO',
},
},
}
APScheduler Monitoring
- Check scheduler status:
scheduler.runningreturns True/False - List all jobs:
scheduler.get_jobs() - Monitor task execution via Django logs
- Track task execution times in application logs
Health Checks
Implement health check endpoint:
# views.py
def health_check(request):
from core.scheduler import scheduler
# Check database
# Check APScheduler status
scheduler_running = scheduler.running
return JsonResponse({
'status': 'healthy',
'scheduler_running': scheduler_running
})
Useful Commands Reference
just (Command Runner)
The project uses just as its command runner. Run just (no args) to list all recipes.
# Local development
just dev # Run Django + Tailwind together
just tailwind # Run Tailwind CSS watcher (standalone)
just migrate # Apply pending migrations
just makemigrations # Create new migrations
just shell # Django shell
just dbshell # Raw DB shell
just superuser # Create a superuser
just build-css # Build minified Tailwind CSS
just collectstatic # Collect static files
just test # Run tests
just compilemessages # Compile i18n translations
just makemessages # Extract translatable strings (zh_Hans)
# Docker
just docker-build # Build images
just docker-start # Start services (detached)
just docker-stop # Stop services
just docker-logs # Tail all service logs
just docker-logs web # Tail a specific service's logs
just docker-shell # Shell into web container
just docker-migrate # Run migrations in Docker
just docker-static # Collect static in Docker
just docker-rebuild web # Rebuild + restart a service
Direct Django Management
When you need to run manage.py directly, activate the venv first:
source .venv/bin/activate
uv run manage.py <command>
APScheduler (In-Process)
APScheduler starts automatically with Django. To interact with it:
# In Django shell (just shell)
from core.scheduler import scheduler
scheduler.get_jobs() # List all scheduled jobs
scheduler.print_jobs() # Print job details
scheduler.running # Check if scheduler is running
UV Package Manager
uv add package_name # Add a new dependency
uv sync # Sync environment from pyproject.toml / uv.lock
Resources
- Django Documentation: https://docs.djangoproject.com/
- Django REST Framework: https://www.django-rest-framework.org/
- APScheduler Documentation: https://apscheduler.readthedocs.io/
- Tailwind CSS: https://tailwindcss.com/docs
- SwiftUI: https://developer.apple.com/documentation/swiftui/
- just: https://just.systems/man/en/
Note:
- No need to generate extra summary guide or docs, unless I ask you to.