Skip to content

AndrewMead10/python-template

Repository files navigation

Python App Template

A full-stack local Python web application template with FastAPI, Jinja2, htmx, Tailwind CSS, SQLite, and Google OAuth.

Stack

Layer Technology
Framework FastAPI (async)
Templates Jinja2 + htmx
Styling Tailwind CSS
Database SQLite (via SQLModel + SQLAlchemy)
Migrations Alembic
Auth Google OAuth (authlib)
Validation Pydantic v2
Storage Local filesystem
Logging Pydantic Logfire
Package manager uv
Deployment Docker (optional)

Quick Start

# 1. Install dependencies
uv sync

# 2. Configure environment
uv run python scripts/setup.py   # interactive setup
# or: cp .env.example .env && edit .env

# 3. Run database migrations
uv run alembic upgrade head

# 4. Start the development server
make dev
# -> http://localhost:8000
# -> http://localhost:8000/docs  (API docs)

Project Structure

app/
├── main.py              # App entry point, middleware, router mounts
├── config.py            # Settings (reads .env)
├── db/
│   ├── database.py      # DB engine and session dependency
│   └── schema.py        # SQLModel table definitions
├── lib/
│   ├── auth.py          # Session management, OAuth user creation
│   ├── errors.py        # Standardised API response helpers
│   ├── oauth.py         # authlib Google OAuth client
│   └── storage.py       # Local file storage
├── middleware/
│   ├── auth.py          # Auth FastAPI dependencies
│   ├── logging.py       # Request logging
│   └── security.py      # CSP + security headers
├── pages/               # One file per page (HTML + API routes)
├── functions/           # Shared DB query functions
└── templates/           # Jinja2 HTML templates
alembic/                 # Database migrations
Dockerfile               # Production container image
scripts/
└── setup.py             # Interactive setup

Commands

make dev            # Start dev server with hot reload
make setup          # Interactive first-time configuration
make db-generate    # Generate a new migration (msg="description")
make db-migrate     # Apply pending migrations
make db-downgrade   # Roll back one migration
make typecheck      # Run pyright type checker
make lint           # Run ruff linter
make format         # Run ruff formatter

Adding a New Page

  1. Create app/pages/my_page.py
  2. Create app/templates/my_page.html
  3. Add from app.pages import my_page and app.include_router(my_page.router) in app/main.py

See AGENTS.md for detailed patterns and examples.

Environment Variables

Copy .env.example to .env and fill in:

Variable Required Default
SECRET_KEY Yes
APP_URL Yes http://localhost:8000
DATABASE_URL No sqlite:///./data.db
GOOGLE_CLIENT_ID OAuth only
GOOGLE_CLIENT_SECRET OAuth only
DEBUG No false

Google OAuth Setup

  1. Go to Google Cloud Console
  2. Create an OAuth 2.0 Client ID (Web application)
  3. Add http://localhost:8000/auth/callback as an authorised redirect URI
  4. Copy the client ID and secret into .env

Deployment

CI (homelab cluster)

.github/workflows/deploy.yml builds the image on every push to master/main and pushes it to the homelab registry as 192.168.0.165:5000/<repo-name>:latest (plus a :<git-sha> tag for rollbacks). Override the registry with the repo variable REGISTRY_HOST.

This requires a self-hosted GitHub Actions runner on the homelab LAN — GitHub-hosted runners cannot reach the private registry. The runner host's docker daemon must trust the registry (all swarm nodes already do).

Deploys are automatic: any swarm service using this image with the shepherd.enable=true label rolls to the new :latest within ~5 minutes of the push. The first deploy of a project is done once in the m720q-swarm repo (stack file + ./run --tags stacks); after that, pushes deploy themselves.

Docker

The included image runs Alembic migrations before starting Uvicorn. Build it locally or reference this repository as the build context from your deployment configuration:

docker build -t python-app .

The deployment environment must provide SECRET_KEY and the public APP_URL. It should mount persistent storage at /data and configure:

DATABASE_URL=sqlite:////data/app.db
STORAGE_PATH=/data/uploads

Set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET when using OAuth, and configure ${APP_URL}/auth/callback as the Google OAuth redirect URI. Keep DEBUG=false, terminate TLS outside the container, and back up /data. SQLite deployments should use a single app container; use an external database before scaling to multiple replicas.

Without Docker

uv sync --locked --no-dev
uv run alembic upgrade head
uv run uvicorn app.main:app --host 0.0.0.0 --port 8000

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages