Add initial pyproject.toml for dj-beets project with dependencies and metadata
This commit is contained in:
@@ -0,0 +1,173 @@
|
|||||||
|
# GitHub Copilot Instructions – infra-nix (NixOS Infrastructure)
|
||||||
|
|
||||||
|
Copilot soll in diesem Repo so unterstützen, dass die Struktur der NixOS-
|
||||||
|
Infrastruktur konsistent, deklarativ und sauber bleibt. Diese Anweisung
|
||||||
|
gilt für alle Dateien im Repository, insbesondere für Flakes, Modules,
|
||||||
|
Hosts, Deployment-Skripte und Shell-Files.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧱 Projektstruktur
|
||||||
|
|
||||||
|
Das Repository enthält eine deklarative NixOS-Infrastruktur für zwei
|
||||||
|
Systeme:
|
||||||
|
|
||||||
|
- **fileserver**
|
||||||
|
→ SMB, FileBrowser (OCI Container), rclone-Backup, /data-Datendisk
|
||||||
|
|
||||||
|
- **gateway**
|
||||||
|
→ AdGuardHome (DNS), Caddy (Reverse Proxy), feste IPs
|
||||||
|
|
||||||
|
Globale Struktur:
|
||||||
|
|
||||||
|
```
|
||||||
|
infra-nix/
|
||||||
|
├─ flake.nix
|
||||||
|
├─ hosts/
|
||||||
|
│ ├─ fileserver/
|
||||||
|
│ │ ├─ configuration.nix
|
||||||
|
│ │ └─ hardware-configuration.nix
|
||||||
|
│ └─ gateway/
|
||||||
|
│ ├─ configuration.nix
|
||||||
|
│ └─ hardware-configuration.nix
|
||||||
|
└─ modules/
|
||||||
|
├─ common.nix
|
||||||
|
├─ fileserver.nix
|
||||||
|
└─ gateway.nix
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Ziele für Copilot
|
||||||
|
|
||||||
|
Copilot soll:
|
||||||
|
|
||||||
|
1. **Nur deklarative Änderungen vorschlagen**, die mit NixOS-Flakes kompatibel sind.
|
||||||
|
2. Die bestehende Struktur respektieren:
|
||||||
|
- `hosts/*` → Host-spezifisch
|
||||||
|
- `modules/*` → Rollen / Services
|
||||||
|
- Keine Vermischung
|
||||||
|
3. Immer die Syntax und Konventionen von NixOS 25.05 einhalten.
|
||||||
|
4. Kein imperatives Bash/Script in Nix-Files vorschlagen.
|
||||||
|
5. Installationsscripte sollen *nur* im Ordner `install/` liegen.
|
||||||
|
6. Kein Secret in irgendwelche Dateien einfügen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚫 Copilot darf NICHT:
|
||||||
|
|
||||||
|
- rclone-Konfigs einfügen
|
||||||
|
- API-Keys, Tokens, SSH-Keys generieren
|
||||||
|
- Passwörter hartcodieren (außer Platzhalter im Installer)
|
||||||
|
- Konfigurationen mischen (z. B. Caddy-Konfig in fileserver host packen)
|
||||||
|
- SystemstateVersions verändern
|
||||||
|
- hardware-configuration.nix anfassen (außer Kommentar)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🟢 Copilot SOLL:
|
||||||
|
|
||||||
|
### **Flakes**
|
||||||
|
- NUR `flake.nix` updaten, wenn Struktur unverändert bleibt.
|
||||||
|
- `nixosConfigurations.<host>` sauber definieren.
|
||||||
|
- Keine Inputs manipulieren, außer wenn der Nutzer es verlangt.
|
||||||
|
|
||||||
|
### **Modules**
|
||||||
|
- Rein deklarative NixOS-Optionen vorschlagen.
|
||||||
|
- Firewall-Regeln nur innerhalb der Host- oder Rollenmodule definieren.
|
||||||
|
- systemd-Services korrekt bauen:
|
||||||
|
- `serviceConfig.Type = "oneshot";`
|
||||||
|
- `wantedBy = [ "multi-user.target" ];` oder Timer.
|
||||||
|
|
||||||
|
### **Hosts**
|
||||||
|
- Netzwerk-Konfiguration korrekt setzen:
|
||||||
|
- `networking.useDHCP = false;`
|
||||||
|
- `networking.interfaces.<iface>.ipv4.addresses = [...]`
|
||||||
|
- `networking.defaultGateway`
|
||||||
|
- `networking.nameservers`
|
||||||
|
|
||||||
|
### **Installer-Skripte**
|
||||||
|
- Bash-Skripte strikt POSIX-kompatibel generieren.
|
||||||
|
- Partitionierung per `parted` + `mkfs` sauber halten.
|
||||||
|
- Nie secret-basierte Dinge reinschreiben.
|
||||||
|
- Platzhalter-Passwörter erlauben (z. B. "changeme").
|
||||||
|
|
||||||
|
### **Deployment**
|
||||||
|
Copilot soll folgende Struktur unterstützen:
|
||||||
|
|
||||||
|
```
|
||||||
|
deploy.sh
|
||||||
|
install/install-fileserver.sh
|
||||||
|
install/install-gateway.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Deploy-Script-Regeln:
|
||||||
|
|
||||||
|
- remote `git pull`
|
||||||
|
- remote `nixos-rebuild switch --flake .#host`
|
||||||
|
- Fehlermeldungen klar halten
|
||||||
|
- SSH-Verbindungen per definierte Host-IPs nutzen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📄 Stil & Format-Vorgaben
|
||||||
|
|
||||||
|
- Nix-Files immer **2 spaces indent**, keine Tabs.
|
||||||
|
- Kommentare in Nix:
|
||||||
|
```nix
|
||||||
|
# Kommentar
|
||||||
|
```
|
||||||
|
- Bash-Skripte:
|
||||||
|
- `set -euo pipefail`
|
||||||
|
- Funktionen statt dupliziertem Code
|
||||||
|
- Keine trailing spaces
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛡 Security
|
||||||
|
|
||||||
|
Copilot darf:
|
||||||
|
- KEINE Secrets generieren
|
||||||
|
- KEINE echten Passwörter erstellen
|
||||||
|
- KEINE private Keys einfügen
|
||||||
|
- KEINE rclone.conf, sops/age Keys etc. erstellen
|
||||||
|
- KEINE Passwörter automatisch setzen außer Dummy im Installer
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 Testbarkeit / Rebuilds
|
||||||
|
|
||||||
|
Copilot soll bevorzugt:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo nixos-rebuild switch --flake .#fileserver
|
||||||
|
```
|
||||||
|
|
||||||
|
und NICHT den alten channel-basierten Modus vorschlagen.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Erweiterbare Bereiche
|
||||||
|
|
||||||
|
Copilot darf folgende Rollen ergänzen, wenn der Nutzer danach fragt:
|
||||||
|
- zusätzlicher Storage Host
|
||||||
|
- Monitoring-VM (Prometheus/Grafana)
|
||||||
|
- Backup-VM
|
||||||
|
- VPN (WireGuard)
|
||||||
|
- Logging-Server
|
||||||
|
|
||||||
|
Dabei:
|
||||||
|
- eigene module-Dateien erzeugen
|
||||||
|
- Hosts sauber anlegen
|
||||||
|
- Struktur konsistent halten
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 Zusammenfassung
|
||||||
|
|
||||||
|
Copilot soll helfen:
|
||||||
|
- Struktur beizubehalten
|
||||||
|
- deklarativ zu bleiben
|
||||||
|
- nichts zu vermischen
|
||||||
|
- saubere, sichere, reproducible NixOS-Konfigurationen zu erzeugen
|
||||||
|
- Install- und Deploy-Skripte korrekt, minimal und robust zu halten
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
.PHONY: help deploy deploy-gateway deploy-fileserver install-gateway install-fileserver fetch-hwconfig-gateway fetch-hwconfig-fileserver all
|
||||||
|
|
||||||
|
# Hosts
|
||||||
|
GATEWAY_HOST := danlin@10.202.82.3
|
||||||
|
FILESERVER_HOST := danlin@10.202.82.6
|
||||||
|
|
||||||
|
# Default target
|
||||||
|
all: deploy
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "NixOS Infrastructure Management"
|
||||||
|
@echo ""
|
||||||
|
@echo "Targets:"
|
||||||
|
@echo " make deploy Deploy to both gateway and fileserver"
|
||||||
|
@echo " make deploy-gateway Deploy to gateway only"
|
||||||
|
@echo " make deploy-fileserver Deploy to fileserver only"
|
||||||
|
@echo " make install-gateway Run installation script for gateway"
|
||||||
|
@echo " make install-fileserver Run installation script for fileserver"
|
||||||
|
@echo " make fetch-hwconfig-gateway Fetch hardware config from gateway"
|
||||||
|
@echo " make fetch-hwconfig-fileserver Fetch hardware config from fileserver"
|
||||||
|
@echo ""
|
||||||
|
|
||||||
|
# Deployment targets
|
||||||
|
deploy: deploy-gateway deploy-fileserver
|
||||||
|
|
||||||
|
deploy-gateway:
|
||||||
|
@echo "======================================"
|
||||||
|
@echo "Deploying gateway..."
|
||||||
|
@echo "======================================"
|
||||||
|
@echo "Syncing files to gateway..."
|
||||||
|
rsync -av --delete --exclude '.git' ./ $(GATEWAY_HOST):/tmp/infra-nix/
|
||||||
|
@echo "Building and switching on gateway..."
|
||||||
|
ssh -tt $(GATEWAY_HOST) "cd /tmp/infra-nix && sudo nixos-rebuild switch --flake '.#gateway'"
|
||||||
|
@echo "✓ Gateway deployed successfully"
|
||||||
|
@echo ""
|
||||||
|
|
||||||
|
deploy-fileserver:
|
||||||
|
@echo "======================================"
|
||||||
|
@echo "Deploying fileserver..."
|
||||||
|
@echo "======================================"
|
||||||
|
@echo "Syncing files to fileserver..."
|
||||||
|
rsync -av --delete --exclude '.git' --exclude 'src/dj-beets/poetry.lock' ./ $(FILESERVER_HOST):/tmp/infra-nix/
|
||||||
|
@echo "Copying dj-beets project to /opt/dj-beets..."
|
||||||
|
ssh $(FILESERVER_HOST) "sudo mkdir -p /opt/dj-beets"
|
||||||
|
rsync -av --delete src/dj-beets/ $(FILESERVER_HOST):/tmp/dj-beets-tmp/
|
||||||
|
ssh $(FILESERVER_HOST) "sudo rsync -a --delete /tmp/dj-beets-tmp/ /opt/dj-beets/ && sudo rm -rf /tmp/dj-beets-tmp"
|
||||||
|
@echo "Building and switching on fileserver..."
|
||||||
|
ssh -tt $(FILESERVER_HOST) "cd /tmp/infra-nix && sudo nixos-rebuild switch --flake '.#fileserver'"
|
||||||
|
@echo "✓ Fileserver deployed successfully"
|
||||||
|
@echo ""
|
||||||
|
|
||||||
|
# Installation targets
|
||||||
|
install-gateway:
|
||||||
|
@echo "======================================"
|
||||||
|
@echo "Installing gateway..."
|
||||||
|
@echo "======================================"
|
||||||
|
@read -p "Enter gateway IP address: " IP; \
|
||||||
|
if [ -z "$$IP" ]; then \
|
||||||
|
echo "Error: IP address required"; \
|
||||||
|
exit 1; \
|
||||||
|
fi; \
|
||||||
|
echo "Copying install script to $$IP..."; \
|
||||||
|
scp install/install-gateway.sh root@$$IP:/tmp/; \
|
||||||
|
echo "Connecting to $$IP..."; \
|
||||||
|
ssh root@$$IP "bash /tmp/install-gateway.sh"; \
|
||||||
|
echo ""; \
|
||||||
|
echo "Fetching hardware config from $$IP..."; \
|
||||||
|
scp root@$$IP:/mnt/etc/nixos/hardware-configuration.nix hosts/gateway/hardware-configuration.nix; \
|
||||||
|
echo "✓ Hardware config saved to hosts/gateway/hardware-configuration.nix"; \
|
||||||
|
echo "✓ Update hosts/gateway/configuration.nix with the correct IP if needed"
|
||||||
|
@echo ""
|
||||||
|
|
||||||
|
install-fileserver:
|
||||||
|
@echo "======================================"
|
||||||
|
@echo "Installing fileserver..."
|
||||||
|
@echo "======================================"
|
||||||
|
@read -p "Enter fileserver IP address: " IP; \
|
||||||
|
if [ -z "$$IP" ]; then \
|
||||||
|
echo "Error: IP address required"; \
|
||||||
|
exit 1; \
|
||||||
|
fi; \
|
||||||
|
echo "Copying install script to $$IP..."; \
|
||||||
|
scp install/install-fileserver.sh root@$$IP:/tmp/; \
|
||||||
|
echo "Connecting to $$IP..."; \
|
||||||
|
ssh root@$$IP "bash /tmp/install-fileserver.sh"; \
|
||||||
|
echo ""; \
|
||||||
|
echo "Fetching hardware config from $$IP..."; \
|
||||||
|
scp root@$$IP:/mnt/etc/nixos/hardware-configuration.nix hosts/fileserver/hardware-configuration.nix; \
|
||||||
|
echo "✓ Hardware config saved to hosts/fileserver/hardware-configuration.nix"; \
|
||||||
|
echo "✓ Update hosts/fileserver/configuration.nix with the correct IP if needed"
|
||||||
|
@echo ""
|
||||||
|
|
||||||
|
# Fetch hardware configuration targets
|
||||||
|
fetch-hwconfig-gateway:
|
||||||
|
@echo "======================================"
|
||||||
|
@echo "Fetching hardware config from gateway..."
|
||||||
|
@echo "======================================"
|
||||||
|
scp $(GATEWAY_HOST):/etc/nixos/hardware-configuration.nix hosts/gateway/
|
||||||
|
@echo "✓ Hardware config saved to hosts/gateway/hardware-configuration.nix"
|
||||||
|
@echo ""
|
||||||
|
|
||||||
|
fetch-hwconfig-fileserver:
|
||||||
|
@echo "======================================"
|
||||||
|
@echo "Fetching hardware config from fileserver..."
|
||||||
|
@echo "======================================"
|
||||||
|
scp $(FILESERVER_HOST):/etc/nixos/hardware-configuration.nix hosts/fileserver/
|
||||||
|
@echo "✓ Hardware config saved to hosts/fileserver/hardware-configuration.nix"
|
||||||
|
@echo ""
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
````markdown
|
||||||
|
# infra-nix: NixOS Infrastructure
|
||||||
|
|
||||||
|
Dieses Repository enthält die deklarative NixOS-Infrastruktur für:
|
||||||
|
- **fileserver**: SMB, FileBrowser, rclone-Backup, DJ-Beets Musik-Library
|
||||||
|
- **gateway**: AdGuardHome (DNS), Caddy (Reverse Proxy)
|
||||||
|
|
||||||
|
## Quick Deploy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Alle Systeme deployen
|
||||||
|
make deploy
|
||||||
|
|
||||||
|
# Nur fileserver deployen
|
||||||
|
make deploy-fileserver
|
||||||
|
|
||||||
|
# Nur gateway deployen
|
||||||
|
make deploy-gateway
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DJ-Beets Projekt (`src/dj-beets/`)
|
||||||
|
|
||||||
|
Dein eigenes Projekt als Beets-Ersatz. Wird beim Deploy auf `/opt/dj-beets` kopiert.
|
||||||
|
|
||||||
|
**Details siehe:** [src/dj-beets/README.md](src/dj-beets/README.md)
|
||||||
|
|
||||||
|
**Workflow:**
|
||||||
|
1. Lokal in `src/dj-beets/` entwickeln
|
||||||
|
2. `make deploy-fileserver` → deployed auf Server
|
||||||
|
3. Auto-Import läuft alle 10 Minuten via Timer
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fileserver: rclone + SMB Quickstart
|
||||||
|
|
||||||
|
This guide covers setting up rclone (Google Drive) and setting the SMB password for user `danlin`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## rclone (Google Drive)
|
||||||
|
|
||||||
|
The system has `rclone` installed and a systemd job to sync `/data` to a remote named `gdrive`:
|
||||||
|
|
||||||
|
- Service: `rclone-backup.service` (oneshot)
|
||||||
|
- Timer: `rclone-backup.timer` (runs daily 03:00)
|
||||||
|
- Log: `/var/log/rclone-backup.log`
|
||||||
|
|
||||||
|
### 1) Create the remote `gdrive`
|
||||||
|
Run on the fileserver:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo -i
|
||||||
|
rclone config
|
||||||
|
```
|
||||||
|
|
||||||
|
Then:
|
||||||
|
- n) New remote
|
||||||
|
- name: `gdrive`
|
||||||
|
- storage: `drive` (Google Drive)
|
||||||
|
- Use auto config? For headless server choose "No"
|
||||||
|
- Follow the printed instructions using another machine, or run on a desktop and copy the token
|
||||||
|
- Keep defaults unless you need a service account
|
||||||
|
- y) Yes to save
|
||||||
|
|
||||||
|
Verify:
|
||||||
|
```bash
|
||||||
|
rclone lsd gdrive:
|
||||||
|
rclone mkdir gdrive:backup-daten
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2) Test backup manually
|
||||||
|
```bash
|
||||||
|
sudo systemctl start rclone-backup.service
|
||||||
|
sudo journalctl -u rclone-backup -e
|
||||||
|
sudo tail -n 100 /var/log/rclone-backup.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3) Check/enable timer
|
||||||
|
```bash
|
||||||
|
systemctl list-timers '*rclone*'
|
||||||
|
sudo systemctl enable --now rclone-backup.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SMB password for `danlin`
|
||||||
|
|
||||||
|
The share configuration:
|
||||||
|
- Protected share: `daten` → requires user `danlin`
|
||||||
|
- Guest share: `daten-share` → guest access allowed
|
||||||
|
|
||||||
|
Set the Samba password for `danlin` (independent from system login password):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo smbpasswd -a danlin
|
||||||
|
```
|
||||||
|
|
||||||
|
Useful commands:
|
||||||
|
```bash
|
||||||
|
sudo pdbedit -L # list Samba users
|
||||||
|
sudo systemctl status samba
|
||||||
|
sudo journalctl -u samba -e
|
||||||
|
```
|
||||||
|
|
||||||
|
Access examples:
|
||||||
|
- Windows: `\\fileserver\daten` or `\\fileserver\daten-share`
|
||||||
|
- macOS Finder: Go → Connect to Server → `smb://fileserver/daten` or `smb://fileserver/daten-share`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
- rclone auth on headless: use `rclone authorize 'drive'` on a desktop and paste token
|
||||||
|
- Permissions: ensure `/data/daten` and `/data/daten/share` exist and are writable (`systemd-tmpfiles` creates them)
|
||||||
|
- Firewall: SMB ports 139/445 are open by module config
|
||||||
|
|
||||||
|
````
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## rclone (Google Drive)
|
||||||
|
|
||||||
|
The system has `rclone` installed and a systemd job to sync `/data` to a remote named `gdrive`:
|
||||||
|
|
||||||
|
- Service: `rclone-backup.service` (oneshot)
|
||||||
|
- Timer: `rclone-backup.timer` (runs daily 03:00)
|
||||||
|
- Log: `/var/log/rclone-backup.log`
|
||||||
|
|
||||||
|
### 1) Create the remote `gdrive`
|
||||||
|
Run on the fileserver:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo -i
|
||||||
|
rclone config
|
||||||
|
```
|
||||||
|
|
||||||
|
Then:
|
||||||
|
- n) New remote
|
||||||
|
- name: `gdrive`
|
||||||
|
- storage: `drive` (Google Drive)
|
||||||
|
- Use auto config? For headless server choose "No"
|
||||||
|
- Follow the printed instructions using another machine, or run on a desktop and copy the token
|
||||||
|
- Keep defaults unless you need a service account
|
||||||
|
- y) Yes to save
|
||||||
|
|
||||||
|
Verify:
|
||||||
|
```bash
|
||||||
|
rclone lsd gdrive:
|
||||||
|
rclone mkdir gdrive:backup-daten
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2) Test backup manually
|
||||||
|
```bash
|
||||||
|
sudo systemctl start rclone-backup.service
|
||||||
|
sudo journalctl -u rclone-backup -e
|
||||||
|
sudo tail -n 100 /var/log/rclone-backup.log
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3) Check/enable timer
|
||||||
|
```bash
|
||||||
|
systemctl list-timers '*rclone*'
|
||||||
|
sudo systemctl enable --now rclone-backup.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SMB password for `danlin`
|
||||||
|
|
||||||
|
The share configuration:
|
||||||
|
- Protected share: `daten` → requires user `danlin`
|
||||||
|
- Guest share: `daten-share` → guest access allowed
|
||||||
|
|
||||||
|
Set the Samba password for `danlin` (independent from system login password):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo smbpasswd -a danlin
|
||||||
|
```
|
||||||
|
|
||||||
|
Useful commands:
|
||||||
|
```bash
|
||||||
|
sudo pdbedit -L # list Samba users
|
||||||
|
sudo systemctl status samba
|
||||||
|
sudo journalctl -u samba -e
|
||||||
|
```
|
||||||
|
|
||||||
|
Access examples:
|
||||||
|
- Windows: `\\fileserver\daten` or `\\fileserver\daten-share`
|
||||||
|
- macOS Finder: Go → Connect to Server → `smb://fileserver/daten` or `smb://fileserver/daten-share`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
- rclone auth on headless: use `rclone authorize 'drive'` on a desktop and paste token
|
||||||
|
- Permissions: ensure `/data/daten` and `/data/daten/share` exist and are writable (`systemd-tmpfiles` creates them)
|
||||||
|
- Firewall: SMB ports 139/445 are open by module config
|
||||||
Generated
+27
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1762756533,
|
||||||
|
"narHash": "sha256-HiRDeUOD1VLklHeOmaKDzf+8Hb7vSWPVFcWwaTrpm+U=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "c2448301fb856e351aab33e64c33a3fc8bcf637d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-25.05",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
description = "Home infra for lindenfelser.de (fileserver + gateway)";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, ... }:
|
||||||
|
let
|
||||||
|
system = "x86_64-linux";
|
||||||
|
lib = nixpkgs.lib;
|
||||||
|
in {
|
||||||
|
nixosConfigurations = {
|
||||||
|
fileserver = lib.nixosSystem {
|
||||||
|
inherit system;
|
||||||
|
modules = [
|
||||||
|
./hosts/fileserver/configuration.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
gateway = lib.nixosSystem {
|
||||||
|
inherit system;
|
||||||
|
modules = [
|
||||||
|
./hosts/gateway/configuration.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./hardware-configuration.nix
|
||||||
|
../../modules/common.nix
|
||||||
|
../../modules/fileserver.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
networking.hostName = "fileserver";
|
||||||
|
|
||||||
|
networking.useDHCP = false;
|
||||||
|
networking.interfaces.ens18.ipv4.addresses = [
|
||||||
|
{
|
||||||
|
address = "10.202.82.6";
|
||||||
|
prefixLength = 24;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
networking.defaultGateway = "10.202.82.1";
|
||||||
|
networking.nameservers = [ "10.202.82.3" "10.202.82.4" ];
|
||||||
|
|
||||||
|
system.stateVersion = "25.05";
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Do not modify this file! It was generated by ‘nixos-generate-config’
|
||||||
|
# and may be overwritten by future invocations. Please make changes
|
||||||
|
# to /etc/nixos/configuration.nix instead.
|
||||||
|
{ config, lib, pkgs, modulesPath, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports =
|
||||||
|
[ (modulesPath + "/profiles/qemu-guest.nix")
|
||||||
|
];
|
||||||
|
|
||||||
|
boot.initrd.availableKernelModules = [ "uhci_hcd" "ehci_pci" "ahci" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod" ];
|
||||||
|
boot.initrd.kernelModules = [ ];
|
||||||
|
boot.kernelModules = [ "kvm-intel" ];
|
||||||
|
boot.extraModulePackages = [ ];
|
||||||
|
|
||||||
|
fileSystems."/" =
|
||||||
|
{ device = "/dev/disk/by-uuid/8e49deb3-6b40-4f80-80d3-33b67dc26ae5";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems."/boot" =
|
||||||
|
{ device = "/dev/disk/by-uuid/9329-D032";
|
||||||
|
fsType = "vfat";
|
||||||
|
options = [ "fmask=0022" "dmask=0022" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems."/data" =
|
||||||
|
{ device = "/dev/disk/by-uuid/06615467-f4bf-46b2-9099-fa4a11a5a241";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
|
||||||
|
swapDevices = [ ];
|
||||||
|
|
||||||
|
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
|
||||||
|
# (the default) this is the recommended approach. When using systemd-networkd it's
|
||||||
|
# still possible to use this option, but it's recommended to use it in conjunction
|
||||||
|
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
|
||||||
|
networking.useDHCP = lib.mkDefault true;
|
||||||
|
# networking.interfaces.ens18.useDHCP = lib.mkDefault true;
|
||||||
|
|
||||||
|
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./hardware-configuration.nix
|
||||||
|
../../modules/common.nix
|
||||||
|
../../modules/gateway.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
networking.hostName = "gateway";
|
||||||
|
networking.useDHCP = false;
|
||||||
|
networking.interfaces.ens18.ipv4.addresses = [
|
||||||
|
{
|
||||||
|
address = "10.202.82.3";
|
||||||
|
prefixLength = 24;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
address = "10.202.82.4";
|
||||||
|
prefixLength = 24;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
networking.defaultGateway = "10.202.82.1";
|
||||||
|
networking.nameservers = [ "10.202.82.3" "10.202.82.4" ];
|
||||||
|
|
||||||
|
system.stateVersion = "25.05";
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# Do not modify this file! It was generated by ‘nixos-generate-config’
|
||||||
|
# and may be overwritten by future invocations. Please make changes
|
||||||
|
# to /etc/nixos/configuration.nix instead.
|
||||||
|
{ config, lib, pkgs, modulesPath, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports =
|
||||||
|
[ (modulesPath + "/profiles/qemu-guest.nix")
|
||||||
|
];
|
||||||
|
|
||||||
|
boot.initrd.availableKernelModules = [ "uhci_hcd" "ehci_pci" "ahci" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod" ];
|
||||||
|
boot.initrd.kernelModules = [ ];
|
||||||
|
boot.kernelModules = [ "kvm-intel" ];
|
||||||
|
boot.extraModulePackages = [ ];
|
||||||
|
|
||||||
|
fileSystems."/" =
|
||||||
|
{ device = "/dev/disk/by-uuid/c419a2e8-28c6-4035-9dca-e1e438bb4726";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems."/boot" =
|
||||||
|
{ device = "/dev/disk/by-uuid/8962-D9DE";
|
||||||
|
fsType = "vfat";
|
||||||
|
options = [ "fmask=0022" "dmask=0022" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
swapDevices = [ ];
|
||||||
|
|
||||||
|
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
|
||||||
|
# (the default) this is the recommended approach. When using systemd-networkd it's
|
||||||
|
# still possible to use this option, but it's recommended to use it in conjunction
|
||||||
|
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
|
||||||
|
networking.useDHCP = lib.mkDefault true;
|
||||||
|
# networking.interfaces.ens18.useDHCP = lib.mkDefault true;
|
||||||
|
|
||||||
|
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
### CONFIG ###
|
||||||
|
OS_DISK=/dev/sda
|
||||||
|
DATA_DISK=/dev/sdb
|
||||||
|
HOSTNAME=fileserver
|
||||||
|
STATE_VERSION="25.05"
|
||||||
|
TIMEZONE="Europe/Berlin"
|
||||||
|
USERNAME="danlin"
|
||||||
|
PASSWORD="changeme"
|
||||||
|
ROOT_PASS="root"
|
||||||
|
|
||||||
|
echo ">>> WARNUNG: ALLE DATEN auf ${OS_DISK} und ${DATA_DISK} werden GELÖSCHT!"
|
||||||
|
echo ">>> Warte 5 Sekunden... (STRG+C zum Abbrechen)"
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
echo ">>> Partitioniere OS-Disk (${OS_DISK}) für EFI + ROOT..."
|
||||||
|
parted "${OS_DISK}" --script mklabel gpt
|
||||||
|
parted "${OS_DISK}" --script mkpart ESP fat32 1MiB 513MiB
|
||||||
|
parted "${OS_DISK}" --script set 1 esp on
|
||||||
|
parted "${OS_DISK}" --script mkpart primary ext4 513MiB 100%
|
||||||
|
|
||||||
|
echo ">>> Formatiere OS-Partitionen..."
|
||||||
|
mkfs.fat -F32 "${OS_DISK}1"
|
||||||
|
mkfs.ext4 -F "${OS_DISK}2"
|
||||||
|
|
||||||
|
echo ">>> Partitioniere DATA-Disk (${DATA_DISK})..."
|
||||||
|
parted "${DATA_DISK}" --script mklabel gpt
|
||||||
|
parted "${DATA_DISK}" --script mkpart primary ext4 0% 100%
|
||||||
|
|
||||||
|
echo ">>> Formatiere DATA-Partition..."
|
||||||
|
mkfs.ext4 -F "${DATA_DISK}1"
|
||||||
|
|
||||||
|
echo ">>> Mounten..."
|
||||||
|
mount "${OS_DISK}2" /mnt
|
||||||
|
mkdir -p /mnt/boot
|
||||||
|
mount "${OS_DISK}1" /mnt/boot
|
||||||
|
|
||||||
|
mkdir -p /mnt/data
|
||||||
|
mount "${DATA_DISK}1" /mnt/data
|
||||||
|
|
||||||
|
echo ">>> Generiere NixOS-Config..."
|
||||||
|
nixos-generate-config --root /mnt
|
||||||
|
|
||||||
|
CONFIG=/mnt/etc/nixos/configuration.nix
|
||||||
|
HWCFG=/mnt/etc/nixos/hardware-configuration.nix
|
||||||
|
|
||||||
|
echo ">>> Schreibe minimale configuration.nix..."
|
||||||
|
|
||||||
|
cat > "${CONFIG}" <<EOF
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [ ./hardware-configuration.nix ];
|
||||||
|
|
||||||
|
boot.loader.systemd-boot.enable = true;
|
||||||
|
boot.loader.efi.canTouchEfiVariables = true;
|
||||||
|
|
||||||
|
security.sudo.wheelNeedsPassword = false;
|
||||||
|
|
||||||
|
networking.hostName = "${HOSTNAME}";
|
||||||
|
time.timeZone = "${TIMEZONE}";
|
||||||
|
|
||||||
|
services.openssh.enable = true;
|
||||||
|
|
||||||
|
networking.firewall = {
|
||||||
|
enable = true;
|
||||||
|
allowedTCPPorts = [ 22 ];
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.${USERNAME} = {
|
||||||
|
isNormalUser = true;
|
||||||
|
extraGroups = [ "wheel" ];
|
||||||
|
initialPassword = "${PASSWORD}";
|
||||||
|
openssh.authorizedKeys.keys = [
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAzAW0DTpdQJaQOWDC3YJCmPc/veBQ0R3e1q9nOlWgxC danlin@MacBook-Pro-von-Daniel.fritz.box"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.root.initialPassword = "${ROOT_PASS}";
|
||||||
|
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
vim
|
||||||
|
htop
|
||||||
|
rclone
|
||||||
|
];
|
||||||
|
|
||||||
|
networking.useDHCP = false;
|
||||||
|
networking.interfaces.ens18.ipv4.addresses = [
|
||||||
|
{
|
||||||
|
address = "10.202.82.6";
|
||||||
|
prefixLength = 24;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
networking.defaultGateway = "10.0.10.1";
|
||||||
|
networking.nameservers = [ "10.202.82.3" "10.202.82.4" ];
|
||||||
|
|
||||||
|
system.stateVersion = "${STATE_VERSION}";
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo ">>> /data in hardware-configuration ergänzen (falls fehlt)..."
|
||||||
|
DATA_UUID=$(blkid -s UUID -o value "${DATA_DISK}1")
|
||||||
|
|
||||||
|
if ! grep -q '"/data"' "${HWCFG}"; then
|
||||||
|
cat >> "${HWCFG}" <<EOF
|
||||||
|
|
||||||
|
fileSystems."/data" = {
|
||||||
|
device = "/dev/disk/by-uuid/${DATA_UUID}";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ">>> Starte nixos-install..."
|
||||||
|
nixos-install
|
||||||
|
|
||||||
|
echo ">>> Erzeuge README mit rclone/SMB-Hinweisen unter /mnt/root/README-rclone-smb.txt ..."
|
||||||
|
cat > /mnt/root/README-rclone-smb.txt <<'TXT'
|
||||||
|
Fileserver: rclone + SMB Quickstart\n\n1) rclone Remote erstellen (Name: gdrive)\n sudo -i\n rclone config\n # storage: drive (Google Drive), headless: No und Token von Desktop übernehmen\n\n Testen:\n rclone lsd gdrive:\n rclone mkdir gdrive:backup-daten\n\n Manuell starten:\n systemctl start rclone-backup.service\n journalctl -u rclone-backup -e\n tail -n 100 /var/log/rclone-backup.log\n\n Timer aktivieren/prüfen:\n systemctl list-timers '*rclone*'\n systemctl enable --now rclone-backup.timer\n\n2) SMB-Passwort für Benutzer danlin setzen\n smbpasswd -a danlin\n\n Shares:\n - daten (nur mit Benutzer danlin)\n - daten-share (Gastzugriff erlaubt)\n\n Zugriff:\n - Windows: \\fileserver\daten oder \\fileserver\daten-share\n - macOS: smb://fileserver/daten oder smb://fileserver/daten-share\nTXT
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "===== NACHINSTALLATION HINWEISE ====="
|
||||||
|
echo "- rclone Remote 'gdrive' einrichten: sudo -i && rclone config"
|
||||||
|
echo "- Backup testen: systemctl start rclone-backup.service && journalctl -u rclone-backup -e"
|
||||||
|
echo "- SMB-Passwort setzen: sudo smbpasswd -a danlin"
|
||||||
|
echo "- Details siehe: /root/README.md"
|
||||||
|
|
||||||
|
echo ">>> Fertig. Jetzt reboot ausführen."
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
### CONFIG ###
|
||||||
|
OS_DISK=/dev/sda
|
||||||
|
HOSTNAME=gateway
|
||||||
|
STATE_VERSION="25.05"
|
||||||
|
TIMEZONE="Europe/Berlin"
|
||||||
|
USERNAME="danlin"
|
||||||
|
PASSWORD="changeme"
|
||||||
|
ROOT_PASS="root"
|
||||||
|
|
||||||
|
echo ">>> WARNUNG: ALLE DATEN auf ${OS_DISK} werden GELÖSCHT!"
|
||||||
|
echo ">>> Warte 5 Sekunden... (STRG+C zum Abbrechen)"
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
echo ">>> Partitioniere OS-Disk (${OS_DISK}) für EFI + ROOT..."
|
||||||
|
parted "${OS_DISK}" --script mklabel gpt
|
||||||
|
parted "${OS_DISK}" --script mkpart ESP fat32 1MiB 513MiB
|
||||||
|
parted "${OS_DISK}" --script set 1 esp on
|
||||||
|
parted "${OS_DISK}" --script mkpart primary ext4 513MiB 100%
|
||||||
|
|
||||||
|
echo ">>> Formatiere OS-Partitionen..."
|
||||||
|
mkfs.fat -F32 "${OS_DISK}1"
|
||||||
|
mkfs.ext4 -F "${OS_DISK}2"
|
||||||
|
|
||||||
|
echo ">>> Mounten..."
|
||||||
|
mount "${OS_DISK}2" /mnt
|
||||||
|
mkdir -p /mnt/boot
|
||||||
|
mount "${OS_DISK}1" /mnt/boot
|
||||||
|
|
||||||
|
echo ">>> Generiere NixOS-Config..."
|
||||||
|
nixos-generate-config --root /mnt
|
||||||
|
|
||||||
|
CONFIG=/mnt/etc/nixos/configuration.nix
|
||||||
|
|
||||||
|
echo ">>> Schreibe minimale configuration.nix..."
|
||||||
|
|
||||||
|
cat > "${CONFIG}" <<EOF
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [ ./hardware-configuration.nix ];
|
||||||
|
|
||||||
|
boot.loader.systemd-boot.enable = true;
|
||||||
|
boot.loader.efi.canTouchEfiVariables = true;
|
||||||
|
|
||||||
|
security.sudo.wheelNeedsPassword = false;
|
||||||
|
|
||||||
|
networking.hostName = "${HOSTNAME}";
|
||||||
|
networking.useDHCP = false;
|
||||||
|
networking.interfaces.ens18.ipv4.addresses = [
|
||||||
|
{
|
||||||
|
address = "10.202.82.3";
|
||||||
|
prefixLength = 24;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
address = "10.202.82.4";
|
||||||
|
prefixLength = 24;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
networking.defaultGateway = "10.202.82.1";
|
||||||
|
networking.nameservers = [ "9.9.9.9" "1.1.1.1" "8.8.8.8" ];
|
||||||
|
|
||||||
|
time.timeZone = "${TIMEZONE}";
|
||||||
|
|
||||||
|
services.openssh.enable = true;
|
||||||
|
|
||||||
|
networking.firewall = {
|
||||||
|
enable = true;
|
||||||
|
allowedTCPPorts = [ 22 ];
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.${USERNAME} = {
|
||||||
|
isNormalUser = true;
|
||||||
|
extraGroups = [ "wheel" ];
|
||||||
|
initialPassword = "${PASSWORD}";
|
||||||
|
openssh.authorizedKeys.keys = [
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAzAW0DTpdQJaQOWDC3YJCmPc/veBQ0R3e1q9nOlWgxC danlin@MacBook-Pro-von-Daniel.fritz.box"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.root.initialPassword = "${ROOT_PASS}";
|
||||||
|
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
vim
|
||||||
|
htop
|
||||||
|
];
|
||||||
|
|
||||||
|
system.stateVersion = "${STATE_VERSION}";
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo ">>> Starte nixos-install..."
|
||||||
|
nixos-install
|
||||||
|
|
||||||
|
echo ">>> Fertig. Jetzt reboot ausführen."
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
# Boot configuration
|
||||||
|
boot.loader.systemd-boot.enable = true;
|
||||||
|
boot.loader.efi.canTouchEfiVariables = true;
|
||||||
|
|
||||||
|
# Timezone
|
||||||
|
time.timeZone = "Europe/Berlin";
|
||||||
|
i18n.defaultLocale = "en_US.UTF-8";
|
||||||
|
|
||||||
|
# Security & sudo
|
||||||
|
security.sudo.enable = true;
|
||||||
|
security.sudo.wheelNeedsPassword = false;
|
||||||
|
|
||||||
|
# SSH configuration
|
||||||
|
services.openssh = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
PermitRootLogin = "no";
|
||||||
|
PasswordAuthentication = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# User configuration
|
||||||
|
users.users.danlin = {
|
||||||
|
isNormalUser = true;
|
||||||
|
extraGroups = [ "wheel" ];
|
||||||
|
initialPassword = "changeme";
|
||||||
|
openssh.authorizedKeys.keys = [
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAzAW0DTpdQJaQOWDC3YJCmPc/veBQ0R3e1q9nOlWgxC danlin@MacBook-Pro-von-Daniel.fritz.box"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Base system packages
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
neovim
|
||||||
|
rclone
|
||||||
|
htop
|
||||||
|
curl
|
||||||
|
wget
|
||||||
|
];
|
||||||
|
|
||||||
|
# Automatic updates (security)
|
||||||
|
system.autoUpgrade = {
|
||||||
|
enable = false; # Manual control via flake
|
||||||
|
allowReboot = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,320 @@
|
|||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
########################################
|
||||||
|
# Samba File Sharing
|
||||||
|
########################################
|
||||||
|
services.samba = {
|
||||||
|
enable = true;
|
||||||
|
openFirewall = true;
|
||||||
|
# Enable nmbd for NetBIOS name resolution and browsing (legacy Windows/macOS)
|
||||||
|
nmbd.enable = true;
|
||||||
|
# Enable WSD discovery (Windows) and mDNS/NetBIOS advertisement improvements via wsdd + Avahi.
|
||||||
|
# "smb encrypt" set to desired to avoid discovery issues with some clients while still allowing encryption.
|
||||||
|
settings = {
|
||||||
|
global = {
|
||||||
|
workgroup = "WORKGROUP";
|
||||||
|
"server string" = "NixFiles";
|
||||||
|
"netbios name" = "fileserver";
|
||||||
|
security = "user";
|
||||||
|
"map to guest" = "bad user";
|
||||||
|
"unix extensions" = "no";
|
||||||
|
|
||||||
|
# macOS optimizations
|
||||||
|
"vfs objects" = "catia fruit streams_xattr";
|
||||||
|
"fruit:aapl" = "yes";
|
||||||
|
"fruit:metadata" = "stream";
|
||||||
|
"fruit:resource" = "stream";
|
||||||
|
"fruit:model" = "MacSamba";
|
||||||
|
"fruit:advertise_fullsync" = "yes";
|
||||||
|
"ea support" = "yes";
|
||||||
|
|
||||||
|
# Time Machine support
|
||||||
|
"fruit:time machine" = "yes";
|
||||||
|
"fruit:time machine max size" = "500G";
|
||||||
|
|
||||||
|
# Prefer encryption but do not require it for basic browsing/guest visibility
|
||||||
|
"smb encrypt" = "desired";
|
||||||
|
|
||||||
|
# Better macOS discovery
|
||||||
|
"min protocol" = "SMB2";
|
||||||
|
"max protocol" = "SMB3";
|
||||||
|
"server role" = "standalone server";
|
||||||
|
# Allow Apple extended attributes & full sync advertisement already configured above.
|
||||||
|
};
|
||||||
|
|
||||||
|
daten = {
|
||||||
|
path = "/data/daten";
|
||||||
|
browseable = "yes";
|
||||||
|
"read only" = "no";
|
||||||
|
"valid users" = "danlin";
|
||||||
|
"fruit:time machine" = "no";
|
||||||
|
};
|
||||||
|
|
||||||
|
daten-share = {
|
||||||
|
path = "/data/daten/share";
|
||||||
|
browseable = "yes";
|
||||||
|
"read only" = "no";
|
||||||
|
"guest ok" = "yes"; # Gastzugriff erlaubt
|
||||||
|
public = "yes"; # Alias für guest ok
|
||||||
|
# Removed "valid users" to allow true guest access; danlin can still write via group permissions
|
||||||
|
"fruit:time machine" = "no";
|
||||||
|
};
|
||||||
|
|
||||||
|
timemachine = {
|
||||||
|
path = "/data/backup/timemachine";
|
||||||
|
browseable = "yes";
|
||||||
|
"read only" = "no";
|
||||||
|
"valid users" = "danlin";
|
||||||
|
"fruit:time machine" = "yes";
|
||||||
|
"fruit:time machine max size" = "500G";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Network Discovery Services (Avahi + WSD)
|
||||||
|
########################################
|
||||||
|
# Avahi for macOS Finder discovery (mDNS) and general _smb._tcp advertising
|
||||||
|
services.avahi = {
|
||||||
|
enable = true;
|
||||||
|
nssmdns4 = true; # Provide .local resolution
|
||||||
|
openFirewall = true;
|
||||||
|
publish = {
|
||||||
|
enable = true;
|
||||||
|
userServices = true;
|
||||||
|
};
|
||||||
|
# Explicit SMB service advertisement for macOS Finder
|
||||||
|
extraServiceFiles = {
|
||||||
|
smb = ''
|
||||||
|
<?xml version="1.0" standalone='no'?>
|
||||||
|
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
|
||||||
|
<service-group>
|
||||||
|
<name replace-wildcards="yes">%h</name>
|
||||||
|
<service>
|
||||||
|
<type>_smb._tcp</type>
|
||||||
|
<port>445</port>
|
||||||
|
</service>
|
||||||
|
<service>
|
||||||
|
<type>_device-info._tcp</type>
|
||||||
|
<port>0</port>
|
||||||
|
<txt-record>model=TimeCapsule8,119</txt-record>
|
||||||
|
</service>
|
||||||
|
<service>
|
||||||
|
<type>_adisk._tcp</type>
|
||||||
|
<port>9</port>
|
||||||
|
<txt-record>sys=waMa=0,adVF=0x100</txt-record>
|
||||||
|
<txt-record>dk0=adVN=timemachine,adVF=0x82</txt-record>
|
||||||
|
</service>
|
||||||
|
</service-group>
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Windows network neighborhood discovery via Web Services for Devices (WSD)
|
||||||
|
# NixOS does not provide a `services.wsdd` option in this release, so define a systemd unit.
|
||||||
|
systemd.services.wsdd = {
|
||||||
|
description = "Web Services Discovery daemon for Samba (wsdd)";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = [ "network.target" "samba-smbd.service" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "simple";
|
||||||
|
ExecStart = "${pkgs.wsdd}/bin/wsdd --hostname fileserver --workgroup WORKGROUP";
|
||||||
|
Restart = "on-failure";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Directory Structure
|
||||||
|
########################################
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d /data 0775 root root -"
|
||||||
|
"d /data/daten 0775 danlin users -"
|
||||||
|
"d /data/daten/share 0775 danlin users -"
|
||||||
|
|
||||||
|
# DJ-Struktur
|
||||||
|
"d /data/daten/DJing 0775 danlin users -"
|
||||||
|
"d /data/daten/DJing/Music 0775 danlin users -"
|
||||||
|
"d /data/daten/DJing/Data 0775 danlin users -"
|
||||||
|
"d /data/daten/DJing/Inbox 0775 danlin users -"
|
||||||
|
|
||||||
|
# Time Machine backup
|
||||||
|
"d /data/backup 0775 root root -"
|
||||||
|
"d /data/backup/timemachine 0775 danlin users -"
|
||||||
|
|
||||||
|
# dj-beets project
|
||||||
|
"d /opt/dj-beets 0755 root root -"
|
||||||
|
];
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Virtualization & Containers
|
||||||
|
########################################
|
||||||
|
# Virtualization
|
||||||
|
virtualisation.podman.enable = true;
|
||||||
|
virtualisation.oci-containers.backend = "podman";
|
||||||
|
|
||||||
|
virtualisation.oci-containers.containers.filebrowser = {
|
||||||
|
image = "gtstef/filebrowser:stable";
|
||||||
|
autoStart = true;
|
||||||
|
ports = [ "8080:80" ];
|
||||||
|
volumes = [ "/data:/srv" ];
|
||||||
|
environment = {
|
||||||
|
TZ = "Europe/Berlin";
|
||||||
|
FILEBROWSER_ADMIN_PASSWORD = "CHANGE_ME";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
virtualisation.oci-containers.containers.dj-beets-cli = {
|
||||||
|
image = "dj-beets:latest";
|
||||||
|
autoStart = false; # Run manually or via systemd service
|
||||||
|
volumes = [
|
||||||
|
"/data/daten/DJing:/data/daten/DJing"
|
||||||
|
"/opt/dj-beets/config.yaml:/etc/beets/config.yaml:ro"
|
||||||
|
"/opt/dj-beets/beatport_token.json:/opt/dj-beets/beatport_token.json"
|
||||||
|
];
|
||||||
|
environment = {
|
||||||
|
BEETS_CONFIG = "/etc/beets/config.yaml";
|
||||||
|
};
|
||||||
|
cmd = [ "tail" "-f" "/dev/null" ]; # Keep container alive
|
||||||
|
};
|
||||||
|
|
||||||
|
virtualisation.oci-containers.containers.dj-beets-web = {
|
||||||
|
image = "dj-beets:latest";
|
||||||
|
autoStart = true;
|
||||||
|
ports = [ "8337:8337" ];
|
||||||
|
volumes = [
|
||||||
|
"/data/daten/DJing:/data/daten/DJing"
|
||||||
|
"/opt/dj-beets/config.yaml:/etc/beets/config.yaml:ro"
|
||||||
|
"/opt/dj-beets/beatport_token.json:/opt/dj-beets/beatport_token.json"
|
||||||
|
];
|
||||||
|
environment = {
|
||||||
|
BEETS_CONFIG = "/etc/beets/config.yaml";
|
||||||
|
};
|
||||||
|
cmd = [ "beet" "web" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Backup Services
|
||||||
|
########################################
|
||||||
|
systemd.services.rclone-backup = {
|
||||||
|
description = "Backup /data to Google Drive via rclone";
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = ''
|
||||||
|
${pkgs.rclone}/bin/rclone sync /data gdrive:backup-daten \
|
||||||
|
--fast-list \
|
||||||
|
--drive-stop-on-upload-limit \
|
||||||
|
--log-file=/var/log/rclone-backup.log \
|
||||||
|
--log-level=INFO
|
||||||
|
'';
|
||||||
|
User = "root";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.timers.rclone-backup = {
|
||||||
|
wantedBy = [ "timers.target" ];
|
||||||
|
timerConfig = {
|
||||||
|
OnCalendar = "03:00";
|
||||||
|
Persistent = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# System Packages
|
||||||
|
########################################
|
||||||
|
environment.systemPackages = [
|
||||||
|
pkgs.git
|
||||||
|
pkgs.tmux
|
||||||
|
pkgs.htop
|
||||||
|
pkgs.podman-compose
|
||||||
|
|
||||||
|
# Beets CLI wrapper
|
||||||
|
(pkgs.writeShellScriptBin "beet" ''
|
||||||
|
exec ${pkgs.podman}/bin/podman run --rm -it \
|
||||||
|
-v /data/daten/DJing:/data/daten/DJing \
|
||||||
|
-v /opt/dj-beets/config.yaml:/etc/beets/config.yaml:ro \
|
||||||
|
-v /opt/dj-beets/beatport_token.json:/opt/dj-beets/beatport_token.json \
|
||||||
|
-e BEETS_CONFIG=/etc/beets/config.yaml \
|
||||||
|
-u $(id -u):$(id -g) \
|
||||||
|
dj-beets:latest \
|
||||||
|
beet "$@"
|
||||||
|
'')
|
||||||
|
];
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# DJ-Beets Services
|
||||||
|
########################################
|
||||||
|
# Systemd service to build dj-beets image on deploy
|
||||||
|
systemd.services.dj-beets-build = {
|
||||||
|
description = "Build dj-beets Docker image";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
before = [ "podman-dj-beets-web.service" ];
|
||||||
|
after = [ "podman.service" ];
|
||||||
|
wants = [ "podman.service" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
ExecStart = pkgs.writeShellScript "build-dj-beets" ''
|
||||||
|
set -euo pipefail
|
||||||
|
if [ ! -f /opt/dj-beets/Dockerfile ]; then
|
||||||
|
echo "ERROR: Dockerfile not found at /opt/dj-beets" >&2
|
||||||
|
echo "Deploy with 'make deploy-fileserver' first" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
cd /opt/dj-beets
|
||||||
|
${pkgs.podman}/bin/podman build -t dj-beets:latest .
|
||||||
|
echo "dj-beets image built successfully"
|
||||||
|
'';
|
||||||
|
User = "root";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Auto-import service using Docker container
|
||||||
|
systemd.services.dj-beets-autoimport = {
|
||||||
|
description = "Auto-import music into beets library via Docker";
|
||||||
|
after = [ "dj-beets-build.service" ];
|
||||||
|
wants = [ "dj-beets-build.service" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = pkgs.writeShellScript "beets-autoimport" ''
|
||||||
|
set -euo pipefail
|
||||||
|
INBOX="/data/daten/DJing/Inbox"
|
||||||
|
LOG="/data/daten/DJing/Data/BeetsAutoImport.log"
|
||||||
|
|
||||||
|
# Check if inbox has files
|
||||||
|
if ! find "$INBOX" -mindepth 1 -maxdepth 1 -type f -print -quit | grep -q .; then
|
||||||
|
echo "[$(date)] No files in Inbox, skipping" >> "$LOG"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[$(date)] Starting beets import" >> "$LOG"
|
||||||
|
${pkgs.podman}/bin/podman run --rm \
|
||||||
|
-v /data/daten/DJing:/data/daten/DJing \
|
||||||
|
-v /opt/dj-beets/config.yaml:/etc/beets/config.yaml:ro \
|
||||||
|
-v /opt/dj-beets/beatport_token.json:/opt/dj-beets/beatport_token.json \
|
||||||
|
-e BEETS_CONFIG=/etc/beets/config.yaml \
|
||||||
|
localhost/dj-beets:latest \
|
||||||
|
beet import -q /data/daten/DJing/Inbox >> "$LOG" 2>&1
|
||||||
|
'';
|
||||||
|
# Use root so the service can access the root Podman image storage
|
||||||
|
User = "root";
|
||||||
|
Group = "root";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.timers.dj-beets-autoimport = {
|
||||||
|
wantedBy = [ "timers.target" ];
|
||||||
|
timerConfig = {
|
||||||
|
OnCalendar = "*:0/10"; # Every 10 minutes
|
||||||
|
Persistent = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Firewall
|
||||||
|
########################################
|
||||||
|
networking.firewall.enable = true;
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = [ 22 139 445 8080 8337 5357 ]; # 5357 WSD
|
||||||
|
networking.firewall.allowedUDPPorts = [ 137 138 3702 5353 ]; # NetBIOS + WSD (3702) + mDNS (5353)
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
########################################
|
||||||
|
# AdGuardHome (DNS / DNS-Filter)
|
||||||
|
########################################
|
||||||
|
services.adguardhome = {
|
||||||
|
enable = true;
|
||||||
|
host = "127.0.0.1";
|
||||||
|
port = 3000; # Web-UI
|
||||||
|
};
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Caddy (Reverse Proxy)
|
||||||
|
########################################
|
||||||
|
services.caddy = {
|
||||||
|
enable = true;
|
||||||
|
email = "daniel@lindenfelser.de";
|
||||||
|
virtualHosts = {
|
||||||
|
"files.home.lindenfelser.de" = {
|
||||||
|
extraConfig = ''
|
||||||
|
reverse_proxy 10.202.82.6:8080
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"cloud.home.lindenfelser.de" = {
|
||||||
|
extraConfig = ''
|
||||||
|
reverse_proxy 10.202.82.6:8080
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"dns.home.lindenfelser.de" = {
|
||||||
|
extraConfig = ''
|
||||||
|
reverse_proxy 127.0.0.1:3000
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"smart.home.lindenfelser.de" = {
|
||||||
|
extraConfig = ''
|
||||||
|
reverse_proxy 10.202.82.30:8123
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Firewall
|
||||||
|
########################################
|
||||||
|
networking.firewall.enable = true;
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = [
|
||||||
|
22 # SSH
|
||||||
|
80 # HTTP (Caddy)
|
||||||
|
443 # HTTPS (Caddy)
|
||||||
|
];
|
||||||
|
|
||||||
|
networking.firewall.allowedUDPPorts = [
|
||||||
|
53 # DNS (AdGuardHome)
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
.git
|
||||||
|
.venv
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
.Python
|
||||||
|
poetry.lock
|
||||||
|
data/
|
||||||
|
.DS_Store
|
||||||
Vendored
+22
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Python Debugger: Current File",
|
||||||
|
"type": "debugpy",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/main.py",
|
||||||
|
"args": [
|
||||||
|
"--import",
|
||||||
|
"/Volumes/daten/DJing/Inbox",
|
||||||
|
"--config",
|
||||||
|
"${workspaceFolder}/config.yaml"
|
||||||
|
],
|
||||||
|
"console": "integratedTerminal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
# Install system dependencies for beets plugins
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
# For chromaprint/acoustid fingerprinting
|
||||||
|
libchromaprint-tools \
|
||||||
|
# For media file handling
|
||||||
|
ffmpeg \
|
||||||
|
# Build tools for some Python packages
|
||||||
|
gcc \
|
||||||
|
g++ \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Create app directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy dependency files
|
||||||
|
COPY pyproject.toml ./
|
||||||
|
|
||||||
|
# Install Poetry
|
||||||
|
RUN pip install --no-cache-dir poetry
|
||||||
|
|
||||||
|
# Configure Poetry to not create virtual env (we're in container)
|
||||||
|
RUN poetry config virtualenvs.create false
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN poetry install --no-root --no-interaction --no-ansi
|
||||||
|
|
||||||
|
# Copy config
|
||||||
|
COPY config.yaml /etc/beets/config.yaml
|
||||||
|
|
||||||
|
# Create data directories
|
||||||
|
RUN mkdir -p /data/Music /data/Inbox
|
||||||
|
|
||||||
|
# Set environment
|
||||||
|
ENV BEETS_CONFIG=/etc/beets/config.yaml
|
||||||
|
|
||||||
|
# Default command
|
||||||
|
CMD ["beet", "--help"]
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
# DJ Beets Setup
|
||||||
|
|
||||||
|
Diese Poetry-Umgebung ist dein eigenes Projekt als Ersatz für beets.
|
||||||
|
Hier verwaltest du Versionen und baust eigene Tools ein.
|
||||||
|
|
||||||
|
## Verwendung
|
||||||
|
|
||||||
|
### Lokal testen
|
||||||
|
```bash
|
||||||
|
cd src/dj-beets
|
||||||
|
poetry install
|
||||||
|
poetry run beet --version
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deployment auf fileserver
|
||||||
|
|
||||||
|
Das komplette `src/dj-beets/` Projekt wird beim Deploy nach `/opt/dj-beets` kopiert:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make deploy-fileserver
|
||||||
|
```
|
||||||
|
|
||||||
|
Dies:
|
||||||
|
1. Kopiert dein `src/dj-beets/` nach `/opt/dj-beets` auf dem Server
|
||||||
|
2. Installiert alle Dependencies via Poetry (beets, beatport4, beetcamp, etc.)
|
||||||
|
3. Nutzt `poetry run beet` für alle beets-Operationen
|
||||||
|
|
||||||
|
**Wichtig**: Dein lokales Projekt ist die einzige Quelle. Kein externes GitHub-Repo mehr!
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
1. **Änderungen lokal testen**:
|
||||||
|
```bash
|
||||||
|
cd src/dj-beets
|
||||||
|
poetry install
|
||||||
|
poetry run beet --version
|
||||||
|
# Deine Änderungen testen
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Änderungen committen & deployen**:
|
||||||
|
```bash
|
||||||
|
git add src/dj-beets/
|
||||||
|
git commit -m "Update dj-beets project"
|
||||||
|
git push
|
||||||
|
|
||||||
|
# Auf fileserver deployen
|
||||||
|
make deploy-fileserver
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Auf fileserver nutzen**:
|
||||||
|
```bash
|
||||||
|
ssh fileserver
|
||||||
|
cd /opt/dj-beets
|
||||||
|
poetry run beet --version
|
||||||
|
# Auto-Import läuft automatisch alle 10 Minuten via Timer
|
||||||
|
```
|
||||||
|
|
||||||
|
## Plugins
|
||||||
|
|
||||||
|
Alle Plugins werden via `pyproject.toml` verwaltet:
|
||||||
|
- **beets** ^2.3.1: Basis-Library-Manager
|
||||||
|
- **beets-beatport4** ^0.4.1: Beatport API v4 Integration
|
||||||
|
- **beetcamp** ^0.9.3: Bandcamp Autotagger
|
||||||
|
- **requests, beautifulsoup4, html5lib**: Web-Scraping Dependencies
|
||||||
|
|
||||||
|
Weitere Plugins kannst du direkt in `pyproject.toml` hinzufügen.
|
||||||
|
|
||||||
|
## Konfiguration
|
||||||
|
|
||||||
|
Die beets-Config liegt in `config.yaml` (wird nach `/opt/dj-beets/config.yaml` deployed).
|
||||||
|
|
||||||
|
### Beatport Authentication
|
||||||
|
|
||||||
|
Beatport4 nutzt Token-basierte Authentifizierung. Token wird in `/data/daten/DJing/Data/beatport_token.json` gespeichert.
|
||||||
|
|
||||||
|
**Einmalig authentifizieren:**
|
||||||
|
```bash
|
||||||
|
# Auf fileserver
|
||||||
|
ssh danlin@10.202.82.6
|
||||||
|
beet beatport4 auth
|
||||||
|
# Folge den Prompts für Beatport Login
|
||||||
|
|
||||||
|
# Oder lokal
|
||||||
|
cd src/dj-beets
|
||||||
|
poetry run beet beatport4 auth
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Token-File wird automatisch in allen Containern gemountet und bleibt nach Rebuilds erhalten.
|
||||||
|
|
||||||
|
### API Tokens
|
||||||
|
|
||||||
|
- **Discogs**: Token in `config.yaml` → `discogs.user_token`
|
||||||
|
- **Beatport**: Token-File → `/data/daten/DJing/Data/beatport_token.json`
|
||||||
|
|
||||||
|
Wichtige Pfade:
|
||||||
|
- **Library**: `/data/daten/DJing/Data/MusicLibrary.db`
|
||||||
|
- **Music**: `/data/daten/DJing/Music`
|
||||||
|
- **Inbox**: `/data/daten/DJing/Inbox`
|
||||||
|
- **Project**: `/opt/dj-beets`
|
||||||
|
|
||||||
|
## Updates
|
||||||
|
|
||||||
|
### Eigenes Projekt updaten:
|
||||||
|
```bash
|
||||||
|
# Lokal ändern
|
||||||
|
cd src/dj-beets
|
||||||
|
# z.B. neue Dependency hinzufügen
|
||||||
|
poetry add some-new-package
|
||||||
|
|
||||||
|
# Deployen
|
||||||
|
make deploy-fileserver
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dependency-Updates:
|
||||||
|
```bash
|
||||||
|
cd src/dj-beets
|
||||||
|
poetry update
|
||||||
|
make deploy-fileserver
|
||||||
|
```
|
||||||
|
|
||||||
|
## Auto-Import
|
||||||
|
|
||||||
|
Der Auto-Import läuft alle 10 Minuten via systemd Timer und nutzt:
|
||||||
|
```bash
|
||||||
|
cd /opt/dj-beets
|
||||||
|
poetry run beet import -q /data/daten/DJing/Inbox
|
||||||
|
```
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"access_token": "mknr9cGYMcVD85uG8m0av2pCXaihUJ",
|
||||||
|
"expires_in": 36000,
|
||||||
|
"token_type": "Bearer",
|
||||||
|
"scope": "app:docs user:dj",
|
||||||
|
"refresh_token": "CsMC8Ya5EzLup27Y8DGDX8LwkFIvVF"
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
# Beets config for fileserver
|
||||||
|
# Paths point to /data/daten/DJing structure
|
||||||
|
|
||||||
|
# Where imported music will be organized into
|
||||||
|
directory: /data/daten/DJing/Music
|
||||||
|
|
||||||
|
# Beets SQLite library database
|
||||||
|
library: /data/daten/DJing/Data/MusicLibrary.db
|
||||||
|
|
||||||
|
# State + import log
|
||||||
|
statefile: /data/daten/DJing/Data/state.pickle
|
||||||
|
|
||||||
|
import:
|
||||||
|
write: yes # write tags to files
|
||||||
|
move: yes # move files into directory
|
||||||
|
copy: no
|
||||||
|
timid: no # don't ask for confirmation on every match
|
||||||
|
log: /data/daten/DJing/Data/BeetsImport.log
|
||||||
|
incremental: yes
|
||||||
|
autotag: yes
|
||||||
|
resume: no # don't ask to resume
|
||||||
|
quiet: yes # less output
|
||||||
|
quiet_fallback: asis # skip if no good match
|
||||||
|
none_rec_action: skip # skip if no recommendation
|
||||||
|
duplicate_action: skip # skip duplicates automatically (was: ask)
|
||||||
|
group_albums: yes
|
||||||
|
|
||||||
|
ui:
|
||||||
|
color: yes
|
||||||
|
|
||||||
|
# Friendly filesystem names
|
||||||
|
asciify_paths: yes
|
||||||
|
per_disc_numbering: yes
|
||||||
|
|
||||||
|
# Replace problematic characters in paths
|
||||||
|
replace:
|
||||||
|
"[\\/]": _
|
||||||
|
":": _
|
||||||
|
"\\?": _
|
||||||
|
'"': _
|
||||||
|
"\\*": _
|
||||||
|
"<": _
|
||||||
|
">": _
|
||||||
|
"\\|": _
|
||||||
|
|
||||||
|
# Plugins
|
||||||
|
plugins:
|
||||||
|
- fetchart
|
||||||
|
# - mbsync
|
||||||
|
# - discogs
|
||||||
|
- duplicates
|
||||||
|
- scrub
|
||||||
|
- convert
|
||||||
|
- info
|
||||||
|
- fromfilename
|
||||||
|
- ftintitle
|
||||||
|
- web
|
||||||
|
- zero
|
||||||
|
- chroma
|
||||||
|
- beatport4
|
||||||
|
- bandcamp
|
||||||
|
- inline
|
||||||
|
|
||||||
|
# Cover Art
|
||||||
|
fetchart:
|
||||||
|
auto: yes
|
||||||
|
minwidth: 600
|
||||||
|
sources: filesystem coverart itunes amazon albumart
|
||||||
|
|
||||||
|
# MusicBrainz
|
||||||
|
# mbsync:
|
||||||
|
# album_query: ""
|
||||||
|
# artist_query: ""
|
||||||
|
|
||||||
|
# Discogs (token required)
|
||||||
|
# discogs:
|
||||||
|
# data_source_mismatch_penalty: 0.5
|
||||||
|
# user_token: "yZwmWYwexyfIdEYHxkWEEKiFxLIxoTHSKozBHdpp"
|
||||||
|
|
||||||
|
# Beatport (beets-beatport4)
|
||||||
|
beatport4:
|
||||||
|
data_source_mismatch_penalty: 0.3
|
||||||
|
art: no
|
||||||
|
tokenfile: /opt/dj-beets/beatport_token.json
|
||||||
|
|
||||||
|
# Bandcamp (beetcamp)
|
||||||
|
bandcamp:
|
||||||
|
data_source_mismatch_penalty: 0.2
|
||||||
|
preferred_media: Digital
|
||||||
|
search_max: 5
|
||||||
|
art: no
|
||||||
|
include_digital_only_tracks: true
|
||||||
|
|
||||||
|
# Cleanup noisy tags
|
||||||
|
scrub:
|
||||||
|
auto: yes
|
||||||
|
|
||||||
|
zero:
|
||||||
|
fields: comments lyrics
|
||||||
|
update_database: yes
|
||||||
|
|
||||||
|
# Duplicate detection keys
|
||||||
|
duplicates:
|
||||||
|
format: "$path"
|
||||||
|
keys:
|
||||||
|
- mb_trackid
|
||||||
|
- isrc
|
||||||
|
- acoustid_fingerprint
|
||||||
|
|
||||||
|
# Acoustic fingerprinting
|
||||||
|
chroma:
|
||||||
|
auto: yes
|
||||||
|
overwrite: no
|
||||||
|
|
||||||
|
# Paths with year/month prefix for better chronology
|
||||||
|
paths:
|
||||||
|
default: >-
|
||||||
|
%time{$added,%Y/%m}/%if{$albumartist,$albumartist,$artist}/$year - $album/
|
||||||
|
$artist - $title %if{$bpm,($bpm BPM%if{$initial_key,, $initial_key})}
|
||||||
|
singleton: >-
|
||||||
|
%time{$added,%Y/%m}/Singles/$artist/
|
||||||
|
$artist - $title %if{$bpm,($bpm BPM%if{$initial_key,, $initial_key})}
|
||||||
|
comp: >-
|
||||||
|
%time{$added,%Y/%m}/Compilations/$album ($year)/
|
||||||
|
$artist - $title %if{$bpm,($bpm BPM%if{$initial_key,, $initial_key})}
|
||||||
|
|
||||||
|
# Optional conversion recipe (requires ffmpeg in PATH)
|
||||||
|
convert:
|
||||||
|
auto: no
|
||||||
|
copy_album_art: yes
|
||||||
|
formats:
|
||||||
|
mp3:
|
||||||
|
command: ffmpeg -i $source -codec:a libmp3lame -qscale:a 2 $dest
|
||||||
|
extension: mp3
|
||||||
|
|
||||||
|
# Web UI
|
||||||
|
web:
|
||||||
|
host: 0.0.0.0 # Listen on all interfaces for Docker/network access
|
||||||
|
port: 8337
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
services:
|
||||||
|
beets:
|
||||||
|
build: .
|
||||||
|
container_name: dj-beets
|
||||||
|
volumes:
|
||||||
|
# Mount local data directory
|
||||||
|
- ./data:/data
|
||||||
|
# Mount config (optional override)
|
||||||
|
- ./config.yaml:/etc/beets/config.yaml:ro
|
||||||
|
environment:
|
||||||
|
- BEETS_CONFIG=/etc/beets/config.yaml
|
||||||
|
# Keep container running for interactive commands
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
# Override default command to keep alive
|
||||||
|
command: tail -f /dev/null
|
||||||
|
|
||||||
|
beets-web:
|
||||||
|
build: .
|
||||||
|
container_name: dj-beets-web
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
- ./config.yaml:/etc/beets/config.yaml:ro
|
||||||
|
environment:
|
||||||
|
- BEETS_CONFIG=/etc/beets/config.yaml
|
||||||
|
ports:
|
||||||
|
- "8337:8337"
|
||||||
|
command: beet web
|
||||||
Generated
+1425
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
[tool.poetry]
|
||||||
|
name = "dj-beets"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Beets + DJ-Plugins (Beatport/Bandcamp) für meine Library"
|
||||||
|
authors = ["Daniel <daniel@lindenfelser.de>"]
|
||||||
|
package-mode = false
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
# Python 3.10+ (NixOS 25.05)
|
||||||
|
python = ">=3.10,<3.14"
|
||||||
|
|
||||||
|
beets = "^2.3.1"
|
||||||
|
beets-beatport4 = "^0.4.1"
|
||||||
|
|
||||||
|
# Plugin dependencies
|
||||||
|
python3-discogs-client = "^2.3.15"
|
||||||
|
flask = "^3.1.0"
|
||||||
|
pyacoustid = "^1.3.0"
|
||||||
|
beetcamp = "^0.23.0"
|
||||||
|
|
||||||
|
[tool.poetry.extras]
|
||||||
|
beets = ["chroma", "discogs"]
|
||||||
|
|
||||||
|
[tool.poetry.scripts]
|
||||||
|
beet = "beets.ui:main"
|
||||||
Reference in New Issue
Block a user