rip Docker, long live systemd
This commit is contained in:
parent
b7116cb13f
commit
7c4bb9acca
15 changed files with 184 additions and 201 deletions
|
|
@ -1,27 +0,0 @@
|
||||||
.env
|
|
||||||
.env.example
|
|
||||||
.git/
|
|
||||||
.github/
|
|
||||||
.pre-commit-config.yaml
|
|
||||||
.pytest_cache/
|
|
||||||
.ruff_cache/
|
|
||||||
.venv
|
|
||||||
.vscode/
|
|
||||||
.vscode/
|
|
||||||
*.json
|
|
||||||
*.log
|
|
||||||
*.py[codz]
|
|
||||||
**/__pycache__/
|
|
||||||
*$py.class
|
|
||||||
archive/
|
|
||||||
check_these_please/
|
|
||||||
db.sqlite3
|
|
||||||
db.sqlite3-journal
|
|
||||||
env.bak/
|
|
||||||
env/
|
|
||||||
ENV/
|
|
||||||
responses/
|
|
||||||
staticfiles/
|
|
||||||
tests/
|
|
||||||
venv.bak/
|
|
||||||
venv/
|
|
||||||
|
|
@ -31,3 +31,7 @@ EMAIL_USE_SSL=False
|
||||||
|
|
||||||
# Connection timeout in seconds
|
# Connection timeout in seconds
|
||||||
EMAIL_TIMEOUT=10
|
EMAIL_TIMEOUT=10
|
||||||
|
|
||||||
|
# Where to store Twitch API responses
|
||||||
|
TTVDROPS_IMPORTED_DIR=/mnt/fourteen/Data/Responses/imported
|
||||||
|
TTVDROPS_BROKEN_DIR=/mnt/fourteen/Data/Responses/broken
|
||||||
|
|
|
||||||
46
.github/workflows/docker.yaml
vendored
46
.github/workflows/docker.yaml
vendored
|
|
@ -1,10 +1,10 @@
|
||||||
name: Test and Build Docker Image
|
name: Run Pytest
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
pull_request:
|
pull_request:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 14 * * *" # Run every day at 14:00 CET
|
- cron: "0 14 * * 0" # Run weekly at 14:00 UTC
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
@ -16,23 +16,7 @@ jobs:
|
||||||
DJANGO_SECRET_KEY: 1234567890
|
DJANGO_SECRET_KEY: 1234567890
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: docker/login-action@v3
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: thelovinator1
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- uses: astral-sh/ruff-action@v3
|
|
||||||
with:
|
|
||||||
version: "latest"
|
|
||||||
- run: ruff check --exit-non-zero-on-fix --verbose
|
|
||||||
- run: ruff format --check --verbose
|
|
||||||
|
|
||||||
- run: docker build --check .
|
|
||||||
- uses: actions/setup-python@v6
|
- uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: 3.14
|
python-version: 3.14
|
||||||
|
|
@ -40,29 +24,3 @@ jobs:
|
||||||
- uses: astral-sh/setup-uv@v7
|
- uses: astral-sh/setup-uv@v7
|
||||||
- run: uv sync --all-extras --dev
|
- run: uv sync --all-extras --dev
|
||||||
- run: uv run pytest
|
- run: uv run pytest
|
||||||
|
|
||||||
- run: uv run python manage.py makemigrations --check
|
|
||||||
env:
|
|
||||||
TESTING: True
|
|
||||||
- run: uv run python manage.py migrate
|
|
||||||
env:
|
|
||||||
TESTING: True
|
|
||||||
- run: uv run python manage.py collectstatic --noinput
|
|
||||||
- id: meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
env:
|
|
||||||
DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index
|
|
||||||
with:
|
|
||||||
images: |
|
|
||||||
ghcr.io/thelovinator1/ttvdrops
|
|
||||||
tags: |
|
|
||||||
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') }}
|
|
||||||
|
|
||||||
- uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
platforms: linux/amd64
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
annotations: ${{ steps.meta.outputs.annotations }}
|
|
||||||
|
|
|
||||||
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
|
@ -79,6 +79,7 @@
|
||||||
"ttvdrops",
|
"ttvdrops",
|
||||||
"twid",
|
"twid",
|
||||||
"twitchgamedata",
|
"twitchgamedata",
|
||||||
|
"usermod",
|
||||||
"venv",
|
"venv",
|
||||||
"wrongpassword",
|
"wrongpassword",
|
||||||
"wsgi",
|
"wsgi",
|
||||||
|
|
|
||||||
30
Dockerfile
30
Dockerfile
|
|
@ -1,30 +0,0 @@
|
||||||
# syntax=docker/dockerfile:1
|
|
||||||
FROM python:3.14-slim-trixie
|
|
||||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
|
||||||
|
|
||||||
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
|
|
||||||
ENV UV_NO_DEV=1
|
|
||||||
ENV UV_PYTHON_DOWNLOADS=0
|
|
||||||
ENV UV_NO_CACHE=1
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
COPY . /app/
|
|
||||||
|
|
||||||
RUN uv sync
|
|
||||||
RUN chmod +x /app/start.sh
|
|
||||||
|
|
||||||
ENV PATH="/app/.venv/bin:$PATH"
|
|
||||||
|
|
||||||
RUN groupadd -g 1000 ttvdrops \
|
|
||||||
&& useradd -m -u 1000 -g 1000 -d /home/ttvdrops -s /bin/sh ttvdrops \
|
|
||||||
&& mkdir -p /home/ttvdrops/.local/share/TTVDrops \
|
|
||||||
&& chown -R ttvdrops:ttvdrops /home/ttvdrops /app
|
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
|
|
||||||
CMD curl -f http://localhost:8000/ || exit 1
|
|
||||||
|
|
||||||
VOLUME ["/home/ttvdrops/.local/share/TTVDrops"]
|
|
||||||
EXPOSE 8000
|
|
||||||
USER ttvdrops
|
|
||||||
ENTRYPOINT [ "/app/start.sh" ]
|
|
||||||
CMD ["uv", "run", "gunicorn", "config.wsgi:application", "--workers", "27", "--bind", "0.0.0.0:8000"]
|
|
||||||
91
README.md
91
README.md
|
|
@ -2,6 +2,74 @@
|
||||||
|
|
||||||
Get notified when a new drop is available on Twitch
|
Get notified when a new drop is available on Twitch
|
||||||
|
|
||||||
|
## TL;DR (Arch Linux + PostgreSQL)
|
||||||
|
|
||||||
|
Install and initialize Postgres, then start the service:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo pacman -S postgresql
|
||||||
|
sudo -u postgres initdb -D /var/lib/postgres/data
|
||||||
|
sudo systemctl enable --now postgresql
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a local role and database:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo -u postgres createuser -P ttvdrops
|
||||||
|
sudo -u postgres createdb -O ttvdrops ttvdrops
|
||||||
|
```
|
||||||
|
|
||||||
|
Point Django at the unix socket used by Arch (`/run/postgresql`):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
POSTGRES_USER=ttvdrops
|
||||||
|
POSTGRES_PASSWORD=your_password
|
||||||
|
POSTGRES_DB=ttvdrops
|
||||||
|
POSTGRES_HOST=/run/postgresql
|
||||||
|
POSTGRES_PORT=5432
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linux (Systemd)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo useradd --create-home --home-dir /home/ttvdrops --shell /bin/fish ttvdrops
|
||||||
|
sudo passwd ttvdrops
|
||||||
|
sudo usermod -aG wheel ttvdrops
|
||||||
|
su - ttvdrops
|
||||||
|
git clone https://github.com/TheLovinator1/ttvdrops.git
|
||||||
|
cd ttvdrops
|
||||||
|
uv sync --no-dev
|
||||||
|
|
||||||
|
# Modify .env with the correct database credentials and other settings, then run migrations:
|
||||||
|
uv run python manage.py migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
Install the systemd service from the repo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo install -m 0644 tools/systemd/ttvdrops.socket /etc/systemd/system/ttvdrops.socket
|
||||||
|
sudo install -m 0644 tools/systemd/ttvdrops.service /etc/systemd/system/ttvdrops.service
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable and start the service:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable --now ttvdrops.socket
|
||||||
|
sudo systemctl enable --now ttvdrops.service
|
||||||
|
|
||||||
|
curl --unix-socket /run/ttvdrops/ttvdrops.sock http://ttvdrops.lovinator.space
|
||||||
|
```
|
||||||
|
|
||||||
|
Install and enable timers:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo install -m 0644 tools/systemd/ttvdrops-backup.{service,timer} /etc/systemd/system/
|
||||||
|
sudo install -m 0644 tools/systemd/ttvdrops-import-drops.{service,timer} /etc/systemd/system/
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
sudo systemctl enable --now ttvdrops-backup.timer ttvdrops-import-drops.timer
|
||||||
|
```
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -42,3 +110,26 @@ Optional arguments:
|
||||||
```bash
|
```bash
|
||||||
uv run python manage.py backup_db --output-dir "<path>" --prefix "ttvdrops"
|
uv run python manage.py backup_db --output-dir "<path>" --prefix "ttvdrops"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### How the duck does permissions work on Linux?
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo groupadd responses
|
||||||
|
sudo usermod -aG responses lovinator
|
||||||
|
sudo usermod -aG responses ttvdrops
|
||||||
|
|
||||||
|
sudo chown -R lovinator:responses /mnt/fourteen/Data/Responses
|
||||||
|
sudo chown -R lovinator:responses /mnt/fourteen/Data/ttvdrops
|
||||||
|
sudo chmod -R 2775 /mnt/fourteen/Data/Responses
|
||||||
|
sudo chmod -R 2775 /mnt/fourteen/Data/ttvdrops
|
||||||
|
|
||||||
|
# Import dir
|
||||||
|
sudo setfacl -b /mnt/fourteen/Data/Responses /mnt/fourteen/Data/Responses/imported
|
||||||
|
sudo setfacl -m g:responses:rwx /mnt/fourteen/Data/Responses /mnt/fourteen/Data/Responses/imported
|
||||||
|
sudo setfacl -d -m g:responses:rwx /mnt/fourteen/Data/Responses /mnt/fourteen/Data/Responses/imported
|
||||||
|
|
||||||
|
# Backup dir
|
||||||
|
sudo setfacl -b /mnt/fourteen/Data/ttvdrops
|
||||||
|
sudo setfacl -m g:responses:rwx /mnt/fourteen/Data/ttvdrops
|
||||||
|
sudo setfacl -d -m g:responses:rwx /mnt/fourteen/Data/ttvdrops
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
services:
|
|
||||||
ttvdrops_postgres:
|
|
||||||
container_name: ttvdrops_postgres
|
|
||||||
image: docker.io/pgautoupgrade/pgautoupgrade:latest
|
|
||||||
command:
|
|
||||||
- "-c"
|
|
||||||
- "max_connections=200"
|
|
||||||
- "-c"
|
|
||||||
- "shared_buffers=4GB"
|
|
||||||
- "-c"
|
|
||||||
- "effective_cache_size=12GB"
|
|
||||||
- "-c"
|
|
||||||
- "maintenance_work_mem=1GB"
|
|
||||||
- "-c"
|
|
||||||
- "checkpoint_completion_target=0.9"
|
|
||||||
- "-c"
|
|
||||||
- "wal_buffers=16MB"
|
|
||||||
- "-c"
|
|
||||||
- "default_statistics_target=100"
|
|
||||||
- "-c"
|
|
||||||
- "random_page_cost=1.1"
|
|
||||||
- "-c"
|
|
||||||
- "effective_io_concurrency=200"
|
|
||||||
- "-c"
|
|
||||||
- "work_mem=20MB"
|
|
||||||
- "-c"
|
|
||||||
- "huge_pages=off"
|
|
||||||
- "-c"
|
|
||||||
- "min_wal_size=1GB"
|
|
||||||
- "-c"
|
|
||||||
- "max_wal_size=4GB"
|
|
||||||
- "-c"
|
|
||||||
- "max_worker_processes=12"
|
|
||||||
- "-c"
|
|
||||||
- "max_parallel_workers_per_gather=4"
|
|
||||||
- "-c"
|
|
||||||
- "max_parallel_workers=12"
|
|
||||||
- "-c"
|
|
||||||
- "max_parallel_maintenance_workers=4"
|
|
||||||
environment:
|
|
||||||
- POSTGRES_DB=ttvdrops
|
|
||||||
- POSTGRES_USER=ttvdrops
|
|
||||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
|
||||||
ports:
|
|
||||||
- "5442:5432"
|
|
||||||
volumes:
|
|
||||||
- /mnt/Docker/Data/ttvdrops/postgres:/var/lib/postgresql
|
|
||||||
shm_size: '8gb'
|
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- internal
|
|
||||||
|
|
||||||
ttvdrops:
|
|
||||||
container_name: ttvdrops
|
|
||||||
image: ghcr.io/thelovinator1/ttvdrops:latest
|
|
||||||
expose:
|
|
||||||
- "8000"
|
|
||||||
user: 1000:1000
|
|
||||||
environment:
|
|
||||||
- DEBUG=False
|
|
||||||
- DJANGO_SECRET_KEY=$(DJANGO_SECRET_KEY)
|
|
||||||
- TWITCH_CLIENT_ID=$(TWITCH_CLIENT_ID)
|
|
||||||
- TWITCH_CLIENT_SECRET=$(TWITCH_CLIENT_SECRET)
|
|
||||||
- EMAIL_HOST=smtp.gmail.com
|
|
||||||
- EMAIL_PORT=587
|
|
||||||
- EMAIL_HOST_USER=$(EMAIL_HOST_USER)
|
|
||||||
- EMAIL_HOST_PASSWORD=$(EMAIL_HOST_PASSWORD)
|
|
||||||
- EMAIL_USE_TLS=True
|
|
||||||
- EMAIL_USE_SSL=False
|
|
||||||
- POSTGRES_DB=ttvdrops
|
|
||||||
- POSTGRES_USER=ttvdrops
|
|
||||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
|
||||||
- POSTGRES_HOST=ttvdrops_postgres
|
|
||||||
- POSTGRES_PORT=5432
|
|
||||||
volumes:
|
|
||||||
- /mnt/Docker/Data/ttvdrops/data:/home/ttvdrops/.local/share/TTVDrops
|
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- web
|
|
||||||
- internal
|
|
||||||
depends_on:
|
|
||||||
ttvdrops_postgres:
|
|
||||||
condition: service_started
|
|
||||||
|
|
||||||
networks:
|
|
||||||
web:
|
|
||||||
external: true
|
|
||||||
internal:
|
|
||||||
driver: bridge
|
|
||||||
|
|
@ -20,7 +20,9 @@ dependencies = [
|
||||||
"pydantic",
|
"pydantic",
|
||||||
"pygments",
|
"pygments",
|
||||||
"python-dotenv",
|
"python-dotenv",
|
||||||
|
"systemd; sys_platform == 'linux'",
|
||||||
"tqdm",
|
"tqdm",
|
||||||
|
"setproctitle>=1.3.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
|
|
|
||||||
11
start.sh
11
start.sh
|
|
@ -1,11 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "Running database migrations..."
|
|
||||||
uv run python manage.py migrate --noinput
|
|
||||||
|
|
||||||
echo "Collecting static files..."
|
|
||||||
uv run python manage.py collectstatic --noinput
|
|
||||||
|
|
||||||
echo "Starting Django server..."
|
|
||||||
exec "$@"
|
|
||||||
10
tools/systemd/ttvdrops-backup.service
Normal file
10
tools/systemd/ttvdrops-backup.service
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
[Unit]
|
||||||
|
Description=TTVDrops database backup
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
User=ttvdrops
|
||||||
|
Group=ttvdrops
|
||||||
|
WorkingDirectory=/home/ttvdrops/ttvdrops
|
||||||
|
EnvironmentFile=/home/ttvdrops/ttvdrops/.env
|
||||||
|
ExecStart=/usr/bin/uv run python manage.py backup_db
|
||||||
9
tools/systemd/ttvdrops-backup.timer
Normal file
9
tools/systemd/ttvdrops-backup.timer
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Nightly TTVDrops database backup
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnCalendar=*-*-* 02:15:00
|
||||||
|
Persistent=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
12
tools/systemd/ttvdrops-import-drops.service
Normal file
12
tools/systemd/ttvdrops-import-drops.service
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
[Unit]
|
||||||
|
Description=TTVDrops import drops from pending directory
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
User=ttvdrops
|
||||||
|
Group=ttvdrops
|
||||||
|
WorkingDirectory=/home/ttvdrops/ttvdrops
|
||||||
|
EnvironmentFile=/home/ttvdrops/ttvdrops/.env
|
||||||
|
ExecStart=/usr/bin/uv run python manage.py better_import_drops /mnt/fourteen/Data/Responses/pending
|
||||||
|
-ExecStartPost=/usr/bin/uv run python manage.py download_box_art
|
||||||
|
-ExecStartPost=/usr/bin/uv run python manage.py download_campaign_images
|
||||||
10
tools/systemd/ttvdrops-import-drops.timer
Normal file
10
tools/systemd/ttvdrops-import-drops.timer
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
[Unit]
|
||||||
|
Description=Frequent TTVDrops import drops timer
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnBootSec=0
|
||||||
|
OnUnitActiveSec=1min
|
||||||
|
Persistent=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
32
tools/systemd/ttvdrops.service
Normal file
32
tools/systemd/ttvdrops.service
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
[Unit]
|
||||||
|
Description=TTVDrops
|
||||||
|
Requires=ttvdrops.socket
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=ttvdrops
|
||||||
|
Group=ttvdrops
|
||||||
|
WorkingDirectory=/home/ttvdrops/ttvdrops
|
||||||
|
EnvironmentFile=/home/ttvdrops/ttvdrops/.env
|
||||||
|
RuntimeDirectory=ttvdrops
|
||||||
|
UMask=0077
|
||||||
|
ExecStart=/usr/bin/uv run gunicorn config.wsgi:application --bind unix:/run/ttvdrops/ttvdrops.sock --workers 13 --name ttvdrops --max-requests-jitter 50 --max-requests 1200
|
||||||
|
ExecReload=/bin/kill -s HUP $MAINPID
|
||||||
|
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
ProtectSystem=full
|
||||||
|
ProtectHome=no
|
||||||
|
ReadWritePaths=/home/ttvdrops/ttvdrops /run/ttvdrops /mnt/fourteen/Data/Responses
|
||||||
|
PrivateDevices=yes
|
||||||
|
CapabilityBoundingSet=
|
||||||
|
AmbientCapabilities=
|
||||||
|
RestrictRealtime=yes
|
||||||
|
LockPersonality=yes
|
||||||
|
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
11
tools/systemd/ttvdrops.socket
Normal file
11
tools/systemd/ttvdrops.socket
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[Unit]
|
||||||
|
Description=TTVDrops Socket
|
||||||
|
|
||||||
|
[Socket]
|
||||||
|
ListenStream=/run/ttvdrops/ttvdrops.sock
|
||||||
|
SocketUser=ttvdrops
|
||||||
|
SocketGroup=ttvdrops
|
||||||
|
SocketMode=0660
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=sockets.target
|
||||||
Loading…
Add table
Add a link
Reference in a new issue