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.
- django_backupgram-0.1.0/.github/workflows/ci-cd.yml +65 -0
- django_backupgram-0.1.0/.gitignore +143 -0
- django_backupgram-0.1.0/CHANGELOG.md +60 -0
- django_backupgram-0.1.0/LICENSE +21 -0
- django_backupgram-0.1.0/Makefile +41 -0
- django_backupgram-0.1.0/PKG-INFO +161 -0
- django_backupgram-0.1.0/README.md +134 -0
- django_backupgram-0.1.0/example/README.md +33 -0
- django_backupgram-0.1.0/example/config/__init__.py +0 -0
- django_backupgram-0.1.0/example/config/settings.py +68 -0
- django_backupgram-0.1.0/example/config/urls.py +8 -0
- django_backupgram-0.1.0/example/config/wsgi.py +7 -0
- django_backupgram-0.1.0/example/manage.py +24 -0
- django_backupgram-0.1.0/example/seed.py +48 -0
- django_backupgram-0.1.0/pyproject.toml +74 -0
- django_backupgram-0.1.0/scripts/create_example_project.sh +119 -0
- django_backupgram-0.1.0/scripts/release.sh +49 -0
- django_backupgram-0.1.0/src/backupgram/__init__.py +1 -0
- django_backupgram-0.1.0/src/backupgram/admin.py +414 -0
- django_backupgram-0.1.0/src/backupgram/apps.py +6 -0
- django_backupgram-0.1.0/src/backupgram/client.py +124 -0
- django_backupgram-0.1.0/src/backupgram/config_meta.py +137 -0
- django_backupgram-0.1.0/src/backupgram/fields.py +36 -0
- django_backupgram-0.1.0/src/backupgram/forms.py +54 -0
- django_backupgram-0.1.0/src/backupgram/migrations/0001_initial.py +59 -0
- django_backupgram-0.1.0/src/backupgram/migrations/__init__.py +0 -0
- django_backupgram-0.1.0/src/backupgram/models.py +37 -0
- django_backupgram-0.1.0/src/backupgram/schedule.py +110 -0
- django_backupgram-0.1.0/src/backupgram/static/backupgram/admin.css +334 -0
- django_backupgram-0.1.0/src/backupgram/static/backupgram/admin.js +100 -0
- django_backupgram-0.1.0/src/backupgram/static/backupgram/vendor/cronstrue.LICENSE.txt +25 -0
- django_backupgram-0.1.0/src/backupgram/static/backupgram/vendor/cronstrue.min.js +1 -0
- django_backupgram-0.1.0/src/backupgram/templates/backupgram/admin/_assets.html +7 -0
- django_backupgram-0.1.0/src/backupgram/templates/backupgram/admin/backups.html +74 -0
- django_backupgram-0.1.0/src/backupgram/templates/backupgram/admin/config.html +95 -0
- django_backupgram-0.1.0/src/backupgram/templates/backupgram/admin/dashboard.html +157 -0
- django_backupgram-0.1.0/src/backupgram/templates/backupgram/admin/job_detail.html +70 -0
- django_backupgram-0.1.0/src/backupgram/templates/backupgram/admin/jobs.html +63 -0
- django_backupgram-0.1.0/src/backupgram/templates/backupgram/admin/restore.html +52 -0
- django_backupgram-0.1.0/src/backupgram/templatetags/__init__.py +0 -0
- django_backupgram-0.1.0/src/backupgram/templatetags/bg_extras.py +43 -0
- django_backupgram-0.1.0/src/backupgram/validators.py +16 -0
- django_backupgram-0.1.0/tests/__init__.py +0 -0
- django_backupgram-0.1.0/tests/conftest.py +1 -0
- django_backupgram-0.1.0/tests/settings.py +33 -0
- django_backupgram-0.1.0/tests/test_admin_backups.py +113 -0
- django_backupgram-0.1.0/tests/test_admin_config.py +108 -0
- django_backupgram-0.1.0/tests/test_admin_jobs.py +121 -0
- django_backupgram-0.1.0/tests/test_admin_registration.py +70 -0
- django_backupgram-0.1.0/tests/test_client.py +140 -0
- django_backupgram-0.1.0/tests/test_fields.py +34 -0
- django_backupgram-0.1.0/tests/test_models.py +39 -0
- django_backupgram-0.1.0/tests/test_schedule.py +45 -0
- django_backupgram-0.1.0/tests/test_smoke.py +4 -0
- django_backupgram-0.1.0/tests/test_templatetags.py +20 -0
- django_backupgram-0.1.0/tests/test_validators.py +33 -0
- django_backupgram-0.1.0/tests/urls.py +4 -0
- 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
|