add dev setup/run scripts and Resend test email docs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
11
CHANGELOG.md
11
CHANGELOG.md
@@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **Dev setup script** (`scripts/dev_setup.sh`) — interactive bootstrap that
|
||||||
|
checks prerequisites, installs deps, creates `.env` with auto-generated
|
||||||
|
`SECRET_KEY`, runs migrations, seeds test data, optionally sets up Paddle
|
||||||
|
sandbox products, and builds CSS
|
||||||
|
- **Dev run script** (`scripts/dev_run.sh`) — starts app, worker, and CSS
|
||||||
|
watcher in parallel with colored/labeled output and clean Ctrl-C shutdown
|
||||||
|
- **Resend test email docs** — documented Resend test addresses
|
||||||
|
(`delivered@resend.dev`, `bounced@resend.dev`, etc.) in `.env.example` and
|
||||||
|
README for testing email flows without a verified domain
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- **Quote wizard state loss** — `_accumulated` hidden input used `"` attribute
|
- **Quote wizard state loss** — `_accumulated` hidden input used `"` attribute
|
||||||
delimiters which broke on `tojson` output containing literal `"` characters;
|
delimiters which broke on `tojson` output containing literal `"` characters;
|
||||||
|
|||||||
@@ -12,7 +12,18 @@ DATABASE_PATH=data/app.db
|
|||||||
MAGIC_LINK_EXPIRY_MINUTES=15
|
MAGIC_LINK_EXPIRY_MINUTES=15
|
||||||
SESSION_LIFETIME_DAYS=30
|
SESSION_LIFETIME_DAYS=30
|
||||||
|
|
||||||
# Email (Resend) — leave blank for dev (emails print to console)
|
# Email (Resend)
|
||||||
|
# Leave blank for dev — emails print to console (no Resend account needed).
|
||||||
|
#
|
||||||
|
# Resend test addresses (work with any valid API key, no verified domain needed):
|
||||||
|
# delivered@resend.dev — accepted, simulates successful delivery
|
||||||
|
# bounced@resend.dev — simulates a hard bounce
|
||||||
|
# complained@resend.dev — simulates a spam complaint
|
||||||
|
# suppressed@resend.dev — simulates a suppressed recipient
|
||||||
|
# These support +label syntax: delivered+test1@resend.dev
|
||||||
|
# You can also send FROM onboarding@resend.dev without a verified domain.
|
||||||
|
#
|
||||||
|
# Dev login shortcut (no email needed): /auth/dev-login?email=dev@localhost
|
||||||
RESEND_API_KEY=
|
RESEND_API_KEY=
|
||||||
EMAIL_FROM=hello@padelnomics.io
|
EMAIL_FROM=hello@padelnomics.io
|
||||||
ADMIN_EMAIL=leads@padelnomics.io
|
ADMIN_EMAIL=leads@padelnomics.io
|
||||||
|
|||||||
@@ -2,6 +2,18 @@
|
|||||||
|
|
||||||
Plan, finance, and build your padel business.
|
Plan, finance, and build your padel business.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/dev_setup.sh # one-time: deps, .env, migrations, seed data, CSS
|
||||||
|
./scripts/dev_run.sh # start app + worker + CSS watcher
|
||||||
|
```
|
||||||
|
|
||||||
|
Then open:
|
||||||
|
- **App**: http://localhost:5000
|
||||||
|
- **Dev login**: http://localhost:5000/auth/dev-login?email=dev@localhost
|
||||||
|
- **Admin**: http://localhost:5000/admin (password: `admin`)
|
||||||
|
|
||||||
## Local Development Setup
|
## Local Development Setup
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
@@ -224,6 +236,25 @@ print('Test data seeded')
|
|||||||
"
|
"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Resend Test Emails
|
||||||
|
|
||||||
|
When `RESEND_API_KEY` is blank (default), all emails print to the console —
|
||||||
|
no Resend account needed.
|
||||||
|
|
||||||
|
When you have a Resend API key, you can use their test addresses to simulate
|
||||||
|
delivery outcomes without a verified domain:
|
||||||
|
|
||||||
|
| Address | Behavior |
|
||||||
|
|---------|----------|
|
||||||
|
| `delivered@resend.dev` | Accepted, simulates successful delivery |
|
||||||
|
| `bounced@resend.dev` | Simulates a hard bounce |
|
||||||
|
| `complained@resend.dev` | Simulates a spam complaint |
|
||||||
|
| `suppressed@resend.dev` | Simulates a suppressed recipient |
|
||||||
|
|
||||||
|
These support `+label` syntax (e.g. `delivered+test1@resend.dev`) for unique
|
||||||
|
recipients. You can also send **from** `onboarding@resend.dev` without a
|
||||||
|
verified domain.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Architecture Overview
|
## Architecture Overview
|
||||||
|
|||||||
62
padelnomics/scripts/dev_run.sh
Executable file
62
padelnomics/scripts/dev_run.sh
Executable file
@@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Start all Padelnomics dev processes with colored, labeled output.
|
||||||
|
#
|
||||||
|
# Usage: ./scripts/dev_run.sh
|
||||||
|
#
|
||||||
|
# Starts: app (port 5000), background worker, CSS watcher.
|
||||||
|
# Ctrl-C stops everything cleanly.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
# -- Colors for each process -------------------------------------------------
|
||||||
|
|
||||||
|
COLOR_APP='\033[0;36m' # cyan
|
||||||
|
COLOR_WORKER='\033[0;33m' # yellow
|
||||||
|
COLOR_CSS='\033[0;35m' # magenta
|
||||||
|
NC='\033[0m'
|
||||||
|
BOLD='\033[1m'
|
||||||
|
|
||||||
|
PIDS=()
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Stopping all processes...${NC}"
|
||||||
|
for pid in "${PIDS[@]}"; do
|
||||||
|
kill "$pid" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
wait 2>/dev/null || true
|
||||||
|
echo "Done."
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup SIGINT SIGTERM
|
||||||
|
|
||||||
|
# Prefix each line of a command's output with a colored label.
|
||||||
|
# Usage: run_with_label COLOR LABEL COMMAND...
|
||||||
|
run_with_label() {
|
||||||
|
local color="$1" label="$2"
|
||||||
|
shift 2
|
||||||
|
"$@" 2>&1 | while IFS= read -r line; do
|
||||||
|
echo -e "${color}[${label}]${NC} ${line}"
|
||||||
|
done &
|
||||||
|
PIDS+=($!)
|
||||||
|
}
|
||||||
|
|
||||||
|
# -- Start processes ---------------------------------------------------------
|
||||||
|
|
||||||
|
echo -e "${BOLD}Starting Padelnomics dev environment${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " app: http://localhost:5000"
|
||||||
|
echo " admin: http://localhost:5000/admin"
|
||||||
|
echo " login: http://localhost:5000/auth/dev-login?email=dev@localhost"
|
||||||
|
echo ""
|
||||||
|
echo "Press Ctrl-C to stop all processes."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
run_with_label "$COLOR_APP" "app " uv run python -m padelnomics.app
|
||||||
|
run_with_label "$COLOR_WORKER" "worker" uv run python -m padelnomics.worker
|
||||||
|
run_with_label "$COLOR_CSS" "css " make css-watch
|
||||||
|
|
||||||
|
wait
|
||||||
135
padelnomics/scripts/dev_setup.sh
Executable file
135
padelnomics/scripts/dev_setup.sh
Executable file
@@ -0,0 +1,135 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Bootstrap a local dev environment for Padelnomics.
|
||||||
|
#
|
||||||
|
# Usage: ./scripts/dev_setup.sh
|
||||||
|
#
|
||||||
|
# Checks prerequisites, installs deps, creates .env, runs migrations,
|
||||||
|
# seeds test data, and builds CSS.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
# -- Colors & helpers -------------------------------------------------------
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
BOLD='\033[1m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
info() { echo -e "${BLUE}==>${NC} ${BOLD}$1${NC}"; }
|
||||||
|
ok() { echo -e "${GREEN} ✓${NC} $1"; }
|
||||||
|
warn() { echo -e "${YELLOW} !${NC} $1"; }
|
||||||
|
fail() { echo -e "${RED} ✗${NC} $1"; exit 1; }
|
||||||
|
|
||||||
|
# -- Prerequisites -----------------------------------------------------------
|
||||||
|
|
||||||
|
info "Checking prerequisites"
|
||||||
|
|
||||||
|
command -v python3 >/dev/null 2>&1 || fail "python3 not found. Install Python 3.12+."
|
||||||
|
command -v uv >/dev/null 2>&1 || fail "uv not found. Install: https://docs.astral.sh/uv/getting-started/installation/"
|
||||||
|
|
||||||
|
PYTHON_VERSION=$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')
|
||||||
|
ok "python3 ${PYTHON_VERSION}"
|
||||||
|
ok "uv $(uv --version 2>/dev/null | head -1)"
|
||||||
|
|
||||||
|
# -- Install dependencies ----------------------------------------------------
|
||||||
|
|
||||||
|
info "Installing Python dependencies"
|
||||||
|
uv sync
|
||||||
|
ok "Dependencies installed"
|
||||||
|
|
||||||
|
# -- .env --------------------------------------------------------------------
|
||||||
|
|
||||||
|
if [ -f .env ]; then
|
||||||
|
warn ".env already exists — skipping creation"
|
||||||
|
else
|
||||||
|
info "Creating .env from .env.example"
|
||||||
|
cp .env.example .env
|
||||||
|
|
||||||
|
# Auto-generate SECRET_KEY
|
||||||
|
SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
|
||||||
|
sed -i "s/^SECRET_KEY=.*/SECRET_KEY=${SECRET_KEY}/" .env
|
||||||
|
ok "SECRET_KEY generated"
|
||||||
|
|
||||||
|
# Prompt for optional keys
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Optional API keys${NC} (press Enter to skip — everything works without them)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
read -rp " Paddle API key (for checkout testing): " PADDLE_API_KEY
|
||||||
|
if [ -n "$PADDLE_API_KEY" ]; then
|
||||||
|
sed -i "s/^PADDLE_API_KEY=.*/PADDLE_API_KEY=${PADDLE_API_KEY}/" .env
|
||||||
|
ok "PADDLE_API_KEY set"
|
||||||
|
|
||||||
|
read -rp " Paddle client token: " PADDLE_CLIENT_TOKEN
|
||||||
|
[ -n "$PADDLE_CLIENT_TOKEN" ] && sed -i "s/^PADDLE_CLIENT_TOKEN=.*/PADDLE_CLIENT_TOKEN=${PADDLE_CLIENT_TOKEN}/" .env
|
||||||
|
|
||||||
|
read -rp " Paddle webhook secret: " PADDLE_WEBHOOK_SECRET
|
||||||
|
[ -n "$PADDLE_WEBHOOK_SECRET" ] && sed -i "s/^PADDLE_WEBHOOK_SECRET=.*/PADDLE_WEBHOOK_SECRET=${PADDLE_WEBHOOK_SECRET}/" .env
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -rp " Resend API key (for email testing): " RESEND_API_KEY
|
||||||
|
if [ -n "$RESEND_API_KEY" ]; then
|
||||||
|
sed -i "s/^RESEND_API_KEY=.*/RESEND_API_KEY=${RESEND_API_KEY}/" .env
|
||||||
|
ok "RESEND_API_KEY set"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
ok ".env created"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# -- Migrations ---------------------------------------------------------------
|
||||||
|
|
||||||
|
info "Running database migrations"
|
||||||
|
uv run python -m padelnomics.migrations.migrate
|
||||||
|
ok "Migrations applied"
|
||||||
|
|
||||||
|
# -- Seed data ----------------------------------------------------------------
|
||||||
|
|
||||||
|
info "Seeding development data"
|
||||||
|
uv run python -m padelnomics.scripts.seed_dev_data
|
||||||
|
ok "Dev data seeded"
|
||||||
|
|
||||||
|
# -- Paddle setup (optional) --------------------------------------------------
|
||||||
|
|
||||||
|
if grep -q '^PADDLE_API_KEY=.\+' .env 2>/dev/null; then
|
||||||
|
echo ""
|
||||||
|
read -rp "$(echo -e "${BLUE}==>${NC} Set up Paddle sandbox products? [y/N] ")" SETUP_PADDLE
|
||||||
|
if [[ "$SETUP_PADDLE" =~ ^[Yy]$ ]]; then
|
||||||
|
uv run python -m padelnomics.scripts.setup_paddle
|
||||||
|
ok "Paddle products created"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# -- CSS build ----------------------------------------------------------------
|
||||||
|
|
||||||
|
info "Building CSS"
|
||||||
|
make css-build
|
||||||
|
ok "CSS built"
|
||||||
|
|
||||||
|
# -- Summary ------------------------------------------------------------------
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}${BOLD}Setup complete!${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " Start all services: ./scripts/dev_run.sh"
|
||||||
|
echo ""
|
||||||
|
echo " URLs:"
|
||||||
|
echo " App: http://localhost:5000"
|
||||||
|
echo " Dev login: http://localhost:5000/auth/dev-login?email=dev@localhost"
|
||||||
|
echo " Admin: http://localhost:5000/admin (password: admin)"
|
||||||
|
echo ""
|
||||||
|
echo " Seeded accounts:"
|
||||||
|
echo " dev@localhost — dev user (use dev-login link above)"
|
||||||
|
echo ""
|
||||||
|
echo " Email:"
|
||||||
|
if grep -q '^RESEND_API_KEY=.\+' .env 2>/dev/null; then
|
||||||
|
echo " Resend configured — emails sent via API"
|
||||||
|
echo " Test addresses: delivered@resend.dev, bounced@resend.dev"
|
||||||
|
else
|
||||||
|
echo " No Resend key — emails print to console"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
Reference in New Issue
Block a user