feat: implement serve command with dev mode support
- CLI serve command now runs uvicorn with migrations - Add dev_mode setting to bypass auth with default admin user - Add bin/animaltrack wrapper for Nix environment - Add bin/serve-dev for quick local development - Update flake.nix shellHook for PYTHONPATH and bin PATH 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2
bin/animaltrack
Executable file
2
bin/animaltrack
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env bash
|
||||
exec python -m animaltrack.cli "$@"
|
||||
2
bin/serve-dev
Executable file
2
bin/serve-dev
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env bash
|
||||
exec env CSRF_SECRET="dev-secret" DEV_MODE=true python -m animaltrack.cli serve "$@"
|
||||
@@ -80,6 +80,8 @@
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
export PYTHONPATH="$PWD/src:$PYTHONPATH"
|
||||
export PATH="$PWD/bin:$PATH"
|
||||
echo "AnimalTrack development environment ready!"
|
||||
echo "Run 'animaltrack serve' to start the app"
|
||||
'';
|
||||
|
||||
@@ -26,7 +26,7 @@ def main():
|
||||
|
||||
# serve command
|
||||
serve_parser = subparsers.add_parser("serve", help="Start the web server")
|
||||
serve_parser.add_argument("--port", type=int, default=5000, help="Port to listen on")
|
||||
serve_parser.add_argument("--port", type=int, default=3366, help="Port to listen on")
|
||||
serve_parser.add_argument("--host", type=str, default="0.0.0.0", help="Host to bind to")
|
||||
|
||||
args = parser.parse_args()
|
||||
@@ -86,9 +86,33 @@ def main():
|
||||
run_seeds(db)
|
||||
print("Seed data loaded successfully")
|
||||
elif args.command == "serve":
|
||||
import uvicorn
|
||||
|
||||
from animaltrack.config import Settings
|
||||
from animaltrack.db import get_db
|
||||
from animaltrack.migrations import run_migrations
|
||||
from animaltrack.web.app import create_app
|
||||
|
||||
settings = Settings()
|
||||
|
||||
# Run migrations first
|
||||
print("Running migrations...")
|
||||
success = run_migrations(
|
||||
db_path=settings.db_path,
|
||||
migrations_dir="migrations",
|
||||
verbose=False,
|
||||
)
|
||||
if not success:
|
||||
print("Migration failed", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Create app
|
||||
db = get_db(settings.db_path)
|
||||
app, _ = create_app(settings=settings, db=db)
|
||||
|
||||
# Start server
|
||||
print(f"Starting server on {args.host}:{args.port}...")
|
||||
# TODO: Implement server
|
||||
print("Server not yet implemented")
|
||||
uvicorn.run(app, host=args.host, port=args.port)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -37,6 +37,7 @@ class Settings(BaseSettings):
|
||||
seed_on_start: bool = False
|
||||
log_level: str = "INFO"
|
||||
metrics_enabled: bool = True
|
||||
dev_mode: bool = False # Bypasses auth, sets default user
|
||||
|
||||
@cached_property
|
||||
def trusted_proxy_ips(self) -> list[str]:
|
||||
|
||||
@@ -10,6 +10,7 @@ from starlette.responses import PlainTextResponse, Response
|
||||
|
||||
from animaltrack.config import Settings
|
||||
from animaltrack.id_gen import generate_id
|
||||
from animaltrack.models.reference import User, UserRole
|
||||
from animaltrack.repositories.users import UserRepository
|
||||
|
||||
# Safe HTTP methods that don't require CSRF protection
|
||||
@@ -170,6 +171,8 @@ def auth_before(req: Request, settings: Settings, db) -> Response | None:
|
||||
- Auth header is present
|
||||
- User exists and is active in database
|
||||
|
||||
In dev_mode, bypasses all checks and uses a default admin user.
|
||||
|
||||
Args:
|
||||
req: The Starlette request object.
|
||||
settings: Application settings.
|
||||
@@ -178,6 +181,17 @@ def auth_before(req: Request, settings: Settings, db) -> Response | None:
|
||||
Returns:
|
||||
None to continue processing, or Response to short-circuit.
|
||||
"""
|
||||
# Dev mode: bypass auth entirely
|
||||
if settings.dev_mode:
|
||||
req.scope["auth"] = User(
|
||||
username="dev",
|
||||
role=UserRole.ADMIN,
|
||||
active=True,
|
||||
created_at_utc=0,
|
||||
updated_at_utc=0,
|
||||
)
|
||||
return None
|
||||
|
||||
# Check trusted proxy
|
||||
if not is_trusted_proxy(req, settings):
|
||||
return PlainTextResponse("Forbidden: Request not from trusted proxy", status_code=403)
|
||||
|
||||
Reference in New Issue
Block a user