* feat(dev): add LOCAL_DEV_MODE for one-command local dev When LOCAL_DEV_MODE=1, every protected route auto-authenticates as a seeded admin user (default dev@localhost) — no login screen, no Google OAuth config, no magic-link roundtrip. Startup logs a loud warning to make misuse obvious. Also fixes two preexisting bugs in the magic-link flow that surfaced while wiring up the dev fallback: - /auth/email/verify only accepted POST, but the URL embedded in emails is a GET link — clicking from any mail client returned 405. Added a GET variant that consumes the token, sets the auth cookie, and redirects to /dashboard. - Token expiry check compared an offset-aware datetime.now(timezone.utc) against an offset-naive value from DuckDB, raising TypeError on every valid link. Normalize the stored timestamp to UTC before subtracting. Dev-only fallback (scoped strictly to LOCAL_DEV_MODE to keep test and production behavior identical): send-link logs the magic link to stderr and returns it as dev_link in the JSON response when no SMTP is configured. Usage: ./scripts/run-local-dev.sh open http://localhost:8000 # lands on /dashboard as admin * fix(dev): URL-encode magic-link email + avoid /login redirect loop Two issues surfaced by Devin review on PR #32. 1. _build_magic_link interpolated email into the URL unescaped. For addresses with '+' (e.g. user+tag@gmail.com) Starlette's query parser decoded '+' as a space on the GET /verify side, so repo.get_by_email returned None and every click yielded 401 "Invalid link". quote(email, safe='') fixes both the email transport and the dev_link fallback. 2. /login in LOCAL_DEV_MODE unconditionally redirected to /dashboard. If dev-user seeding failed at startup (main.py wraps seed in try/except), /dashboard 401'd, the HTML redirect handler bounced to /login, and the loop repeated until the browser aborted. Now /login checks the dev user actually exists before short-circuiting; otherwise it falls through to the normal login form so the missing seed is visible.
25 lines
881 B
Bash
Executable file
25 lines
881 B
Bash
Executable file
#!/usr/bin/env bash
|
|
# Run Agnes locally with auth bypass + dev-mode magic links.
|
|
#
|
|
# Stacks three compose files:
|
|
# 1. docker-compose.yml — base services
|
|
# 2. docker-compose.override.yml — hot-reload + source bind mount (dev default)
|
|
# 3. docker-compose.local-dev.yml — LOCAL_DEV_MODE=1, drops .env requirement
|
|
#
|
|
# After startup visit http://localhost:8000 — you'll land on /dashboard
|
|
# logged in as dev@localhost (role=admin). No login screen, no email delivery needed.
|
|
set -euo pipefail
|
|
|
|
cd "$(dirname "$0")/.."
|
|
|
|
# Ensure docker-compose.yml does not require a .env file. We override env_file in the
|
|
# local-dev overlay, but compose still touches the file path during config validation.
|
|
if [[ ! -f .env ]]; then
|
|
touch .env
|
|
fi
|
|
|
|
exec docker compose \
|
|
-f docker-compose.yml \
|
|
-f docker-compose.override.yml \
|
|
-f docker-compose.local-dev.yml \
|
|
up "$@"
|