Metadata-Version: 2.4
Name: paper-draft
Version: 0.1.0
Summary: Opinionated FastAPI toolkit with CLI-driven architecture
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: paper-core
Requires-Dist: typer
Requires-Dist: jinja2
Requires-Dist: rich
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-asyncio; extra == "dev"
Requires-Dist: httpx; extra == "dev"

# PaperDraft

Opinionated Python REST API framework with CLI-driven architecture.

PaperDraft scaffolds projects and services with an enforced layered structure, and ships a production-ready core library covering auth, database, encryption, email, middleware, and more.

> **Status:** pre-release · v0.1 in progress · Paper Plane Consulting LLC

---

## Table of Contents

1. [Installation](#installation)
2. [Setup](#setup)
3. [CLI Reference](#cli-reference)
4. [Building with paper-draft](#building-with-paper-draft)
5. [Project Structure](#project-structure)
6. [Extending paper-draft](#extending-paper-draft)
7. [Architecture](#architecture)

---

## Packages

PaperDraft is split into two packages:

| Package | Purpose |
|---|---|
| `paper-core` | Runtime library — auth, DB, security, middleware, email, errors, audit |
| `paper-draft` | CLI tooling — project scaffolding and service generation |

---

## Installation

**Requirements:** Python 3.11+

```bash
pip install paper-draft
```

`paper-core` is installed automatically as a dependency.

---

## Local Development Setup

**Prerequisites:** Python 3.11+

```bash
# 1. Clone the CLI repo
git clone https://flypaperplane@bitbucket.org/ppc-llc/paper-draft.git
cd paper-draft

# 2. Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate        # macOS/Linux
.venv\Scripts\activate           # Windows

# 3. Install paper-core
pip install paper-core

# 4. Install paper-draft CLI in editable mode with dev dependencies
pip install -e ".[dev]"
```

Once installed, the `draft` CLI is available in your environment:

```bash
draft --help
```

---

## Setup

### Environment Files

PaperDraft uses layered `.env` files. The base `.env` sets the active environment, and the environment-specific file is loaded on top.

```
.env               ← sets ENV=dev (or staging, production)
.env.dev           ← development values (auto-created by draft init)
.env.staging       ← staging values (create manually)
.env.production    ← production values (create manually)
```

**`.env`** (minimal — sets the active environment):
```env
ENV=dev
```

**`.env.dev`** (created automatically by `draft init`):
```env
ENV=dev
APP_URL=http://localhost:8000
APP_PORT=8000

POSTGRES_CONN_STRING=postgresql+asyncpg://user:password@localhost:5432/dbname
POSTGRES_ADMIN_CONN_STRING=postgresql+asyncpg://user:password@localhost:5432/postgres

ENCRYPTION_PUBLIC_KEY=<base64-encoded PEM public key>
ENCRYPTION_PRIVATE_KEY=<base64-encoded PEM private key>

EMAIL_SMTP_HOST=smtp.example.com
EMAIL_SMTP_PORT=587
EMAIL_SMTP_USERNAME=your@email.com
EMAIL_SMTP_PASSWORD=your-smtp-password
EMAIL_SENDER_NAME=Your App
EMAIL_SENDER_ADDRESS=no-reply@example.com
```

### Generating Encryption Keys

PaperDraft uses RSA key pairs (RS256) for JWT signing and field-level encryption.

```bash
# Generate a 2048-bit RSA private key
openssl genrsa -out private.pem 2048

# Derive the public key
openssl rsa -in private.pem -pubout -out public.pem

# Base64-encode each for .env storage
base64 -w 0 private.pem   # → ENCRYPTION_PRIVATE_KEY
base64 -w 0 public.pem    # → ENCRYPTION_PUBLIC_KEY
```

---

## CLI Reference

### `draft init <name> --port <port>`

Creates a new project in a `<name>/` directory.

```bash
draft init my-api --port 8000
```

**Creates:**
```
my-api/
├── main.py            # FastAPI app entry point
├── dependencies.py    # DB + auth dependency injection
├── config.py          # Pydantic Settings configuration
├── requirements.txt   # App dependencies
├── .gitignore
├── .env.dev           # Pre-filled environment stub
├── services/          # Your services go here
└── .venv/             # Python virtual environment (auto-created)
```

**Then:**
```bash
cd my-api
# Fill in .env.dev with your DB connection string and keys
# Activate the venv: source .venv/bin/activate (macOS/Linux) or .venv\Scripts\activate (Windows)
```

---

### `draft new service <name>`

Scaffolds a new service inside an existing project.

```bash
draft new service users
draft new service users --prefix api/v1/users   # custom URL prefix
```

**Creates:**
```
services/users/
├── __init__.py
├── router.py       # FastAPI APIRouter — define your routes here
├── controller.py   # UsersController — validate + orchestrate
└── service.py      # UsersService — business logic
```

Also patches `main.py` to import and register the router automatically.

---

### `draft validate <service>`

Validates a service's structure and `main.py` registration.

```bash
draft validate users
draft validate users --quiet   # suppress output on success
```

**Checks:**
- `services/users/` directory exists
- `router.py`, `controller.py`, `service.py`, `__init__.py` all present
- `router.py` references `UsersController`
- `controller.py` defines `class UsersController`
- `service.py` defines `class UsersService`
- `main.py` imports and registers the router

Exits `0` on success, `1` on validation errors, `2` if not in a project root.

---

### `draft run`

Runs the app with uvicorn. Port is read from `APP_PORT` in your `.env.{ENV}` file.

```bash
draft run               # dev mode — auto-reload enabled
draft run --prod        # production — 4 workers, no reload
draft run --host 0.0.0.0 --prod
```

---

## Building with PaperDraft

### Workflow

```bash
# 1. Create a new project
draft init my-api --port 8000
cd my-api

# 2. Fill in .env.dev

# 3. Add a service
draft new service users

# 4. Define your routes in services/users/router.py
# 5. Implement controller logic in services/users/controller.py
# 6. Implement business logic in services/users/service.py

# 7. Validate
draft validate users

# 8. Run
draft run
```

### Layer Responsibilities

Each scaffolded service has three layers with strict responsibilities:

```
router.py       ← HTTP only. Receives request, injects dependencies, returns response.
                  No logic. No direct DB access.

controller.py   ← Validates the request. Orchestrates service calls.
                  No business logic. No direct DB access.

service.py      ← All business logic lives here.
                  Calls the DB via injected Postgres instance.
```

### Dependency Injection

`dependencies.py` wires up DB and config — inject them directly in route signatures:

```python
from dependencies import MasterDb, TenantDb, Configuration

@router.get("")
async def retrieve(
    db:     MasterDb,
    config: Configuration,
    claims: Dict[str, Any] = Depends(Authenticate())
):
    return await UsersController.retrieve(db, config, claims)
```

### Auth

Use `Authenticate` for any valid JWT, `Authorize` for role enforcement:

```python
from paper.core.auth import Authenticate, Authorize

# Any authenticated user
claims = Depends(Authenticate())

# Role-restricted
claims = Depends(Authorize(["admin", "manager"]))
```

---

## Project Structure

A full PaperDraft project looks like this:

```
my-api/
├── main.py                   # FastAPI app, middleware, router registration
├── dependencies.py           # DB + auth dependency wiring
├── config.py                 # Pydantic Settings
├── requirements.txt
├── .env                      # Sets ENV=dev|staging|production
├── .env.dev                  # Dev config (never commit)
├── services/
│   ├── users/
│   │   ├── router.py
│   │   ├── controller.py
│   │   └── service.py
│   └── auth/
│       ├── router.py
│       ├── controller.py
│       └── service.py
└── .venv/
```

---

## Extending PaperDraft

PaperDraft's core components are built on abstract base classes. Extend them to add new database engines, encryption algorithms, or email providers while keeping the same interface everywhere.

---

### Custom Database Engine

Extend `Repository` to add a new DB engine (MySQL, MongoDB, etc.).

```python
from paper.core.db.base import Repository, FilterType
from typing import Any, Dict, List, Optional

class MySQLRepository(Repository[T, M]):

    def __init__(self, connection_string: str) -> None:
        # set up your engine here
        ...

    async def create(self, entity, model, data):
        ...

    async def retrieve(self, entity, model, filter=None):
        ...

    async def single(self, entity, model, id):
        ...

    async def update(self, entity, model, id, data):
        ...

    async def delete(self, entity, id):
        ...
```

Inject it the same way as `Postgres` via `dependencies.py`.

---

### Custom Encryption Algorithm

Extend `BaseCrypto` to add a new cipher (AES, ChaCha20, etc.).

```python
from paper.core.security.base import BaseCrypto

class AESCrypto(BaseCrypto):

    def __init__(self, key: str) -> None:
        self._key = key

    def encrypt(self, value: str) -> str:
        ...

    def decrypt(self, cipher: str) -> str:
        ...

    def encrypt_urlsafe(self, value: str) -> str:
        ...

    def decrypt_urlsafe(self, cipher: str) -> str:
        ...

    def encrypt_raw(self, value: str) -> bytes:
        ...

    def decrypt_raw(self, cipher_bytes: bytes) -> str:
        ...
```

Pass your implementation to `MultiTenantDbDependency` or anywhere `BaseCrypto` is accepted:

```python
crypto = AESCrypto(key=config.ENCRYPTION.KEY.SYMMETRIC)
```

---

### Custom Email Provider

Extend `BaseEmailService` to add a new provider (SendGrid, SES, Postmark, etc.).

```python
from paper.core.email.base import BaseEmailService
from typing import Dict

class SendGridEmailService(BaseEmailService):

    def __init__(self, api_key: str, sender_name: str, sender_email: str) -> None:
        self._api_key      = api_key
        self._sender_name  = sender_name
        self._sender_email = sender_email

    def send(
        self,
        subject:         str,
        recipient_name:  str,
        recipient_email: str,
        data:            Dict[str, str],
    ) -> bool:
        # implement SendGrid API call here
        ...
```

Instantiate in `dependencies.py` and inject wherever email is needed.

---

## Architecture

```
HTTP Request
    ↓
[ Router ]            receives request, injects dependencies, returns response
    ↓
[ Auth Middleware ]    JWT validation + RBAC — always first, never skipped
    ↓
[ Custom Middleware ]  CORS, HIPAA headers, rate limiting, audit logging
    ↓
[ Controller ]        validates request, orchestrates service calls
    ↓
[ Service ]           all business logic lives here
    ↓
[ Postgres / DB ]     async SQLAlchemy — injected, never instantiated directly
```

**Core modules available via `paper.core`:**

| Module | Contents |
|--------|----------|
| `paper.core.auth` | `Authenticate`, `Authorize`, `LoginAttemptLimit`, `Password`, JWT utils |
| `paper.core.db` | `Postgres`, `Repository`, `FilterType`, `MultiTenantDbDependency` |
| `paper.core.security` | `RSACrypto`, `BaseCrypto`, `Crypto`, `Hasher`, `Pem` |
| `paper.core.email` | `SMTPEmailService`, `BaseEmailService`, `Subject`, `EmailTheme` |
| `paper.core.errors` | `ErrorHandler`, `ErrorMessage` |
| `paper.core.middleware` | `HipaaResponseHeaders`, `RequestLoggingMiddleware`, `RequestIdMiddleware` |
| `paper.core.audit` | `Audit` |
