agnes-the-ai-analyst/scripts/run-local-dev.sh
Petr Simecek 9b5214ea6f
feat(dev): LOCAL_DEV_MODE for one-command local dev + magic-link fixes (#32)
* 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.
2026-04-22 14:47:33 +02:00

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 "$@"