Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ docs/
logs/
*.log

# Certs (will be generated at runtime)
certs/

# Environment files (use docker-compose env instead)
.env
.env.*
Expand Down
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
HOST=0.0.0.0
PORT=443
PORT=8000
API_TOKEN_HASH=
18 changes: 8 additions & 10 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Project Overview

FastAPI-based template server providing reusable infrastructure for building secure HTTPS applications.
FastAPI-based template server providing reusable infrastructure for building secure HTTP applications.
Implements authentication, rate limiting, security headers, and observability foundations via a base `TemplateServer` class.
Developers extend `TemplateServer` to create application-specific servers (see `ExampleServer` in `main.py`).

Expand Down Expand Up @@ -75,7 +75,7 @@ uv sync # Install dependencies
uv run generate-new-token # Generate API key, save hash to .env

# Development
uv run python-template-server # Start server (https://localhost:443/api)
uv run python-template-server # Start server (http://localhost:8000/api)
uv run -m pytest # Run tests with coverage
uv run -m ty check . # Type checking
uv run -m ruff check . # Linting
Expand All @@ -100,15 +100,15 @@ docker compose down # Stop and remove containers
- **Stage 2 (runtime)**: Installs wheel, copies configuration from host, copies static files and `.here` from installed package to /app
- **Startup Script**: Created inline in Dockerfile as `/app/start.sh`, generates token if missing, starts server with host/port from environment variables
- **Config Selection**: Uses `config.json` copied from host configuration directory
- **Environment Variables**: `HOST` (default: 0.0.0.0), `PORT` (default: 443), `API_TOKEN_HASH` (auto-generated if not set)
- **Environment Variables**: `HOST` (default: 0.0.0.0), `PORT` (default: 8000), `API_TOKEN_HASH` (auto-generated if not set)
- **Health Check**: Python urllib request to `/api/health` with unverified SSL context (no auth required)
- **Note**: No user switching - runs as root (could be security improvement)

## Project-Specific Conventions

### Code Organization

- **Handlers**: Separate modules for auth (`authentication_handler.py`), certs (`certificate_handler.py`)
- **Handlers**: Module for auth (`authentication_handler.py`)
- **Middleware**: Dedicated package `middleware/` with base classes extending `BaseHTTPMiddleware`
- **Constants**: All magic strings/numbers in `constants.py` (ports, file names, log config, static directory)
- **Models**: Pydantic models for config + API responses, use `@property` for derived values
Expand All @@ -117,9 +117,8 @@ docker compose down # Stop and remove containers
### Security Patterns

- **Never log secrets**: Print tokens via `print()`, not `logger` (see `generate_new_token()`)
- **Path validation**: Use Pydantic validators, Path objects for cert paths
- **Path validation**: Use Pydantic validators
- **Security headers**: HSTS, CSP, X-Frame-Options via `SecurityHeadersMiddleware`
- **Cert generation**: RSA-4096, SHA-256, 365-day validity, SANs for localhost

### API Design

Expand Down Expand Up @@ -172,21 +171,20 @@ All PRs must pass:
- `template_server.py` - Base TemplateServer class with middleware/auth setup
- `main.py` - ExampleServer implementation showing how to extend TemplateServer
- `authentication_handler.py` - Token generation, hashing, verification
- `certificate_handler.py` - Self-signed SSL certificate generation and loading
- `logging_setup.py` - Logging configuration (executed on import)
- `models.py` - All Pydantic models (config + responses)
- `constants.py` - Project constants, logging config
- `docker-compose.yml` - Container stack

### Environment Variables

- `HOST` - Server host address (default: localhost)
- `PORT` - Server port (default: 443)
- `HOST` - Server host address (default: 127.0.0.1)
- `PORT` - Server port (default: 8000)
- `API_TOKEN_HASH` - SHA-256 hash of API token (auto-generated if not provided)

### Configuration Files

- `configuration/config.json` - Server configuration (rate limiting, security, CORS, certificate, etc.)
- `configuration/config.json` - Server configuration (rate limiting, security, CORS, etc.)
- `.env.example` - Template for environment variables (HOST, PORT, API_TOKEN_HASH)
- `.env` - Environment variables including host, port, and API token hash (auto-created by generate-new-token or Docker startup script)
- **Docker**: Startup script auto-generates token if .env doesn't exist or API_TOKEN_HASH is empty
2 changes: 1 addition & 1 deletion .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:

- uses: javidahmed64592/actions-template-python/actions/docker/check-containers@main
with:
port: 443
port: 8000

- uses: javidahmed64592/actions-template-python/actions/docker/stop-services@main

Expand Down
6 changes: 0 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,6 @@ ENV/
env.bak/
venv.bak/

# SSL/TLS Certificates
*.pem
*.key
*.crt
*.csr

# Spyder project settings
.spyderproject
.spyproject
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,6 @@ EXPOSE $PORT

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('https://localhost:$PORT/api/health', context=__import__('ssl')._create_unverified_context()).read()" || exit 1
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:$PORT/api/health', context=__import__('ssl')._create_unverified_context()).read()" || exit 1

CMD ["/app/start.sh"]
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ This repository provides a solid foundation for building secure, observable Fast
- **CORS Support**: Configurable cross-origin resource sharing for frontend integration
- **Static File Serving**: FastAPI's StaticFiles mounting with custom 404.html support
- **Docker Support**: Multi-stage builds with docker-compose orchestration
- **Production Patterns**: Token generation, SSL certificate handling, health checks
- **Production Patterns**: Token generation, health checks

## Architecture

Expand All @@ -56,8 +56,8 @@ Download the latest release from [GitHub Releases](https://github.com/javidahmed

Rename `.env.example` to `.env` and edit it to configure the server.

- `HOST`: Server host address (default: localhost)
- `PORT`: Server port (default: 443)
- `HOST`: Server host address (default: 0.0.0.0)
- `PORT`: Server port (default: 8000)
- `API_TOKEN_HASH`: Leave blank to auto-generate on first run, or provide your own token hash

### Managing the Container
Expand Down
6 changes: 3 additions & 3 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ docker compose up -d

### Access Points

- **API Server**: https://localhost:443/api
- **Swagger UI**: https://localhost:443/api/docs
- **ReDoc**: https://localhost:443/api/redoc
- **API Server**: http://localhost:8000/api
- **Swagger UI**: http://localhost:8000/api/docs
- **ReDoc**: http://localhost:8000/api/redoc
19 changes: 3 additions & 16 deletions configuration/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,10 @@
},
"cors": {
"enabled": false,
"allow_origins": [
"*"
],
"allow_origins": ["*"],
"allow_credentials": true,
"allow_methods": [
"GET"
],
"allow_headers": [
"Content-Type",
"X-API-Key"
],
"allow_methods": ["GET"],
"allow_headers": ["Content-Type", "X-API-Key"],
"expose_headers": [],
"max_age": 600
},
Expand All @@ -24,12 +17,6 @@
"rate_limit": "100/minute",
"storage_uri": ""
},
"certificate": {
"directory": "certs",
"ssl_keyfile": "key.pem",
"ssl_certfile": "cert.pem",
"days_valid": 365
},
"json_response": {
"ensure_ascii": false,
"allow_nan": false,
Expand Down
10 changes: 3 additions & 7 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
services:
# Python Template Server
python-template-server:
image: ${IMAGE:-ghcr.io/javidahmed64592/python-template-server:latest}
build:
context: .
dockerfile: Dockerfile
container_name: python-template-server
ports:
- "${PORT:-443}:${PORT:-443}"
- "127.0.0.1:${PORT:-8000}:${PORT:-8000}"
environment:
- HOST=${HOST:-0.0.0.0}
- PORT=${PORT:-443}
# Load API token hash from .env file (optional - will be generated if missing)
- PORT=${PORT:-8000}
- API_TOKEN_HASH=${API_TOKEN_HASH:-}
volumes:
- ./.env:/app/.env
- certs:/app/certs
- logs:/app/logs
restart: unless-stopped
healthcheck:
Expand All @@ -24,13 +21,12 @@ services:
"CMD",
"sh",
"-c",
'python -c "import urllib.request; urllib.request.urlopen(''https://localhost:$PORT/api/health'', context=__import__(''ssl'')._create_unverified_context()).read()"',
'python -c "import urllib.request; urllib.request.urlopen(''http://localhost:$PORT/api/health'', context=__import__(''ssl'')._create_unverified_context()).read()"',
]
interval: 30s
timeout: 10s
retries: 3
start_period: 10s

volumes:
certs:
logs:
5 changes: 2 additions & 3 deletions docs/source/server.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ All API responses include security headers to protect against common web vulnera

**Headers Included:**

- ``Strict-Transport-Security``: Forces HTTPS connections (HSTS)
- ``X-Content-Type-Options``: Prevents MIME-type sniffing
- ``X-Frame-Options``: Prevents clickjacking attacks
- ``Content-Security-Policy``: Controls which resources can be loaded
Expand Down Expand Up @@ -85,7 +84,7 @@ FastAPI automatically generates interactive API documentation, providing two dif

**Swagger UI**

- **URL**: ``https://localhost:443/api/docs``
- **URL**: ``http://localhost:8000/api/docs``
- **Purpose**: Interactive API documentation with "Try it out" functionality

**Features**:
Expand All @@ -98,7 +97,7 @@ FastAPI automatically generates interactive API documentation, providing two dif

**ReDoc**

- **URL**: ``https://localhost:443/api/redoc``
- **URL**: ``http://localhost:8000/api/redoc``
- **Purpose**: Alternative API documentation with a clean, three-panel layout

**Features**:
Expand Down
10 changes: 5 additions & 5 deletions docs/source/smg.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,19 +96,19 @@ Start the server with:

uv run |repo_name|

The backend will be available at ``https://localhost:443/api`` by default.
The backend will be available at ``http://localhost:8000/api`` by default.

**Available Endpoints:**

- **Health Check:** ``https://localhost:443/api/health``
- **Login:** ``https://localhost:443/api/login`` (requires authentication)
- **Health Check:** ``http://localhost:8000/api/health``
- **Login:** ``http://localhost:8000/api/login`` (requires authentication)

**Testing the API:**

.. code-block:: sh

curl -k https://localhost:443/api/health
curl -k -H "X-API-Key: your-token-here" https://localhost:443/api/login
curl -k http://localhost:8000/api/health
curl -k -H "X-API-Key: your-token-here" http://localhost:8000/api/login

Testing, Linting, and Type Checking
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,21 @@ classifiers = [
"License :: OSI Approved :: MIT License",
]
dependencies = [
"cryptography>=49.0.0",
"fastapi>=0.139.0",
"cryptography>=50.0.0",
"fastapi>=0.141.1",
"httpx>=0.28.1",
"python-dotenv>=1.2.2",
"python-multipart>=0.0.32",
"slowapi>=0.1.10",
"sqlmodel>=0.0.39",
"template-python @ git+https://github.com/javidahmed64592/template-python.git",
"uvicorn[standard]>=0.49.0",
"uvicorn>=0.52.0",
]

[project.optional-dependencies]
dev = [
"pytest-asyncio>=1.4.0",
"pytest-env>=1.6.0",
"pytest-env>=1.7.0",
"template-python[dev] @ git+https://github.com/javidahmed64592/template-python.git",
]
docs = [
Expand Down
Loading