Compare commits

..

1 Commits

Author SHA1 Message Date
Alexander Heldt
389c1e73f4 Update komga-comictracker input 2026-02-14 11:20:26 +01:00
20 changed files with 121 additions and 2042 deletions

30
flake.lock generated
View File

@@ -573,27 +573,6 @@
"type": "github"
}
},
"komga-bookmanager": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1772572455,
"narHash": "sha256-B4KOdV2GQogU1XL9903qFkOFf/6H7UGa+dprNGctgMY=",
"ref": "main",
"rev": "002732d4ebc5b80c2b8522896427c9f6bee9e1d1",
"revCount": 6,
"type": "git",
"url": "ssh://gitea@git.ppp.pm:1122/alex/komga-bookmanager.git"
},
"original": {
"ref": "main",
"type": "git",
"url": "ssh://gitea@git.ppp.pm:1122/alex/komga-bookmanager.git"
}
},
"komga-comictracker": {
"inputs": {
"nixpkgs": [
@@ -601,11 +580,11 @@
]
},
"locked": {
"lastModified": 1772884418,
"narHash": "sha256-2V/JnGsIhvQoAiH7iIQkOPX5OTZVwGTOEv+nFetd4I8=",
"lastModified": 1771064292,
"narHash": "sha256-nxcgONb6iAA1AJc9A6e/lRrsDj/sKWMlfivCHD1BsYk=",
"ref": "main",
"rev": "0b91385e8906ca40cc89381bf8394ed24a116c6b",
"revCount": 49,
"rev": "51f0ba7e87f4d7ef8ae034ee16360deb4c7d7551",
"revCount": 29,
"type": "git",
"url": "ssh://gitea@git.ppp.pm:1122/alex/komga-comictracker.git"
},
@@ -807,7 +786,6 @@
"home-manager": "home-manager_2",
"hyprland": "hyprland",
"hyprland-contrib": "hyprland-contrib",
"komga-bookmanager": "komga-bookmanager",
"komga-comictracker": "komga-comictracker",
"naviterm": "naviterm",
"nh": "nh",

View File

@@ -74,12 +74,6 @@
# url = "path:/home/alex/code/own/komga-comictracker";
inputs.nixpkgs.follows = "nixpkgs";
};
komga-bookmanager = {
url = "git+ssh://gitea@git.ppp.pm:1122/alex/komga-bookmanager.git?ref=main";
# url = "path:/home/alex/code/own/komga-comictracker";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =

View File

@@ -22,13 +22,6 @@
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
extraConfig.pipewire."90-hdmi-fix" = {
"context.properties" = {
"default.clock.rate" = 48000;
"default.clock.allowed-rates" = [ 48000 ];
};
};
};
hardware = {

View File

@@ -38,11 +38,6 @@ in
efi.canTouchEfiVariables = true;
};
extraModprobeConfig = ''
options snd-intel-dspcfg dsp_driver=1
options snd_hda_intel power_save=0 power_save_controller=N
'';
};
};
}

View File

@@ -3,40 +3,33 @@
networking = {
hostName = "backwards";
wireless.enable = false;
networkmanager.enable = false;
networkmanager = {
#wireless.networks are defined in the secret `wpa_supplicant.conf`
wireless = {
enable = true;
wifi.backend = "iwd";
secretsFile = config.age.secrets.wireless-network-secrets.path;
ensureProfiles = {
environmentFiles = [
config.age.secrets.wireless-network-secrets.path
];
networks = {
"w1-f1_5G" = {
pskRaw = "ext:w1-f1_psk";
};
};
};
profiles = {
w1-f1_5G = {
connection = {
id = "w1-f1_5G";
type = "wifi";
interface-name = "wlp1s0";
};
wifi = {
ssid = "w1-f1_5G";
mode = "infrastructure";
};
wifi-security = {
key-mgmt = "wpa-psk";
psk = "$w1_f1_psk";
};
ipv4 = {
method = "manual";
addresses = "192.168.50.202/24";
gateway = "192.168.50.1";
dns = "1.1.1.1";
};
};
defaultGateway = "192.168.50.1";
nameservers = [ "1.1.1.1" ];
interfaces = {
wlp1s0 = {
useDHCP = false;
ipv4 = {
addresses = [
{
address = "192.168.50.202";
prefixLength = 24;
}
];
};
};
};

View File

@@ -10,14 +10,6 @@ in
};
config = lib.mkIf enabled {
mod.homepage.services = [
{
name = "Audiobookshelf";
port = 8000;
description = "Audiobooks & podcasts";
}
];
users.users.audiobookshelf = {
isSystemUser = true;
description = "audiobookshelf";

View File

@@ -0,0 +1,34 @@
{ lib, config, ... }:
let
enabled = config.mod.calibre-web.enable;
in
{
options = {
mod.calibre-web = {
enable = lib.mkEnableOption "add calibre-web module";
};
};
config = lib.mkIf enabled {
services = {
calibre-web = {
enable = true;
user = "storage";
group = "storage";
listen = {
ip = "0.0.0.0";
port = 8083;
};
dataDir = "/mnt/media/public/books";
options = {
calibreLibrary = "/mnt/media/public/books";
enableBookUploading = true;
};
};
};
};
}

View File

@@ -20,19 +20,6 @@
"--http-timeout=60"
];
};
"komga.ppp.pm" = {
dnsProvider = "hetzner";
environmentFile = config.age.secrets.hetzner-dns.path;
group = "nginx";
extraLegoFlags = [
"--dns.resolvers=1.1.1.1:53,8.8.8.8:53"
"--dns.propagation-wait=60s"
"--dns-timeout=60"
"--http-timeout=60"
];
};
};
};

View File

@@ -16,12 +16,12 @@ in
nginx.enable = true;
syncthing.enable = true;
transmission.enable = true;
calibre-web.enable = true;
audiobookshelf.enable = true;
jellyfin.enable = true;
immich.enable = true;
navidrome.enable = true;
komga.enable = true;
homepage.enable = true;
};
};
}

View File

@@ -6,70 +6,8 @@
}:
let
nginxEnabled = config.mod.nginx.enable;
script = pkgs.writeShellScript "bt-reset" ''
set -euo pipefail
export PATH="${
lib.makeBinPath [
pkgs.bluez
pkgs.util-linux
pkgs.kmod
pkgs.gnugrep
pkgs.coreutils
]
}"
logger -t bt-reset "Starting Bluetooth adapter reset..."
# Exit early if the adapter is already present and running
if hciconfig hci0 2>/dev/null | grep -q "UP RUNNING"; then
logger -t bt-reset "hci0 is already UP RUNNING nothing to do"
exit 0
fi
# If hci0 exists but isn't UP, try bringing it up
if hciconfig hci0 2>/dev/null; then
logger -t bt-reset "hci0 exists but not running bringing it up"
hciconfig hci0 up || true
sleep 2
if hciconfig hci0 2>/dev/null | grep -q "UP RUNNING"; then
logger -t bt-reset "hci0 is UP now"
systemctl restart bluetooth.service
logger -t bt-reset "bluetooth.service restarted done"
exit 0
fi
fi
# Hard reset: reload the btusb kernel module (works for USB adapters)
logger -t bt-reset "hci0 missing reloading btusb module..."
modprobe -r btusb 2>/dev/null || true
sleep 3
modprobe btusb
sleep 3
if hciconfig hci0 2>/dev/null; then
hciconfig hci0 up
logger -t bt-reset "hci0 restored after module reload"
else
logger -t bt-reset "ERROR: hci0 not found after module reload"
exit 1
fi
# Restart the bluetooth systemd service so bluetoothd picks up the adapter
systemctl restart bluetooth.service
logger -t bt-reset "bluetooth.service restarted done"
'';
in
{
mod.homepage.services = [
{
name = "Home Assistant";
port = 8123;
description = "Home automation";
}
];
hardware.bluetooth.enable = true;
virtualisation.oci-containers = {
@@ -119,114 +57,71 @@ in
};
};
};
# Trigger reset via udev when hci0 disappears
udev.extraRules = ''
ACTION=="remove", SUBSYSTEM=="bluetooth", KERNEL=="hci0", \
TAG+="systemd", ENV{SYSTEMD_WANTS}+="bt-reset.service"
'';
};
systemd = {
services = {
# Trigger reset on bluetoothd failure
bluetooth = {
unitConfig.OnFailure = [ "bt-reset.service" ];
};
systemd.user = {
timers = {
"update-hetzner-ha-dns" = {
unitConfig = {
Description = "updates Hetzner DNS for home-assistant";
};
bt-reset = {
description = "Reset Bluetooth adapter";
after = [ "bluetooth.service" ];
timerConfig = {
Unit = "update-hetzner-ha-dns.service";
OnCalendar = "*-*-* *:00/30:00";
Persistent = true;
};
wantedBy = [ "timers.target" ];
};
};
services = {
"update-hetzner-ha-dns" = {
unitConfig = {
Description = "updates Hetzner DNS for home-assistant";
};
serviceConfig = {
Type = "oneshot";
ExecStart = script;
Restart = "on-failure";
RestartSec = "10s";
StartLimitIntervalSec = "120";
StartLimitBurst = 3;
Type = "exec";
EnvironmentFile = config.age.secrets.hetzner-dns.path;
};
};
};
timers.bt-reset = {
description = "Periodically reset Bluetooth adapter";
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "5min"; # first run 5 min after boot
OnUnitActiveSec = "4h"; # then every 4 hours
RandomizedDelaySec = "5min";
};
};
path = [
pkgs.curl
pkgs.coreutils # For `cat`
pkgs.jq
];
user = {
timers = {
"update-hetzner-dns" = {
unitConfig = {
Description = "updates Hetzner DNS records";
};
script = ''
LAST_IP_FILE="/tmp/hetzner-dns-ha-ip"
INTERFACE="enp3s0"
timerConfig = {
Unit = "update-hetzner-dns.service";
OnCalendar = "*-*-* *:00/30:00";
Persistent = true;
};
CURRENT_IP=$(curl -s --fail --interface "$INTERFACE" ifconfig.me)
wantedBy = [ "timers.target" ];
};
};
LAST_IP=""
if [[ -f "$LAST_IP_FILE" ]]; then
LAST_IP=$(cat "$LAST_IP_FILE")
fi
services = {
"update-hetzner-dns" = {
unitConfig = {
Description = "updates Hetzner DNS records";
};
if [[ "$CURRENT_IP" == "$LAST_IP" ]]; then
echo "IP unchanged, NOOP update."
exit 0
else
echo "Updating IP"
serviceConfig = {
Type = "exec";
EnvironmentFile = config.age.secrets.hetzner-dns.path;
};
JSON_BODY=$(jq -n --arg ip "$CURRENT_IP" '{records: [{value: $ip}]}')
path = [
pkgs.curl
pkgs.coreutils
pkgs.jq
];
script = ''
SUBDOMAINS="ha komga"
INTERFACE="enp3s0"
CURRENT_IP=$(curl -s --fail --interface "$INTERFACE" ifconfig.me)
for SUBDOMAIN in $SUBDOMAINS; do
LAST_IP_FILE="/tmp/hetzner-dns-''${SUBDOMAIN}-ip"
LAST_IP=""
if [[ -f "$LAST_IP_FILE" ]]; then
LAST_IP=$(cat "$LAST_IP_FILE")
fi
if [[ "$CURRENT_IP" == "$LAST_IP" ]]; then
echo "$SUBDOMAIN: IP unchanged, NOOP update."
else
echo "$SUBDOMAIN: Updating IP"
JSON_BODY=$(jq -n --arg ip "$CURRENT_IP" '{records: [{value: $ip}]}')
curl \
--fail \
-X POST \
-H "Authorization: Bearer $HETZNER_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$JSON_BODY" \
"https://api.hetzner.cloud/v1/zones/ppp.pm/rrsets/''${SUBDOMAIN}/A/actions/set_records" \
&& echo $CURRENT_IP > $LAST_IP_FILE
fi
done
'';
};
curl \
--fail \
-X POST \
-H "Authorization: Bearer $HETZNER_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$JSON_BODY" \
"https://api.hetzner.cloud/v1/zones/ppp.pm/rrsets/ha/A/actions/set_records" \
&& echo $CURRENT_IP > $LAST_IP_FILE
fi
'';
};
};
};

View File

@@ -1,111 +0,0 @@
{
pkgs,
lib,
config,
...
}:
let
enabled = config.mod.homepage.enable;
nginxEnabled = config.mod.nginx.enable;
services = config.mod.homepage.services;
serviceToCard = svc: ''
<a class="card" href="http://manatee:${toString svc.port}">
<div class="name">${svc.name}</div>
<div class="desc">${svc.description}</div>
<div class="port">:${toString svc.port}</div>
</a>
'';
page = pkgs.writeTextDir "index.html" ''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>manatee</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: #1a1b26;
color: #c0caf5;
min-height: 100vh;
padding: 3rem 1.5rem;
}
h1 {
text-align: center;
font-size: 1.5rem;
font-weight: 400;
color: #7aa2f7;
margin-bottom: 2rem;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
max-width: 900px;
margin: 0 auto;
}
.card {
display: block;
background: #24283b;
border: 1px solid #414868;
border-radius: 8px;
padding: 1.25rem;
text-decoration: none;
color: inherit;
transition: border-color 0.15s;
}
.card:hover { border-color: #7aa2f7; }
.name { font-size: 1.1rem; font-weight: 600; color: #c0caf5; }
.desc { font-size: 0.85rem; color: #565f89; margin-top: 0.35rem; }
.port { font-size: 0.8rem; color: #414868; margin-top: 0.5rem; font-family: monospace; }
</style>
</head>
<body>
<h1>manatee</h1>
<div class="grid">
${lib.concatMapStrings serviceToCard services}
</div>
</body>
</html>
'';
in
{
options = {
mod.homepage = {
enable = lib.mkEnableOption "Enable homepage module";
services = lib.mkOption {
type = lib.types.listOf (
lib.types.submodule {
options = {
name = lib.mkOption { type = lib.types.str; };
port = lib.mkOption { type = lib.types.port; };
description = lib.mkOption { type = lib.types.str; };
};
}
);
default = [ ];
description = "Services to display on the homepage";
};
};
};
config = lib.mkIf (enabled && nginxEnabled) {
services.nginx.virtualHosts."homepage" = {
listen = [
{
addr = "0.0.0.0";
port = 9999;
}
];
root = page;
locations."/" = {
index = "index.html";
};
};
networking.firewall.allowedTCPPorts = [ 9999 ];
};
}

View File

@@ -10,14 +10,6 @@ in
};
config = lib.mkIf enabled {
mod.homepage.services = [
{
name = "Immich";
port = 2283;
description = "Photo library";
}
];
users.users.immich = {
isSystemUser = true;
group = "storage";

View File

@@ -47,14 +47,6 @@ in
};
};
mod.homepage.services = [
{
name = "Jellyfin";
port = 8096;
description = "Media streaming";
}
];
networking = {
firewall.allowedTCPPorts = [ 8096 ];
};

View File

@@ -1,13 +1,11 @@
{
inputs,
pkgs,
lib,
config,
...
}:
let
enabled = config.mod.komga.enable;
nginxEnabled = config.mod.nginx.enable;
in
{
options = {
@@ -16,30 +14,9 @@ in
};
};
imports = [
inputs.komga-comictracker.nixosModules.default
inputs.komga-bookmanager.nixosModules.default
];
imports = [ inputs.komga-comictracker.nixosModules.default ];
config = lib.mkIf enabled {
mod.homepage.services = [
{
name = "Komga";
port = 8002;
description = "Comic library";
}
{
name = "Komga Reader";
port = 8888;
description = "Comic reader";
}
{
name = "Komga Book Manager";
port = 8686;
description = "Book manager";
}
];
users.users.komga = {
isSystemUser = true;
group = "storage";
@@ -51,61 +28,20 @@ in
user = "komga";
group = "storage";
settings = {
server.port = 8002;
komga."cors.allowed-origins" = [
"http://manatee:8888"
"https://komga.ppp.pm"
];
};
settings.server.port = 8002;
openFirewall = true;
};
services.nginx = lib.mkIf nginxEnabled {
virtualHosts."komga-reader" = {
listen = [
{
addr = "0.0.0.0";
port = 8888;
}
];
root = (pkgs.writeTextDir "komga-reader.html" (builtins.readFile ./komga-reader.html));
locations."/" = {
index = "komga-reader.html";
tryFiles = "$uri $uri/ /komga-reader.html";
};
};
virtualHosts."komga.ppp.pm" = {
forceSSL = true;
useACMEHost = "komga.ppp.pm";
locations."/" = {
proxyPass = "http://127.0.0.1:8002";
proxyWebsockets = true;
};
};
};
networking.firewall.allowedTCPPorts = [ 8888 ];
programs.comictracker = {
enable = true;
komgaUrl = "http://127.0.0.1:8002";
komgaLibraryId = "0NVZH5AK3RPE1";
komgaUrl = "http://manatee:8002";
secretsFile = config.age.secrets.komga-comicbooktracker-credentials.path;
};
services.komga-book-manager = {
enable = true;
port = 8686;
group = "storage";
komgaUrl = "http://127.0.0.1:8002";
credentialsFile = config.age.secrets.komga-bookmanager-credentials.path;
libraryRoot = "/mnt/media/public/books";
libraryId = "0PNE1NEPY6995";
notifyService = {
enable = true;
interval = "daily";
user = "alex";
};
};
age.secrets = {
@@ -114,12 +50,6 @@ in
owner = "alex";
group = "users";
};
"komga-bookmanager-credentials" = {
file = ../../../../secrets/manatee/komga-bookmanager-credentials.age;
owner = "alex";
group = "users";
};
};
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -15,14 +15,6 @@ in
};
config = {
mod.homepage.services = lib.mkIf navidromeEnabled [
{
name = "Navidrome";
port = 4533;
description = "Music streaming";
}
];
services = lib.mkIf navidromeEnabled {
navidrome = {
enable = true;

View File

@@ -10,14 +10,6 @@ in
};
config = lib.mkIf enabled {
mod.homepage.services = [
{
name = "Syncthing";
port = 8384;
description = "File sync";
}
];
services.syncthing = {
enable = true;

View File

@@ -15,14 +15,6 @@ in
};
config = lib.mkIf enabled {
mod.homepage.services = [
{
name = "Transmission";
port = 9091;
description = "Torrent client";
}
];
services = {
transmission = {
enable = true;

View File

@@ -1,7 +0,0 @@
age-encryption.org/v1
-> ssh-ed25519 wkRvNA GW8mvnaXpspxr78xV0fKhXwHVvReyjvDc0v7uPwhuBI
Rne8JZYVhrTTesSFpRQ/IOZlFIMoX9Wmv5n1Ed7Ehv8
-> ssh-ed25519 +oNaHQ d7utzodGQ7LsD2Uht1rbT8Qq9BZp3PkJS9EDhajCjnk
qd2Vj+1TQrjEKkSVAf0cXcCdkgeN/Jbp4UrBSp3cKYQ
--- JQr5UQlutONqnTeoT/mIVZL8ME7ipUDK8zDfNcN3uhU
ø5-VŸÌ²ÂòÀ-®ªÁÅ'^žô5kú.t(d1‡)É'<u%

View File

@@ -34,7 +34,6 @@ in {
"manatee/syncthing-key.age".publicKeys = [ manatee alex ];
"manatee/hetzner-dns.age".publicKeys = [ manatee alex ];
"manatee/komga-comicbooktracker-credentials.age".publicKeys = [ manatee alex];
"manatee/komga-bookmanager-credentials.age".publicKeys = [ manatee alex];
"backwards/root.backwards.age".publicKeys = [ backwards alex ];
"backwards/root.backwards.pub.age".publicKeys = [ backwards alex ];