django-backupgram 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. django_backupgram-0.1.0/.github/workflows/ci-cd.yml +65 -0
  2. django_backupgram-0.1.0/.gitignore +143 -0
  3. django_backupgram-0.1.0/CHANGELOG.md +60 -0
  4. django_backupgram-0.1.0/LICENSE +21 -0
  5. django_backupgram-0.1.0/Makefile +41 -0
  6. django_backupgram-0.1.0/PKG-INFO +161 -0
  7. django_backupgram-0.1.0/README.md +134 -0
  8. django_backupgram-0.1.0/example/README.md +33 -0
  9. django_backupgram-0.1.0/example/config/__init__.py +0 -0
  10. django_backupgram-0.1.0/example/config/settings.py +68 -0
  11. django_backupgram-0.1.0/example/config/urls.py +8 -0
  12. django_backupgram-0.1.0/example/config/wsgi.py +7 -0
  13. django_backupgram-0.1.0/example/manage.py +24 -0
  14. django_backupgram-0.1.0/example/seed.py +48 -0
  15. django_backupgram-0.1.0/pyproject.toml +74 -0
  16. django_backupgram-0.1.0/scripts/create_example_project.sh +119 -0
  17. django_backupgram-0.1.0/scripts/release.sh +49 -0
  18. django_backupgram-0.1.0/src/backupgram/__init__.py +1 -0
  19. django_backupgram-0.1.0/src/backupgram/admin.py +414 -0
  20. django_backupgram-0.1.0/src/backupgram/apps.py +6 -0
  21. django_backupgram-0.1.0/src/backupgram/client.py +124 -0
  22. django_backupgram-0.1.0/src/backupgram/config_meta.py +137 -0
  23. django_backupgram-0.1.0/src/backupgram/fields.py +36 -0
  24. django_backupgram-0.1.0/src/backupgram/forms.py +54 -0
  25. django_backupgram-0.1.0/src/backupgram/migrations/0001_initial.py +59 -0
  26. django_backupgram-0.1.0/src/backupgram/migrations/__init__.py +0 -0
  27. django_backupgram-0.1.0/src/backupgram/models.py +37 -0
  28. django_backupgram-0.1.0/src/backupgram/schedule.py +110 -0
  29. django_backupgram-0.1.0/src/backupgram/static/backupgram/admin.css +334 -0
  30. django_backupgram-0.1.0/src/backupgram/static/backupgram/admin.js +100 -0
  31. django_backupgram-0.1.0/src/backupgram/static/backupgram/vendor/cronstrue.LICENSE.txt +25 -0
  32. django_backupgram-0.1.0/src/backupgram/static/backupgram/vendor/cronstrue.min.js +1 -0
  33. django_backupgram-0.1.0/src/backupgram/templates/backupgram/admin/_assets.html +7 -0
  34. django_backupgram-0.1.0/src/backupgram/templates/backupgram/admin/backups.html +74 -0
  35. django_backupgram-0.1.0/src/backupgram/templates/backupgram/admin/config.html +95 -0
  36. django_backupgram-0.1.0/src/backupgram/templates/backupgram/admin/dashboard.html +157 -0
  37. django_backupgram-0.1.0/src/backupgram/templates/backupgram/admin/job_detail.html +70 -0
  38. django_backupgram-0.1.0/src/backupgram/templates/backupgram/admin/jobs.html +63 -0
  39. django_backupgram-0.1.0/src/backupgram/templates/backupgram/admin/restore.html +52 -0
  40. django_backupgram-0.1.0/src/backupgram/templatetags/__init__.py +0 -0
  41. django_backupgram-0.1.0/src/backupgram/templatetags/bg_extras.py +43 -0
  42. django_backupgram-0.1.0/src/backupgram/validators.py +16 -0
  43. django_backupgram-0.1.0/tests/__init__.py +0 -0
  44. django_backupgram-0.1.0/tests/conftest.py +1 -0
  45. django_backupgram-0.1.0/tests/settings.py +33 -0
  46. django_backupgram-0.1.0/tests/test_admin_backups.py +113 -0
  47. django_backupgram-0.1.0/tests/test_admin_config.py +108 -0
  48. django_backupgram-0.1.0/tests/test_admin_jobs.py +121 -0
  49. django_backupgram-0.1.0/tests/test_admin_registration.py +70 -0
  50. django_backupgram-0.1.0/tests/test_client.py +140 -0
  51. django_backupgram-0.1.0/tests/test_fields.py +34 -0
  52. django_backupgram-0.1.0/tests/test_models.py +39 -0
  53. django_backupgram-0.1.0/tests/test_schedule.py +45 -0
  54. django_backupgram-0.1.0/tests/test_smoke.py +4 -0
  55. django_backupgram-0.1.0/tests/test_templatetags.py +20 -0
  56. django_backupgram-0.1.0/tests/test_validators.py +33 -0
  57. django_backupgram-0.1.0/tests/urls.py +4 -0
  58. django_backupgram-0.1.0/uv.lock +602 -0
@@ -0,0 +1,65 @@
1
+ name: CI/CD
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ tags:
7
+ - "v*"
8
+ pull_request:
9
+ branches: [main]
10
+
11
+ jobs:
12
+ test:
13
+ name: Test
14
+ runs-on: ubuntu-latest
15
+
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ python-version: ["3.11", "3.12", "3.13"]
20
+ django-version: ["5.2", "6.0"]
21
+
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+
25
+ - uses: astral-sh/setup-uv@v5
26
+ with:
27
+ enable-cache: true
28
+
29
+ - name: Setup Python
30
+ env:
31
+ PYTHON_VERSION: "${{ matrix.python-version }}"
32
+ run: uv python install "$PYTHON_VERSION"
33
+
34
+ - name: Install dependencies
35
+ env:
36
+ DJANGO_VERSION: "${{ matrix.django-version }}"
37
+ run: |
38
+ uv sync --group dev
39
+ uv pip install "Django~=${DJANGO_VERSION}.0"
40
+
41
+ - name: Lint
42
+ run: uv run ruff check .
43
+
44
+ - name: Test
45
+ run: uv run pytest --cov
46
+
47
+ publish:
48
+ name: Publish to PyPI
49
+ needs: test
50
+ runs-on: ubuntu-latest
51
+ if: startsWith(github.ref, 'refs/tags/v')
52
+ environment: pypi
53
+ permissions:
54
+ id-token: write
55
+
56
+ steps:
57
+ - uses: actions/checkout@v4
58
+
59
+ - uses: astral-sh/setup-uv@v5
60
+
61
+ - name: Build
62
+ run: uv build
63
+
64
+ - name: Publish
65
+ run: uv publish --trusted-publishing always
@@ -0,0 +1,143 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ *.egg-info/
24
+ .installed.cfg
25
+ *.egg
26
+ MANIFEST
27
+
28
+ # PyInstaller
29
+ # Usually these files are written by a python script from a template
30
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
31
+ *.manifest
32
+ *.spec
33
+
34
+ # Installer logs
35
+ pip-log.txt
36
+ pip-delete-this-directory.txt
37
+
38
+ # Unit test / coverage reports
39
+ htmlcov/
40
+ .tox/
41
+ .coverage
42
+ .coverage.*
43
+ .cache
44
+ nosetests.xml
45
+ coverage.xml
46
+ *.cover
47
+ .hypothesis/
48
+ .pytest_cache/
49
+
50
+ # Translations
51
+ *.mo
52
+ *.pot
53
+
54
+ # Django stuff:
55
+ *.log
56
+ local_settings.py
57
+ db.sqlite3
58
+ db.sqlite3-journal
59
+ media/
60
+
61
+ # Flask stuff:
62
+ instance/
63
+ .webassets-cache
64
+
65
+ # Scrapy stuff:
66
+ .scrapy
67
+
68
+ # Sphinx documentation
69
+ docs/_build/
70
+
71
+ # PyBuilder
72
+ target/
73
+
74
+ # Jupyter Notebook
75
+ .ipynb_checkpoints
76
+
77
+ # pyenv
78
+ .python-version
79
+
80
+ # celery beat schedule file
81
+ celerybeat-schedule
82
+
83
+ # SageMath parsed files
84
+ *.sage.py
85
+
86
+ # Environments
87
+ .env
88
+ .venv
89
+ env/
90
+ venv/
91
+ ENV/
92
+ env.bak/
93
+ venv.bak/
94
+
95
+ # Spyder project settings
96
+ .spyderproject
97
+ .spyproject
98
+
99
+ # Rope project settings
100
+ .ropeproject
101
+
102
+ # mkdocs documentation
103
+ /site
104
+
105
+ # mypy
106
+ .mypy_cache/
107
+ .dmypy.json
108
+ dmypy.json
109
+
110
+ # Pyre type checker
111
+ .pyre/
112
+
113
+ # pytype static type analyzer
114
+ .pytype/
115
+
116
+ # IDE
117
+ .idea/
118
+ .vscode/
119
+ *.swp
120
+ *.swo
121
+
122
+ # uv specific
123
+ .uv/
124
+ __pypackages__/
125
+
126
+ # Poetry specific
127
+ poetry.lock
128
+
129
+ # macOS
130
+ .DS_Store
131
+
132
+ # Windows
133
+ Thumbs.db
134
+ ehthumbs.db
135
+ Desktop.ini
136
+
137
+ # Coverage.py
138
+ .coverage.*
139
+ coverage.xml
140
+ htmlcov/
141
+
142
+ # Example project runtime
143
+ example/db.sqlite3
@@ -0,0 +1,60 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-06-07
11
+
12
+ Initial release — a Django admin control panel for the `backupgram`
13
+ (postgres-backup) REST API. It is a thin, well-tested client: it performs no
14
+ backups itself, it drives one or more backup containers over their REST API.
15
+
16
+ ### Added
17
+
18
+ - **`BackupServer` model** — register one or more backup servers (`base_url`,
19
+ bearer `token`, `verify_tls`, `timeout`, `enabled`). The token is **encrypted
20
+ at rest** with Fernet (key derived from `SECRET_KEY`) and is **write-only** in
21
+ admin (never rendered). `base_url` accepts single-label hosts, e.g.
22
+ `https://backup:8081` (Docker service names).
23
+ - **`BackupgramClient`** — typed `httpx` wrapper over every REST endpoint
24
+ (status, list/download/delete backups, trigger backup, restore from a stored
25
+ file or a Telegram message id, jobs, runtime config) with a `BackupgramAPIError`
26
+ that carries the HTTP status and parses the API's `{"error": ...}` envelope.
27
+ - **Admin dashboard** — server overview with a live connection indicator, KPI
28
+ cards (schedule with a human-readable cron description, last backup, next
29
+ backup), a status & configuration card grid (upload mode, Telegram / MTProto
30
+ state, cluster, previous backup), misconfiguration **warnings** (e.g. upload
31
+ method `mtproto` without API credentials), and a recent-backups panel.
32
+ - **Backups page** — list, stream-download, and delete (with confirmation),
33
+ human-readable sizes and relative times.
34
+ - **Jobs** — recent-jobs list and a live job-detail view that auto-refreshes
35
+ while a job is running.
36
+ - **Restore page** — restore from a stored file or a Telegram message id;
37
+ clicking an available backup fills the file field. Server-side `target_db`
38
+ validation and explicit confirmation are required.
39
+ - **Runtime config** — settings grouped into sections (Schedule / Retention /
40
+ Databases / Telegram / Webhook), each rendered with the right widget: enum
41
+ **selects** with per-value help (including `TELEGRAM_UPLOAD_METHOD`), numbers,
42
+ write-only secrets, and a **live cron editor** (bundled cronstrue, MIT) with
43
+ preset shortcuts. Only whitelisted keys are editable; secrets are masked.
44
+ - **Next-run calculation** — computed in pure standard library (no external
45
+ cron dependency), supporting 5-field cron and `@shortcut` schedules.
46
+ - **Theming** — a data-dense dashboard UI that follows Django admin's light/dark
47
+ theme; CSS and JS are bundled and shipped in the wheel.
48
+ - **Packaging** — `src/` layout, Python 3.11–3.13 / Django 5.2+, PyPI
49
+ trusted-publishing release workflow, and an `example/` project for local
50
+ testing against a running backup container.
51
+
52
+ ### Security
53
+
54
+ - Server tokens are encrypted at rest and never returned to the browser; all
55
+ REST calls happen server-side. Admin views are permission-gated, destructive
56
+ actions are POST + CSRF + explicit confirm, and config secrets are masked
57
+ (write-only). **Note:** rotating `SECRET_KEY` invalidates stored tokens.
58
+
59
+ [Unreleased]: https://github.com/ganiyevuz/django-backupgram/compare/v0.1.0...HEAD
60
+ [0.1.0]: https://github.com/ganiyevuz/django-backupgram/releases/tag/v0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jakhongir Ganiev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,41 @@
1
+ .PHONY: help venv install lint format test coverage dist publish release example clean
2
+
3
+ help: ## Show available commands
4
+ @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " %-12s %s\n", $$1, $$2}'
5
+
6
+ venv: ## Create virtual environment
7
+ uv venv
8
+
9
+ install: ## Install with dev dependencies
10
+ uv sync --group dev
11
+
12
+ lint: ## Check code style
13
+ uv run ruff check .
14
+ uv run ruff format --check .
15
+
16
+ format: ## Format code
17
+ uv run ruff check --fix .
18
+ uv run ruff format .
19
+
20
+ test: ## Run tests
21
+ uv run pytest
22
+
23
+ coverage: ## Run tests with coverage
24
+ uv run pytest --cov
25
+
26
+ dist: clean ## Build package
27
+ uv build
28
+
29
+ publish: dist ## Publish to PyPI
30
+ uv publish
31
+
32
+ example: ## Create example Django project
33
+ @./scripts/create_example_project.sh
34
+
35
+ release: lint test ## Release a version (usage: make release v=0.2.0)
36
+ @./scripts/release.sh $(v)
37
+
38
+ clean: ## Remove build artifacts
39
+ rm -rf build/ dist/ *.egg-info
40
+ find . -type d -name __pycache__ -exec rm -rf {} +
41
+ find . -type f -name '*.py[co]' -delete
@@ -0,0 +1,161 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-backupgram
3
+ Version: 0.1.0
4
+ Summary: Django admin control panel for the backupgram REST API (PostgreSQL backups over HTTP).
5
+ Project-URL: Homepage, https://github.com/ganiyevuz/django-backupgram
6
+ Project-URL: Repository, https://github.com/ganiyevuz/django-backupgram
7
+ Author-email: Jakhongir Ganiev <contact@jakhongir.dev>
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: admin,backup,backupgram,django,postgres
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Web Environment
13
+ Classifier: Framework :: Django
14
+ Classifier: Framework :: Django :: 5.2
15
+ Classifier: Framework :: Django :: 6.0
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: cryptography>=42
23
+ Requires-Dist: django-jazzmin>=3.0.4
24
+ Requires-Dist: django>=5.2
25
+ Requires-Dist: httpx>=0.27
26
+ Description-Content-Type: text/markdown
27
+
28
+ # django-backupgram
29
+
30
+ A Django admin control panel for the [backupgram REST API](https://github.com/ganiyevuz/backupgram).
31
+
32
+ ## What it is
33
+
34
+ `django-backupgram` is a thin Django admin client. It talks to one or more
35
+ [backupgram](https://github.com/ganiyevuz/backupgram)
36
+ containers over their REST API, letting you:
37
+
38
+ - **Trigger backups** on demand
39
+ - **Browse and download** backup files
40
+ - **Delete** old backups
41
+ - **Restore** a backup to a target database
42
+ - **Watch async jobs** (restore, backup) as they run
43
+ - **Edit runtime config** of the backup container
44
+
45
+ It does **not** perform backups itself — the backup logic lives entirely inside
46
+ the containers. `django-backupgram` is only the admin UI that drives them.
47
+
48
+ ## Requirements
49
+
50
+ - Python 3.11 – 3.13
51
+ - Django 5.2+
52
+ - One or more backup containers reachable from the Django server with
53
+ `REST_API_ENABLE=TRUE` and a `REST_API_TOKEN` set
54
+
55
+ See the backup container's
56
+ [REST API docs](https://github.com/ganiyevuz/backupgram/blob/main/docs/REST_API.md)
57
+ for how to enable the API.
58
+
59
+ ## Install
60
+
61
+ ```bash
62
+ pip install django-backupgram
63
+ ```
64
+
65
+ ## Setup
66
+
67
+ 1. Add `"backupgram"` to `INSTALLED_APPS`:
68
+
69
+ ```python
70
+ INSTALLED_APPS = [
71
+ ...
72
+ "backupgram",
73
+ ]
74
+ ```
75
+
76
+ 2. Run migrations:
77
+
78
+ ```bash
79
+ python manage.py migrate
80
+ ```
81
+
82
+ 3. Open Django admin → **Backup servers** → **Add**, and fill in:
83
+ - **Name** — a friendly label (e.g. `production`)
84
+ - **Base URL** — the backup container's REST API endpoint, e.g.
85
+ `https://backup:8081`. Docker service names are allowed (the request is
86
+ made server-side).
87
+ - **Token** — the bearer token configured via `REST_API_TOKEN` on the container
88
+ (see below).
89
+
90
+ ### The API token
91
+
92
+ The token is the backup container's single admin bearer key — you generate it
93
+ yourself and set it on the container, then paste the **same** value into the
94
+ **Token** field here.
95
+
96
+ Generate a high-entropy value (≥ 32 bytes of randomness):
97
+
98
+ ```bash
99
+ openssl rand -base64 48
100
+ # or
101
+ python3 -c "import secrets; print(secrets.token_urlsafe(48))"
102
+ ```
103
+
104
+ Set it on the backup container via `REST_API_TOKEN` (or, recommended,
105
+ `REST_API_TOKEN_FILE` pointing at a Docker secret), then enter the same token in
106
+ admin. The container compares it in constant time and refuses to start if the API
107
+ is enabled without one; here it is stored **encrypted at rest** and sent only
108
+ server-side. To **rotate**: change it on the container, recreate it, and update
109
+ the Token field. See the
110
+ [REST API auth docs](https://github.com/ganiyevuz/backupgram/blob/main/docs/REST_API.md#authentication).
111
+
112
+ ## Usage
113
+
114
+ From the **Backup servers** changelist, each server row shows a reachability
115
+ badge and links to its dashboard. The dashboard provides:
116
+
117
+ | Action | Notes |
118
+ |--------|-------|
119
+ | **Run backup** | Triggers an immediate backup |
120
+ | **Backups** | Lists all backup files; download or delete individual files |
121
+ | **Restore** | Select a file, enter the target database name, confirm |
122
+ | **Jobs** | Live view of async jobs (backup/restore in progress) |
123
+ | **Config** | View and edit the container's runtime configuration |
124
+
125
+ Long-running operations (restore, triggered backup) run as async jobs inside
126
+ the backup container. Refresh the Jobs page to see progress and final status.
127
+
128
+ ## Security
129
+
130
+ - The bearer token is stored **encrypted at rest** using Fernet symmetric
131
+ encryption. The key is derived from Django's `SECRET_KEY`.
132
+ - The token is **write-only** in the admin interface — it is never rendered back
133
+ into any form or page.
134
+ - **Rotating `SECRET_KEY` invalidates all stored tokens.** After a key rotation,
135
+ re-enter each server's token in the admin.
136
+ - All REST calls are made **server-side** — the token never reaches the browser.
137
+ - Put the backup container's REST API behind **TLS and a reverse proxy**. Do not
138
+ expose it directly on a public network.
139
+
140
+ ## Optional settings
141
+
142
+ Add to your Django settings to override defaults:
143
+
144
+ ```python
145
+ # Timeout (seconds) for the reachability check shown in the changelist badge.
146
+ # Default: 3
147
+ BACKUPGRAM_REACHABILITY_TIMEOUT = 3
148
+ ```
149
+
150
+ ## Development
151
+
152
+ ```bash
153
+ uv sync
154
+ uv run pytest
155
+ ```
156
+
157
+ ## Links
158
+
159
+ - Backup container: <https://github.com/ganiyevuz/backupgram>
160
+ - REST API reference: <https://github.com/ganiyevuz/backupgram/blob/main/docs/REST_API.md>
161
+ - This package: <https://github.com/ganiyevuz/django-backupgram>
@@ -0,0 +1,134 @@
1
+ # django-backupgram
2
+
3
+ A Django admin control panel for the [backupgram REST API](https://github.com/ganiyevuz/backupgram).
4
+
5
+ ## What it is
6
+
7
+ `django-backupgram` is a thin Django admin client. It talks to one or more
8
+ [backupgram](https://github.com/ganiyevuz/backupgram)
9
+ containers over their REST API, letting you:
10
+
11
+ - **Trigger backups** on demand
12
+ - **Browse and download** backup files
13
+ - **Delete** old backups
14
+ - **Restore** a backup to a target database
15
+ - **Watch async jobs** (restore, backup) as they run
16
+ - **Edit runtime config** of the backup container
17
+
18
+ It does **not** perform backups itself — the backup logic lives entirely inside
19
+ the containers. `django-backupgram` is only the admin UI that drives them.
20
+
21
+ ## Requirements
22
+
23
+ - Python 3.11 – 3.13
24
+ - Django 5.2+
25
+ - One or more backup containers reachable from the Django server with
26
+ `REST_API_ENABLE=TRUE` and a `REST_API_TOKEN` set
27
+
28
+ See the backup container's
29
+ [REST API docs](https://github.com/ganiyevuz/backupgram/blob/main/docs/REST_API.md)
30
+ for how to enable the API.
31
+
32
+ ## Install
33
+
34
+ ```bash
35
+ pip install django-backupgram
36
+ ```
37
+
38
+ ## Setup
39
+
40
+ 1. Add `"backupgram"` to `INSTALLED_APPS`:
41
+
42
+ ```python
43
+ INSTALLED_APPS = [
44
+ ...
45
+ "backupgram",
46
+ ]
47
+ ```
48
+
49
+ 2. Run migrations:
50
+
51
+ ```bash
52
+ python manage.py migrate
53
+ ```
54
+
55
+ 3. Open Django admin → **Backup servers** → **Add**, and fill in:
56
+ - **Name** — a friendly label (e.g. `production`)
57
+ - **Base URL** — the backup container's REST API endpoint, e.g.
58
+ `https://backup:8081`. Docker service names are allowed (the request is
59
+ made server-side).
60
+ - **Token** — the bearer token configured via `REST_API_TOKEN` on the container
61
+ (see below).
62
+
63
+ ### The API token
64
+
65
+ The token is the backup container's single admin bearer key — you generate it
66
+ yourself and set it on the container, then paste the **same** value into the
67
+ **Token** field here.
68
+
69
+ Generate a high-entropy value (≥ 32 bytes of randomness):
70
+
71
+ ```bash
72
+ openssl rand -base64 48
73
+ # or
74
+ python3 -c "import secrets; print(secrets.token_urlsafe(48))"
75
+ ```
76
+
77
+ Set it on the backup container via `REST_API_TOKEN` (or, recommended,
78
+ `REST_API_TOKEN_FILE` pointing at a Docker secret), then enter the same token in
79
+ admin. The container compares it in constant time and refuses to start if the API
80
+ is enabled without one; here it is stored **encrypted at rest** and sent only
81
+ server-side. To **rotate**: change it on the container, recreate it, and update
82
+ the Token field. See the
83
+ [REST API auth docs](https://github.com/ganiyevuz/backupgram/blob/main/docs/REST_API.md#authentication).
84
+
85
+ ## Usage
86
+
87
+ From the **Backup servers** changelist, each server row shows a reachability
88
+ badge and links to its dashboard. The dashboard provides:
89
+
90
+ | Action | Notes |
91
+ |--------|-------|
92
+ | **Run backup** | Triggers an immediate backup |
93
+ | **Backups** | Lists all backup files; download or delete individual files |
94
+ | **Restore** | Select a file, enter the target database name, confirm |
95
+ | **Jobs** | Live view of async jobs (backup/restore in progress) |
96
+ | **Config** | View and edit the container's runtime configuration |
97
+
98
+ Long-running operations (restore, triggered backup) run as async jobs inside
99
+ the backup container. Refresh the Jobs page to see progress and final status.
100
+
101
+ ## Security
102
+
103
+ - The bearer token is stored **encrypted at rest** using Fernet symmetric
104
+ encryption. The key is derived from Django's `SECRET_KEY`.
105
+ - The token is **write-only** in the admin interface — it is never rendered back
106
+ into any form or page.
107
+ - **Rotating `SECRET_KEY` invalidates all stored tokens.** After a key rotation,
108
+ re-enter each server's token in the admin.
109
+ - All REST calls are made **server-side** — the token never reaches the browser.
110
+ - Put the backup container's REST API behind **TLS and a reverse proxy**. Do not
111
+ expose it directly on a public network.
112
+
113
+ ## Optional settings
114
+
115
+ Add to your Django settings to override defaults:
116
+
117
+ ```python
118
+ # Timeout (seconds) for the reachability check shown in the changelist badge.
119
+ # Default: 3
120
+ BACKUPGRAM_REACHABILITY_TIMEOUT = 3
121
+ ```
122
+
123
+ ## Development
124
+
125
+ ```bash
126
+ uv sync
127
+ uv run pytest
128
+ ```
129
+
130
+ ## Links
131
+
132
+ - Backup container: <https://github.com/ganiyevuz/backupgram>
133
+ - REST API reference: <https://github.com/ganiyevuz/backupgram/blob/main/docs/REST_API.md>
134
+ - This package: <https://github.com/ganiyevuz/django-backupgram>
@@ -0,0 +1,33 @@
1
+ # Example project
2
+
3
+ A minimal, local-only Django project for manually exercising `django-backupgram`
4
+ against a running backup container. **Not** shipped in the published wheel.
5
+
6
+ ## Prerequisites
7
+
8
+ A reachable backup container with the REST API enabled. The companion repo's
9
+ `docker-compose.local.yml` publishes it on `127.0.0.1:8081` with token `devsecret`:
10
+
11
+ ```sh
12
+ # in the backupgram repo:
13
+ docker compose -f docker-compose.local.yml up -d
14
+ ```
15
+
16
+ ## Run
17
+
18
+ From the `django-backupgram` repo root:
19
+
20
+ ```sh
21
+ uv run python example/manage.py migrate # create the SQLite db
22
+ uv run python example/seed.py # superuser admin/admin + a 'local' BackupServer
23
+ uv run python example/manage.py runserver # http://localhost:8000/admin/
24
+ ```
25
+
26
+ Log in as `admin` / `admin`, open **Backup servers → local**, and use the
27
+ dashboard: Run backup, browse/download/restore backups, watch jobs, edit config.
28
+
29
+ Override the target via env if your container is elsewhere:
30
+
31
+ ```sh
32
+ BACKUPGRAM_DEMO_URL=http://localhost:8081 BACKUPGRAM_DEMO_TOKEN=devsecret uv run python example/seed.py
33
+ ```
File without changes