No description
  • Python 59.9%
  • HTML 39.4%
  • CSS 0.5%
  • Makefile 0.1%
  • Shell 0.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-06-01 06:27:05 +01:00
.claude initial 2025-12-29 15:21:32 +00:00
conductor conductor(setup): Add conductor setup files and initial track 2026-01-12 08:46:37 +00:00
conductor-pi Archive the move shopping list item conductor files 2026-01-25 17:22:16 +00:00
core wip: add API throttling and secure docs exposure controls 2026-05-31 21:23:18 +01:00
deploy More hardening - this should be it 2026-05-31 22:00:48 +01:00
docs docs: add post-launch checks and incident playbook 2026-05-31 22:11:04 +01:00
mealplanner More hardening - this should be it 2026-05-31 22:00:48 +01:00
scripts chore: Ignore generated styles.css and add a deployment step to force discard local changes to it. 2026-01-05 16:57:43 +00:00
templates feat: Enhance Shopping List Mobile UX and Layout 2026-02-01 21:20:50 +00:00
.gitignore chore: Ignore generated styles.css and add a deployment step to force discard local changes to it. 2026-01-05 16:57:43 +00:00
.python-version initial 2025-12-29 15:21:32 +00:00
AGENTS.md docs: refactor AGENTS.md with progressive disclosure structure 2026-01-30 14:20:39 +00:00
Makefile feat: Migrate to local Tailwind CSS compilation by removing CDN and tailwind.config.js, and adding build automation and documentation. 2026-01-05 11:48:52 +00:00
manage.py initial 2025-12-29 15:21:32 +00:00
mealplanner-spec.md initial 2025-12-29 15:21:32 +00:00
plan.md chore: complete shopping list clear refinement 2026-01-25 14:35:44 +00:00
pyproject.toml API in place - not fully tested 2026-05-31 20:57:03 +01:00
README.md Updates README with refresh token flow 2026-06-01 06:27:05 +01:00
TODO.md Nearly completed the toast work 2026-01-26 09:36:15 +00:00
uv.lock API in place - not fully tested 2026-05-31 20:57:03 +01:00

Meal Planner

A family meal planning application built with Django, HTMX, and Tailwind CSS.

Local Development

Prerequisites

  • uv (Python package manager)
  • Tailwind CLI binary: Download via:
    curl -sLO https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64 && chmod +x tailwindcss-linux-x64 && mv tailwindcss-linux-x64 tailwindcss
    
    (Note: Adjust URL for non-Linux platforms)

Running the Application

  1. Install dependencies:

    uv sync
    
  2. Database setup:

    uv run manage.py migrate
    uv run manage.py seed_data
    
  3. Start the development server:

    uv run manage.py runserver
    
  4. Watch for CSS changes (in a separate terminal):

    make css-watch
    

CSS Build Commands

  • make css: Performs a one-time minified build of styles.css.
  • make css-watch: Watches input.css and all templates for changes and rebuilds CSS automatically.

API Access

The Meal Planner includes a RESTful API with comprehensive endpoints for all models.

Authentication

The API supports both session-based and token-based (JWT) authentication:

Session Authentication (Web Browsers)

# Login to establish session
curl -X POST http://localhost:8000/accounts/login/ \
  -d "username=youruser&password=yourpass" \
  --cookie-jar cookies.txt

# Use session cookie for API requests
curl http://localhost:8000/api/v1/recipes/ \
  --cookie cookies.txt

Token Authentication (Mobile Apps, CLI, Third-Party Services)

Important: Always include -H "Accept: application/json" to get JSON responses instead of HTML.

# Get JWT tokens (note: Accept header is required for JSON response)
curl -s -X POST http://localhost:8000/api/v1/token/ \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"username":"youruser","password":"yourpass"}'

# Response includes access and refresh tokens:
# {
#   "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
#   "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
# }

# Use access token for API requests
curl -s http://localhost:8000/api/v1/recipes/ \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Accept: application/json"

# Refresh access token when expired
curl -s -X POST http://localhost:8000/api/v1/token/refresh/ \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"refresh":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}'

CLI Usage Examples (Bash)

For convenient CLI usage, save the token in a variable and reuse it:

# Login and save token to variable
TOKEN=$(curl -s -X POST http://localhost:8000/api/v1/token/ \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"username":"matt","password":"euShuuZog1ahfaiVa2"}' | jq -r '.access')

# Now use $TOKEN for subsequent requests
curl -s http://localhost:8000/api/v1/recipes/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json"

# List all recipes with pretty printing
curl -s http://localhost:8000/api/v1/recipes/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json" | jq '.'

# Create a new recipe
curl -s -X POST http://localhost:8000/api/v1/recipes/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "name": "My New Recipe",
    "meal_type": 1,
    "difficulty": 2,
    "instructions": "Step 1: Do this. Step 2: Do that."
  }'

# Get a specific recipe
curl -s http://localhost:8000/api/v1/recipes/1/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json" | jq '.'

# Update a recipe
curl -s -X PUT http://localhost:8000/api/v1/recipes/1/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"name": "Updated Recipe Name"}'

# Delete a recipe
curl -s -X DELETE http://localhost:8000/api/v1/recipes/1/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json"

Week Plan Examples

# Assuming $TOKEN is set from login above

# List all week plans
curl -s http://localhost:8000/api/v1/week-plans/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json" | jq '.'

# Create a new week plan
curl -s -X POST http://localhost:8000/api/v1/week-plans/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"start_date": "2026-06-01"}'

# Shuffle meals for a week plan (custom action)
curl -s -X POST http://localhost:8000/api/v1/week-plans/1/shuffle/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json"

# Get planned meals for a week plan
curl -s http://localhost:8000/api/v1/week-plans/1/planned_meals/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json" | jq '.'

Shopping List Examples

# Assuming $TOKEN is set from login above

# List all shopping lists
curl -s http://localhost:8000/api/v1/shopping-lists/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json" | jq '.'

# Generate shopping list from a week plan
curl -s -X POST http://localhost:8000/api/v1/shopping-lists/1/generate/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"week_plan": 1, "store": 1}'

# Get items for a shopping list
curl -s http://localhost:8000/api/v1/shopping-lists/1/items/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json" | jq '.'

# Toggle check status on an item
curl -s -X POST http://localhost:8000/api/v1/shopping-list-items/1/toggle_check/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json"

Filtering Examples

# Filter recipes by meal type
curl -s "http://localhost:8000/api/v1/recipes/?meal_type=1" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json" | jq '.'

# Filter recipes by difficulty
curl -s "http://localhost:8000/api/v1/recipes/?difficulty=2" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json" | jq '.'

# Search recipes by name
curl -s "http://localhost:8000/api/v1/recipes/?search=chicken" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json" | jq '.'

# Combine filters
curl -s "http://localhost:8000/api/v1/recipes/?meal_type=1&difficulty=2&search=pasta" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json" | jq '.'

Token Management

The Meal Planner API uses JWT tokens with refresh tokens for secure authentication. This means:

  • Access Token: Valid for 5 minutes, used for API requests
  • Refresh Token: Valid for 1 day, used to get new access tokens without re-entering credentials
  • Token Rotation: Refresh tokens rotate on use for security (ROTATE_REFRESH_TOKENS=True)

Why use refresh tokens? Instead of re-entering your username/password every 5 minutes, you use the refresh token to silently obtain new access tokens.

Token Refresh Workflow

# Step 1: Login to get both tokens
TOKEN_RESPONSE=$(curl -s -X POST http://localhost:8000/api/v1/token/ \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"username":"matt","password":"euShuuZog1ahfaiVa2"}')

# Extract tokens
ACCESS_TOKEN=$(echo $TOKEN_RESPONSE | jq -r '.access')
REFRESH_TOKEN=$(echo $TOKEN_RESPONSE | jq -r '.refresh')

# Step 2: Use access token for API calls (expires in 5 minutes)
curl -s http://localhost:8000/api/v1/recipes/ \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Accept: application/json"

# Step 3: When access token expires, refresh it WITHOUT credentials
# This returns a NEW access token and a ROTATED refresh token
REFRESH_RESPONSE=$(curl -s -X POST http://localhost:8000/api/v1/token/refresh/ \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d "{\"refresh\":\"$REFRESH_TOKEN\"}")

# Extract new tokens
ACCESS_TOKEN=$(echo $REFRESH_RESPONSE | jq -r '.access')
REFRESH_TOKEN=$(echo $REFRESH_RESPONSE | jq -r '.refresh')  # This is the NEW refresh token

# Step 4: Continue using the new access token
curl -s http://localhost:8000/api/v1/recipes/ \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Accept: application/json"

# Step 5: Verify token validity (optional)
curl -s -X POST http://localhost:8000/api/v1/token/verify/ \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d "{\"token\":\"$ACCESS_TOKEN\"}"

Important Notes:

  1. Always use the latest refresh token - After refreshing, the old refresh token is invalidated
  2. Store refresh tokens securely - They can be used to get new access tokens
  3. Access tokens expire quickly (5 min) but refresh tokens last longer (1 day)
  4. No credentials needed for refresh - Just the refresh token itself
  5. Rate limited - Token endpoints have strict throttling (10/min for /token/, 30/min for /token/refresh/)

Token Management Examples

# Get both access and refresh tokens
TOKEN_RESPONSE=$(curl -s -X POST http://localhost:8000/api/v1/token/ \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"username":"matt","password":"euShuuZog1ahfaiVa2"}')

# Extract both tokens
ACCESS_TOKEN=$(echo $TOKEN_RESPONSE | jq -r '.access')
REFRESH_TOKEN=$(echo $TOKEN_RESPONSE | jq -r '.refresh')

# Use access token
curl -s http://localhost:8000/api/v1/recipes/ \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Accept: application/json"

# When access token expires, refresh it
NEW_TOKENS=$(curl -s -X POST http://localhost:8000/api/v1/token/refresh/ \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d "{\"refresh\":\"$REFRESH_TOKEN\"}")

# Update tokens from the response
ACCESS_TOKEN=$(echo $NEW_TOKENS | jq -r '.access')
REFRESH_TOKEN=$(echo $NEW_TOKENS | jq -r '.refresh')

API Documentation

  • Swagger UI: http://localhost:8000/api/v1/schema/swagger-ui/
  • OpenAPI Schema: http://localhost:8000/api/v1/schema/

API Endpoints

All endpoints are under /api/v1/ and require authentication. See API_IMPLEMENTATION_STATUS.md for complete endpoint documentation.

Quick Reference

Resource Endpoint Methods
Meal Types /api/v1/meal-types/ GET, POST, PUT, PATCH, DELETE
Shopping Categories /api/v1/shopping-categories/ GET, POST, PUT, PATCH, DELETE
Stores /api/v1/stores/ GET, POST, PUT, PATCH, DELETE
Ingredients /api/v1/ingredients/ GET, POST, PUT, PATCH, DELETE
Recipes /api/v1/recipes/ GET, POST, PUT, PATCH, DELETE
Week Plans /api/v1/week-plans/ GET, POST, PUT, PATCH, DELETE
Planned Meals /api/v1/planned-meals/ GET, POST, PUT, PATCH, DELETE
Shopping Lists /api/v1/shopping-lists/ GET, POST, PUT, PATCH, DELETE
Shopping List Items /api/v1/shopping-list-items/ GET, POST, PUT, PATCH, DELETE

Custom Actions

Action Endpoint Method
Shuffle week plan /api/v1/week-plans/{id}/shuffle/ POST
Generate shopping list /api/v1/shopping-lists/{id}/generate/ POST
Toggle pin on meal /api/v1/planned-meals/{id}/toggle_pin/ POST
Toggle check on item /api/v1/shopping-list-items/{id}/toggle_check/ POST
List planned meals /api/v1/week-plans/{id}/planned_meals/ GET
List shopping items /api/v1/shopping-lists/{id}/items/ GET

Token Endpoints

Endpoint Method Description
/api/v1/token/ POST Obtain access and refresh tokens
/api/v1/token/refresh/ POST Refresh access token
/api/v1/token/verify/ POST Verify token validity