Compare commits
5 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d4bbc36795 | |||
| 883e903a50 | |||
| eb787abfa2 | |||
| bb62b5dde4 | |||
| 07e8c7e047 |
12 changed files with 175 additions and 32 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -207,3 +207,5 @@ tags
|
|||
[._]*.un~
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/vim,venv,python
|
||||
|
||||
community_backup/community_backup/configuration.py
|
||||
|
|
|
|||
109
README.md
109
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",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
# enter your overrides here
|
||||
print("foo")
|
||||
15
community_backup/community_backup/example_configuration.py
Normal file
15
community_backup/community_backup/example_configuration.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# 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"
|
||||
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": "/path/to/the/db.sqlite3",
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -78,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
|
||||
|
||||
|
|
@ -127,4 +117,28 @@ STATIC_URL = "static/"
|
|||
|
||||
TASKS = {"default": {"BACKEND": "django.tasks.backends.immediate.ImmediateBackend"}}
|
||||
|
||||
from .configuration import *
|
||||
STATIC_ROOT = BASE_DIR.parent.parent / "static"
|
||||
|
||||
# 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)
|
||||
|
||||
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
|
||||
DATABASES = module.DATABASES
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -48,13 +48,7 @@
|
|||
<div class="row">
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url "about" %}">About</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Imprint</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Data Protection</a>
|
||||
<a class="nav-link" href="{% url 'imprint' %}">Imprint</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class<div class="d-flex">
|
||||
<div class="d-flex">
|
||||
<h1>Your Borg Repositories</h1>
|
||||
<div class="p-2 ms-auto">
|
||||
<a class="btn btn-primary" href="{% url 'borg_add' %}" role="button">Add Repository</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<row>
|
||||
<p>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 <a href="https://borgbackup.readthedocs.io/en/stable/">BorgBackup documentation</a>. There is also a <a href="https://borgbackup.readthedocs.io/en/stable/quickstart.html">Quick start guide</a> available.
|
||||
<b>Always use encrypted backups!</b></p>
|
||||
</row>
|
||||
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
|
|
@ -30,4 +34,6 @@
|
|||
</table>
|
||||
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -4,4 +4,10 @@
|
|||
<row class="text-center p-2">
|
||||
<h1>Welcome to Community Backup!</h1>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<p>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.</p>
|
||||
<p>Currently this service offers only backups via <a href="https://www.borgbackup.org/">Borg Backup</a>.</p>
|
||||
<p>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.</p>
|
||||
</row>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue