Metadata-Version: 2.4
Name: nimoh-be-django-base
Version: 0.5.0
Summary: Reusable Django backend foundation with auth, monitoring, privacy, security and project scaffolding
Project-URL: Homepage, https://github.com/Nimoh-Digital-Solutions/be-django-base
Project-URL: Repository, https://github.com/Nimoh-Digital-Solutions/be-django-base
Project-URL: Changelog, https://github.com/Nimoh-Digital-Solutions/be-django-base/blob/main/CHANGELOG.md
Author: nimoh
License: MIT License
        
        Copyright (c) 2026 nimoh
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: authentication,backend,django,monitoring,privacy,reusable
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: Django
Classifier: Framework :: Django :: 5.1
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Requires-Python: >=3.12
Requires-Dist: argon2-cffi>=23.1
Requires-Dist: celery>=5.4
Requires-Dist: django-celery-beat>=2.8
Requires-Dist: django-celery-results>=2.5
Requires-Dist: django-cors-headers>=4.0
Requires-Dist: django-environ>=0.11
Requires-Dist: django-filter>=24.0
Requires-Dist: django-redis>=5.3
Requires-Dist: django<6.0,>=5.1
Requires-Dist: djangorestframework-simplejwt>=5.3
Requires-Dist: djangorestframework>=3.15
Requires-Dist: drf-spectacular>=0.27
Requires-Dist: redis>=5.0
Requires-Dist: requests>=2.32
Requires-Dist: user-agents>=2.2
Requires-Dist: whitenoise>=6.0
Provides-Extra: 2fa
Requires-Dist: django-otp>=1.5; extra == '2fa'
Provides-Extra: all
Requires-Dist: channels-redis>=4.2; extra == 'all'
Requires-Dist: channels>=4.2; extra == 'all'
Requires-Dist: click>=8.1; extra == 'all'
Requires-Dist: cryptography>=44.0; extra == 'all'
Requires-Dist: django-anymail>=13.0; extra == 'all'
Requires-Dist: django-csp>=4.0b1; extra == 'all'
Requires-Dist: django-fernet-fields>=0.6; extra == 'all'
Requires-Dist: django-otp>=1.5; extra == 'all'
Requires-Dist: jinja2>=3.1; extra == 'all'
Requires-Dist: pillow>=9.0; extra == 'all'
Requires-Dist: psutil>=5.9; extra == 'all'
Requires-Dist: psycopg[binary]>=3.2; extra == 'all'
Requires-Dist: pyyaml>=6.0; extra == 'all'
Requires-Dist: questionary>=2.0; extra == 'all'
Requires-Dist: rich>=13.0; extra == 'all'
Requires-Dist: sendgrid>=6.11; extra == 'all'
Requires-Dist: sentry-sdk>=2.0; extra == 'all'
Provides-Extra: channels
Requires-Dist: channels-redis>=4.2; extra == 'channels'
Requires-Dist: channels>=4.2; extra == 'channels'
Provides-Extra: cli
Requires-Dist: click>=8.1; extra == 'cli'
Requires-Dist: jinja2>=3.1; extra == 'cli'
Requires-Dist: pyyaml>=6.0; extra == 'cli'
Requires-Dist: questionary>=2.0; extra == 'cli'
Requires-Dist: rich>=13.0; extra == 'cli'
Provides-Extra: csp
Requires-Dist: django-csp>=4.0b1; extra == 'csp'
Provides-Extra: dev
Requires-Dist: click>=8.1; extra == 'dev'
Requires-Dist: django-stubs>=5.0; extra == 'dev'
Requires-Dist: djangorestframework>=3.15; extra == 'dev'
Requires-Dist: factory-boy>=3.3; extra == 'dev'
Requires-Dist: faker>=25.0; extra == 'dev'
Requires-Dist: jinja2>=3.1; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest-django>=4.8; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: pyyaml>=6.0; extra == 'dev'
Requires-Dist: questionary>=2.0; extra == 'dev'
Requires-Dist: rich>=13.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Provides-Extra: encryption
Requires-Dist: cryptography>=44.0; extra == 'encryption'
Requires-Dist: django-fernet-fields>=0.6; extra == 'encryption'
Provides-Extra: gis
Requires-Dist: psycopg[binary]>=3.2; extra == 'gis'
Provides-Extra: image
Requires-Dist: pillow>=9.0; extra == 'image'
Provides-Extra: monitoring
Requires-Dist: psutil>=5.9; extra == 'monitoring'
Provides-Extra: sendgrid
Requires-Dist: django-anymail>=13.0; extra == 'sendgrid'
Requires-Dist: sendgrid>=6.11; extra == 'sendgrid'
Provides-Extra: sentry
Requires-Dist: sentry-sdk>=2.0; extra == 'sentry'
Description-Content-Type: text/markdown

# nimoh-be-django-base

> **Python ≥ 3.12 · Django ≥ 5.1, < 6.0 · v0.1.0 · MIT**

A reusable Django backend foundation providing production-ready authentication, monitoring,
privacy (GDPR), security headers, and interactive project-scaffolding tooling.
Extract your boilerplate once; wire it into any Django project in minutes.

---

## Table of Contents

1. [Features](#features)
2. [Requirements](#requirements)
3. [Installation](#installation)
4. [Quick Start](#quick-start)
5. [CLI — `nimoh-base`](#cli--nimoh-base)
6. [Extending the User Model](#extending-the-user-model)
7. [URL Reference](#url-reference)
8. [Celery Setup](#celery-setup)
9. [Email Template Overrides](#email-template-overrides)
10. [Configuration Reference](#configuration-reference)
11. [Architecture Decisions](#architecture-decisions)
12. [Further Reading](#further-reading)
    - [Development Guide](docs/DEVELOPMENT_GUIDE.md)
    - [Settings Reference](docs/SETTINGS.md)
    - [Extending the Package](docs/EXTENDING.md)
13. [License](#license)

---

## Features

| Area | What you get |
|------|-------------|
| **Authentication** | JWT (access + refresh), rotating refresh tokens, device sessions, MFA (TOTP), email verification, HIBP password-breach checking, account-lock after failed attempts, audit logging |
| **Monitoring** | HTTP/DB performance-metrics middleware, health-check endpoints (`/health/`, `/health/ready/`), optional DB persistence |
| **Privacy / GDPR** | `PrivacyProfile` per user, consent records, data-processing log, GDPR data-export endpoint |
| **Core** | Abstract base models (`TimeStampedModel`, `SoftDeleteModel`), security-headers middleware, SQL-injection guard, custom DRF throttling classes, standardised exception handler |
| **Config helpers** | `NimohBaseSettings` — composable blocks for `INSTALLED_APPS`, `MIDDLEWARE`, `REST_FRAMEWORK`, `SIMPLE_JWT`, `CACHES`, `LOGGING`; `nimoh_base_urlpatterns()` one-liner URL registration; `make_celery()` factory |
| **CLI** | `nimoh-base init` — interactive project scaffolding; `nimoh-base check` — settings validation; `nimoh-base config-template` — dump a ready-to-edit `NIMOH_BASE` snippet |

---

## Requirements

| Dependency | Version |
|-----------|---------|
| Python | ≥ 3.12 |
| Django | ≥ 5.1, < 6.0 |
| djangorestframework | ≥ 3.15 |
| djangorestframework-simplejwt | ≥ 5.3 |
| Redis | any recent version (caching + Celery broker) |
| PostgreSQL | recommended (any Django-supported DB works for basic usage) |

---

## Installation

### From PyPI

```bash
pip install nimoh-be-django-base
```

### From GitHub (SSH — for private repos / latest `main`)

```bash
pip install "git+ssh://git@github.com/ThriledLokki983/nimoh-be-django-base.git"
```

### Editable install (local development)

```bash
git clone git@github.com:ThriledLokki983/nimoh-be-django-base.git
pip install -e "nimoh-be-django-base[dev]"
```

### Optional extras

```bash
pip install "nimoh-be-django-base[sendgrid]"  # SendGrid email backend
pip install "nimoh-be-django-base[gis]"        # PostGIS / GeoDjango support
pip install "nimoh-be-django-base[cli]"        # Interactive CLI scaffolding
pip install "nimoh-be-django-base[all]"        # Everything above
```

---

## Quick Start

### Step 1 — Add to `INSTALLED_APPS`

```python
# config/settings/base.py
from nimoh_base.conf import NimohBaseSettings

INSTALLED_APPS = NimohBaseSettings.get_base_apps() + [
    "myproject.profiles",
    "myproject.myapp",
]
```

`get_base_apps()` returns the four package apps in dependency order
(`nimoh_core`, `nimoh_auth`, `nimoh_monitoring`, `nimoh_privacy`) plus all
required third-party apps (`rest_framework`, `corsheaders`, `drf_spectacular`, …).

### Step 2 — Configure `NIMOH_BASE`

```python
NIMOH_BASE = {
    # ── Required ──────────────────────────────────────────────────
    "SITE_NAME": "My Application",
    "SUPPORT_EMAIL": "support@myapp.com",
    "NOREPLY_EMAIL": "noreply@myapp.com",
    # ── Optional (shown with defaults) ────────────────────────────
    "SERVER_HEADER": "",                       # omit HTTP Server header if empty
    "PASSWORD_CHECKER_USER_AGENT": "Django-Password-Validator",
    "CELERY_APP_NAME": "django-app",
    "CACHE_KEY_PREFIX": "app",
    "MOBILE_APP_IDENTIFIERS": [],              # e.g. ["my-ios", "my-android"]
    "ENABLE_MONITORING_PERSISTENCE": False,    # write metrics to DB
}
```

See [docs/SETTINGS.md](docs/SETTINGS.md) for the full reference.

### Step 3 — Middleware, REST framework, auth model

```python
MIDDLEWARE = NimohBaseSettings.get_base_middleware()
REST_FRAMEWORK = NimohBaseSettings.get_base_rest_framework()

# Must subclass AbstractNimohUser — see "Extending the User Model" section
AUTH_USER_MODEL = "profiles.User"
```

### Step 4 — URL patterns

```python
# config/urls.py
from django.contrib import admin
from django.urls import path
from nimoh_base.conf.urls import nimoh_base_urlpatterns

urlpatterns = [
    path("admin/", admin.site.urls),
    *nimoh_base_urlpatterns(api_prefix="api/v1/"),
    # …your project-specific routes
]
```

### Step 5 — Migrate

```bash
python manage.py migrate
```

---

## CLI — `nimoh-base`

Install the `[cli]` extra then run:

```bash
# Scaffold a new project interactively (creates settings, urls, celery, user model)
nimoh-base init

# Validate that all required NIMOH_BASE keys are present and correctly typed
nimoh-base check

# Print a ready-to-copy NIMOH_BASE config block
nimoh-base config-template
```

### `nimoh-base init` walkthrough

```
$ nimoh-base init
? Project name: My Cool App
? Support email: support@mycoolapp.com
? No-reply email: noreply@mycoolapp.com
? Enable PostGIS / geo support? No
? Enable monitoring DB persistence? No

✅  Creating project: my-cool-app
    config/settings/base.py         written
    config/settings/development.py  written
    config/urls.py                  written
    myapp/celery.py                 written
    profiles/models.py              written (User stub)
    requirements.txt                written
```

---

## Extending the User Model

**Always subclass `AbstractNimohUser`**, even if you add zero extra fields.
This keeps Django's auth swappable machinery intact.

```python
# profiles/models.py
from nimoh_base.auth.models import AbstractNimohUser
from django.db import models

class User(AbstractNimohUser):
    '''Project-specific user model — add custom fields here.'''

    bio = models.TextField(blank=True)
    avatar_url = models.URLField(blank=True)

    class Meta(AbstractNimohUser.Meta):
        swappable = "AUTH_USER_MODEL"
```

### `AbstractNimohUser` built-in fields

| Field | Type | Notes |
|-------|------|-------|
| `email` | `EmailField(unique=True)` | `USERNAME_FIELD` |
| `username` | `CharField(max_length=150)` | in `REQUIRED_FIELDS` |
| `email_verified` | `BooleanField(default=False)` | set by email-verify flow |
| `email_verified_at` | `DateTimeField(null=True)` | timestamp of first verify |
| `failed_login_attempts` | `PositiveIntegerField(default=0)` | incremented on auth failure |
| `locked_until` | `DateTimeField(null=True)` | cleared by `unlock_account()` |

### Account-lock helpers

```python
user.is_account_locked()                  # → bool
user.lock_account(duration_minutes=30)    # sets locked_until
user.unlock_account()                     # clears the lock
user.record_failed_login()                # increments counter; may auto-lock
user.record_successful_login()            # resets counter
```

> **Important:** set `AUTH_USER_MODEL` in settings **before** running the first
> `migrate`. Changing it afterwards requires a database rebuild.

See [docs/EXTENDING.md](docs/EXTENDING.md) for advanced patterns.

---

## URL Reference

`nimoh_base_urlpatterns()` mounts the following routes under `<api_prefix>`:

| Mount path | App | Description |
|-----------|-----|-------------|
| `auth/` | `nimoh_auth` | Registration, login, refresh, logout, email verify, password reset, MFA, device sessions |
| `monitoring/` | `nimoh_monitoring` | Performance metrics, health-check aggregates |
| `privacy/` | `nimoh_privacy` | GDPR profile, consent, data-processing log, data export |
| `health/` | `nimoh_core` | Liveness (`/health/`) and readiness (`/health/ready/`) probes |
| `schema/` | drf-spectacular | OpenAPI schema download |
| `schema/ui/` | drf-spectacular | Swagger UI |

```python
# Selective registration — disable what you don't need
urlpatterns = [
    *nimoh_base_urlpatterns(
        api_prefix="api/v1/",
        include_auth=True,
        include_monitoring=False,   # opt-out monitoring routes
        include_privacy=True,
        include_schema=True,
        include_health=True,
    ),
]
```

---

## Celery Setup

```python
# myproject/celery.py
import os
from nimoh_base.conf.celery import make_celery

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.development")
app = make_celery()   # reads NIMOH_BASE['CELERY_APP_NAME'] for the Celery app name
```

```python
# config/__init__.py
from .celery import app as celery_app
__all__ = ("celery_app",)
```

The Celery app is configured automatically from Django settings — no extra
`app.config_from_object()` call needed.

---

## Email Template Overrides

The package ships base templates under:

- `nimoh_base/templates/emails/auth/` — verification, password-reset, account-lock
- `nimoh_base/templates/emails/privacy/` — GDPR export, data-deletion confirmation

Override any template by placing a file at the **same relative path** inside
your project's template directory:

```
myproject/
└── templates/
    └── emails/
        └── auth/
            └── email_verification_email.html   ← overrides the package default
```

Django's template loader checks `TEMPLATES[*]['DIRS']` before installed-app
templates — **no custom loader, no monkey-patching required**.

---

## Configuration Reference

Full annotated reference: [docs/SETTINGS.md](docs/SETTINGS.md)

| Key | Required | Default | Description |
|-----|:--------:|---------|-------------|
| `SITE_NAME` | ✅ | — | Application display name; injected into all outbound emails |
| `SUPPORT_EMAIL` | ✅ | — | Support address embedded in auth and GDPR emails |
| `NOREPLY_EMAIL` | ✅ | — | `From:` address for all system emails |
| `SERVER_HEADER` | ❌ | `''` | Value for HTTP `Server` response header; header omitted if empty |
| `PASSWORD_CHECKER_USER_AGENT` | ❌ | `'Django-Password-Validator'` | `User-Agent` sent to the HIBP Pwned Passwords API |
| `CELERY_APP_NAME` | ❌ | `'django-app'` | Name passed to `Celery(name)` constructor |
| `CACHE_KEY_PREFIX` | ❌ | `'app'` | Prefix for all Redis cache keys |
| `MOBILE_APP_IDENTIFIERS` | ❌ | `[]` | Extra `User-Agent` substrings treated as mobile clients |
| `ENABLE_MONITORING_PERSISTENCE` | ❌ | `False` | When `True`, HTTP/DB metrics are written to `nimoh_monitoring` tables |

---

## Architecture Decisions

**D1 — Single `NIMOH_BASE` dict over many discrete settings**
All package config lives under one key. Consumer projects can diff/grep/override
the full package surface at a glance, and `nimoh_base.conf` validates it at
`AppConfig.ready()` with a clear `ImproperlyConfigured` error if required keys are absent.

**D2 — `AbstractNimohUser` over a concrete swappable model**
Consumers must supply their own concrete `User` subclass (even an empty one).
This keeps `AUTH_USER_MODEL` flexible and removes the traditional "I can't add
fields because the package owns the model" friction.

**D3 — Prefixed app labels (`nimoh_auth`, `nimoh_core`, …)**
Avoids silent collisions with common generic names (`core`, `authentication`)
that many consumer projects already use. Migration history from older apps can be
redirected via `MIGRATION_MODULES`.

**D4 — Django's native template-loader override for emails**
No custom template backend. Override works via standard `TEMPLATES['DIRS']`
ordering — the same mental model every Django developer already has.

**D5 — Optional extras for heavy dependencies**
`sendgrid`, `gis`, and `cli` are opt-in. A project that only needs auth +
monitoring doesn't pull in PostGIS binaries or Click/Questionary.

---

## Further Reading

| Document | Contents |
|---------|----------|
| [docs/GETTING_STARTED.md](docs/GETTING_STARTED.md) | Scaffold a new project from scratch; first-run setup |
| [docs/DEVELOPMENT_GUIDE.md](docs/DEVELOPMENT_GUIDE.md) | Day-to-day development: models, views, tasks, tests, logging |
| [docs/SETTINGS.md](docs/SETTINGS.md) | Full `NIMOH_BASE` key reference with types, validation rules, and examples |
| [docs/EXTENDING.md](docs/EXTENDING.md) | Subclassing `AbstractNimohUser`, custom serializers/views, middleware extension |
| [docs/MIGRATION_GUIDE.md](docs/MIGRATION_GUIDE.md) | Step-by-step: vanilla Django project → nimoh-be-django-base consumer |
| [CHANGELOG.md](CHANGELOG.md) | Version history and breaking-change notes |

---

## License

MIT
