From 07e8c7e0476a5ce6048abacb6960b24b0035d523 Mon Sep 17 00:00:00 2001 From: Johannes Erwerle Date: Mon, 6 Apr 2026 22:53:42 +0200 Subject: [PATCH 1/5] fixed typo in registration form --- community_backup/webui/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community_backup/webui/forms.py b/community_backup/webui/forms.py index de62d80..99e774c 100644 --- a/community_backup/webui/forms.py +++ b/community_backup/webui/forms.py @@ -15,7 +15,7 @@ class BorgRepositoryForm(forms.ModelForm): class RegisterUserForm(UserCreationForm): email = forms.EmailField() - voucher = forms.CharField(help_text="You registration voucher.") + voucher = forms.CharField(help_text="Your registration voucher.") def clean_voucher(self): obj = Voucher.objects.filter(code=self.cleaned_data["voucher"], used=False) From bb62b5dde44ea15399c22fa0631aaed4336c0556 Mon Sep 17 00:00:00 2001 From: Johannes Erwerle Date: Thu, 9 Apr 2026 22:13:58 +0200 Subject: [PATCH 2/5] updated handling of the configuration.py file --- .../community_backup/configuration.py | 2 -- .../community_backup/example_configuration.py | 8 ++++++++ community_backup/community_backup/settings.py | 16 +++++++++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) delete mode 100644 community_backup/community_backup/configuration.py create mode 100644 community_backup/community_backup/example_configuration.py diff --git a/community_backup/community_backup/configuration.py b/community_backup/community_backup/configuration.py deleted file mode 100644 index ea8353c..0000000 --- a/community_backup/community_backup/configuration.py +++ /dev/null @@ -1,2 +0,0 @@ -# enter your overrides here -print("foo") diff --git a/community_backup/community_backup/example_configuration.py b/community_backup/community_backup/example_configuration.py new file mode 100644 index 0000000..ca5f4c6 --- /dev/null +++ b/community_backup/community_backup/example_configuration.py @@ -0,0 +1,8 @@ +# enter your overrides here +from pathlib import Path + +BACKUP_HOME_DIR = Path("/data/backups") +BACKUP_BORG_DIR = BACKUP_HOME_DIR / "borg" +BACKUP_AUTHORIZED_KEYS = BACKUP_HOME_DIR / ".ssh" / "authorized_keys" +BACKUP_USER = "borg" +BACKUP_REPO_HOST = "backup.example.com" diff --git a/community_backup/community_backup/settings.py b/community_backup/community_backup/settings.py index 75ca1de..8311aa3 100644 --- a/community_backup/community_backup/settings.py +++ b/community_backup/community_backup/settings.py @@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/6.0/ref/settings/ """ from pathlib import Path +from importlib import import_module # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -127,4 +128,17 @@ STATIC_URL = "static/" TASKS = {"default": {"BACKEND": "django.tasks.backends.immediate.ImmediateBackend"}} -from .configuration import * + +# Import settings from configuration.py +try: + config_module = "community_backup.configuration" + module = import_module(config_module) + + assert module.BACKUP_USER, "The BACKUP_USER setting is required." + assert module.BACKUP_REPO_HOST, "The BACKUP_REPO_HOST setting is required." + assert module.BACKUP_HOME_DIR, "The BACKUP_HOME_DIR setting is required." + assert module.BACKUP_BORG_DIR, "The BACKUP_BORG_DIR setting is required." + assert module.BACKUP_AUTHORIZED_KEYS, "The BACKUP_AUTHORIZED_KEYS setting is required." +except ModuleNotFoundError: + print(f"could not find configuration file {config_module}") + exit(1) From eb787abfa2f5a2f6395c18654b92bd7d9edafd17 Mon Sep 17 00:00:00 2001 From: Johannes Erwerle Date: Mon, 13 Apr 2026 20:09:27 +0200 Subject: [PATCH 3/5] Updated the HTML context of some views. --- .gitignore | 2 ++ community_backup/community_backup/settings.py | 8 ++++++++ community_backup/webui/templates/base.html | 8 +------- community_backup/webui/templates/borg_list.html | 10 ++++++++-- community_backup/webui/templates/landing.html | 6 ++++++ community_backup/webui/urls.py | 2 +- community_backup/webui/views.py | 9 +++++++-- 7 files changed, 33 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 7db414f..6d35582 100644 --- a/.gitignore +++ b/.gitignore @@ -207,3 +207,5 @@ tags [._]*.un~ # End of https://www.toptal.com/developers/gitignore/api/vim,venv,python + +community_backup/community_backup/configuration.py diff --git a/community_backup/community_backup/settings.py b/community_backup/community_backup/settings.py index 8311aa3..8b45b50 100644 --- a/community_backup/community_backup/settings.py +++ b/community_backup/community_backup/settings.py @@ -128,6 +128,7 @@ STATIC_URL = "static/" TASKS = {"default": {"BACKEND": "django.tasks.backends.immediate.ImmediateBackend"}} +STATIC_ROOT = BASE_DIR.parent.parent / "static" # Import settings from configuration.py try: @@ -139,6 +140,13 @@ try: assert module.BACKUP_HOME_DIR, "The BACKUP_HOME_DIR setting is required." assert module.BACKUP_BORG_DIR, "The BACKUP_BORG_DIR setting is required." assert module.BACKUP_AUTHORIZED_KEYS, "The BACKUP_AUTHORIZED_KEYS setting is required." + except ModuleNotFoundError: print(f"could not find configuration file {config_module}") exit(1) + +BACKUP_USER = module.BACKUP_USER +BACKUP_REPO_HOST = module.BACKUP_REPO_HOST +BACKUP_HOME_DIR = module.BACKUP_HOME_DIR +BACKUP_BORG_DIR = module.BACKUP_BORG_DIR +BACKUP_AUTHORIZED_KEYS = module.BACKUP_AUTHORIZED_KEYS diff --git a/community_backup/webui/templates/base.html b/community_backup/webui/templates/base.html index 30bc23c..ffa2b06 100644 --- a/community_backup/webui/templates/base.html +++ b/community_backup/webui/templates/base.html @@ -48,13 +48,7 @@ diff --git a/community_backup/webui/templates/borg_list.html b/community_backup/webui/templates/borg_list.html index ae3b941..b193fcd 100644 --- a/community_backup/webui/templates/borg_list.html +++ b/community_backup/webui/templates/borg_list.html @@ -1,13 +1,17 @@ {% extends "base.html" %} {% block content %} -
-

Your Borg Repositories

+
+

Your Borg Repositories

+ +

Create a borg repository by specifying a name and the SSH public key that is allowed to access this repository. The name is only for your convenience. To set up the repository please follow the BorgBackup documentation. There is also a Quick start guide available. + Always use encrypted backups!

+
@@ -30,4 +34,6 @@
+ + {% endblock %} diff --git a/community_backup/webui/templates/landing.html b/community_backup/webui/templates/landing.html index 1277742..39db761 100644 --- a/community_backup/webui/templates/landing.html +++ b/community_backup/webui/templates/landing.html @@ -4,4 +4,10 @@

Welcome to Community Backup!

+ + +

This is a service offering space for backups free of charge. This is a hobby project. It comes with no garantuees for anything, especially availability of the service. This service is in a testing phase at the moment. It might be unavailalbe at times and features might change quickly.

+

Currently this service offers only backups via Borg Backup.

+

To access this service you need an account. To register for an account you need a voucher. Vouchers are required to control the amount of users, so that there is enough space available for everyone.

+
{% endblock %} diff --git a/community_backup/webui/urls.py b/community_backup/webui/urls.py index 1476988..0bcf271 100644 --- a/community_backup/webui/urls.py +++ b/community_backup/webui/urls.py @@ -17,5 +17,5 @@ urlpatterns = [ name="borg_delete", ), path("register/", views.RegisterUserView.as_view(), name="register"), - path("about/", views.AboutView.as_view(), name="about"), + path("imprint/", views.ImprintView.as_view(), name="imprint"), ] diff --git a/community_backup/webui/views.py b/community_backup/webui/views.py index a491a73..f265477 100644 --- a/community_backup/webui/views.py +++ b/community_backup/webui/views.py @@ -41,9 +41,14 @@ class MarkdownView(TemplateView): return context -class AboutView(MarkdownView): +class DataProtectionView(MarkdownView): template_name = "markdown.html" - md = "about.md" + md = "dataprotection.md" + + +class ImprintView(MarkdownView): + template_name = "markdown.html" + md = "imprint.md" class BorgView(LoginRequiredMixin, ListView): From 883e903a509a46ca5643a4d29f0c041518a23bff Mon Sep 17 00:00:00 2001 From: Johannes Erwerle Date: Mon, 13 Apr 2026 20:10:23 +0200 Subject: [PATCH 4/5] removed print states from debugging --- community_backup/webui/deployments.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/community_backup/webui/deployments.py b/community_backup/webui/deployments.py index 0f1c9bd..b56d1bc 100644 --- a/community_backup/webui/deployments.py +++ b/community_backup/webui/deployments.py @@ -16,13 +16,11 @@ def sync_repos(dry_run=False): from .models import BorgRepository repos = BorgRepository.objects.all() - print(repos) repos_by_key = defaultdict(list) for repo in repos: repos_by_key[repo.key].append(repo) - print(repos) # create .ssh directory ssh_dir = settings.BACKUP_AUTHORIZED_KEYS.parent if not dry_run: @@ -45,10 +43,8 @@ def sync_repos(dry_run=False): if not dry_run: authorized_keys.write_text("\n".join(commands) + "\n") - print(repos) # remove repositories that do no longer exist repo_paths = {repo.path for repo in repos} - print(repo_paths) for user_dir in settings.BACKUP_BORG_DIR.iterdir(): print(user_dir) for dir in user_dir.iterdir(): From d4bbc367953a01130a510f476cb59e5225b86960 Mon Sep 17 00:00:00 2001 From: Johannes Erwerle Date: Mon, 13 Apr 2026 21:11:28 +0200 Subject: [PATCH 5/5] added DATABASES setting --- README.md | 109 +++++++++++++++++- .../community_backup/example_configuration.py | 7 ++ community_backup/community_backup/settings.py | 16 +-- 3 files changed, 119 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 995a282..f6af251 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,111 @@ A website to manage backup repositories. ## Installation -TODO +This installation guide assumes a Debian 13 system. + +Install Python and several dependencies: + +This example should wo + +```bash +apt update && apt install python3 python3-pip python3-venv borgbackup +``` + +Clone the repository + +git clone https://git.srvspace.net/jo/community-backup.git + +Or install from a release + +Create a venv + +```bash +python3 -m venv venv +``` + +Activea the venv + +```bash +source venv/bin/activate +``` + +Install the package and it's dependencies: + +```bash +pip install -e community-backup +``` + +Create a configuration file in `community_backup/community_backup/community_backup/configration.py`, e.g. by copying and adjusting the `example_configuraton.py` next to that location. + +The settings are explained in detail in the setting section. + +Apply the migrations: + +```bash +python community-backup/community_backup/manage.py migrate +``` + +Collect the static files: + +```bash +# python community-backup/community_backup/manage.py collectstatic + +130 static files copied to '/opt/community_backup/static'. +``` + +Point your webserver to the static files. E.g. with caddy: + +``` +example.backups.org { + handle /static/* { + file_server { + root /opt/community_backup/ + } + } + + handle { + reverse_proxy http://localhost:8000 + } +} +``` + +Create a superuser account + +``` + +``` + +## Settings + +### BACKUP_USER + +`BACKUP_USER` specifies the username, that is used for borg backups. This user is used for various file permissions as well as the user that clients are using to log in and push their backups. + +### BACKUP_REPO_HOST + +`BACKUP_REPO_HOST` is the hostname given to the user for pushing their backups to. E.g. `backup.example.com`. + +### BACKUP_HOME_DIR + +`BACKUP_HOME_DIR` is the home directory of the borg user. This must be a `pathlib.Path`. + +### BACKUP_BORG_DIR + +`BACKUP_BORG_DIR` is the directory in which the actual backups are stored. This must be a `pathlib.Path` + +### BACKUP_AUTHORIZED_KEYS + +`BACKUP_AUTHORIZED_KEYS` is the authorized_keys file of the SSH daemon used for `BORG_USER`. This must be a `pathlib.Path` + +### DATABASES + +`DATABASES` is the Django database setting. Here is an example for sqlite3. + +``` +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": "/some/path/to/db.sqlite3", + } +} +``` diff --git a/community_backup/community_backup/example_configuration.py b/community_backup/community_backup/example_configuration.py index ca5f4c6..fec60ea 100644 --- a/community_backup/community_backup/example_configuration.py +++ b/community_backup/community_backup/example_configuration.py @@ -6,3 +6,10 @@ BACKUP_BORG_DIR = BACKUP_HOME_DIR / "borg" BACKUP_AUTHORIZED_KEYS = BACKUP_HOME_DIR / ".ssh" / "authorized_keys" BACKUP_USER = "borg" BACKUP_REPO_HOST = "backup.example.com" + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": "/path/to/the/db.sqlite3", + } +} diff --git a/community_backup/community_backup/settings.py b/community_backup/community_backup/settings.py index 8b45b50..47514da 100644 --- a/community_backup/community_backup/settings.py +++ b/community_backup/community_backup/settings.py @@ -79,17 +79,6 @@ TEMPLATES = [ WSGI_APPLICATION = "community_backup.wsgi.application" -# Database -# https://docs.djangoproject.com/en/6.0/ref/settings/#databases - -DATABASES = { - "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": BASE_DIR / "db.sqlite3", - } -} - - # Password validation # https://docs.djangoproject.com/en/6.0/ref/settings/#auth-password-validators @@ -139,7 +128,9 @@ try: assert module.BACKUP_REPO_HOST, "The BACKUP_REPO_HOST setting is required." assert module.BACKUP_HOME_DIR, "The BACKUP_HOME_DIR setting is required." assert module.BACKUP_BORG_DIR, "The BACKUP_BORG_DIR setting is required." - assert module.BACKUP_AUTHORIZED_KEYS, "The BACKUP_AUTHORIZED_KEYS setting is required." + assert module.BACKUP_AUTHORIZED_KEYS, ( + "The BACKUP_AUTHORIZED_KEYS setting is required." + ) except ModuleNotFoundError: print(f"could not find configuration file {config_module}") @@ -150,3 +141,4 @@ BACKUP_REPO_HOST = module.BACKUP_REPO_HOST BACKUP_HOME_DIR = module.BACKUP_HOME_DIR BACKUP_BORG_DIR = module.BACKUP_BORG_DIR BACKUP_AUTHORIZED_KEYS = module.BACKUP_AUTHORIZED_KEYS +DATABASES = module.DATABASES