From 6f33ffe8c9760221f0cbd88e462c94c322ea6fd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20Schwarz=C3=A4ugl?= Date: Thu, 17 Jul 2025 21:50:28 +0200 Subject: [PATCH] feat: add slink --- .github/README.md | 41 +++---- SwarselSystems.org | 141 ++++++++++++++++++++---- hosts/nixos/winters/secrets/pii.nix.enc | 6 +- modules/nixos/server/kanidm.nix | 7 ++ modules/nixos/server/radicale.nix | 2 +- modules/nixos/server/slink.nix | 80 ++++++++++++++ profiles/nixos/moonside/default.nix | 1 + secrets/repo/pii.nix.enc | 6 +- 8 files changed, 236 insertions(+), 48 deletions(-) create mode 100644 modules/nixos/server/slink.nix diff --git a/.github/README.md b/.github/README.md index d5d7313..f31bb06 100644 --- a/.github/README.md +++ b/.github/README.md @@ -121,29 +121,30 @@ Alternatively, to install this from any NixOS live ISO, run `nix run --experimen ### Services -| Topic | Program | -|-----------------------|---------------------------------| -|📖 **Books** | [Kavita](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/kavita.nix) | -|📼 **Videos** | [Jellyfin](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/jellyfin.nix) | -|🎵 **Music** | [Navidrome](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/navidrome.nix) + [Spotifyd](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/spotifyd.nix) + [MPD](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/mpd.nix) | -|🗨️ **Messaging** | [Matrix](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/matrix.nix) | +| Topic | Program | +|-----------------------|---------------------------------------------------------------------------------------------------------------------| +|📖 **Books** | [Kavita](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/kavita.nix) | +|📼 **Videos** | [Jellyfin](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/jellyfin.nix) | +|🎵 **Music** | [Navidrome](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/navidrome.nix) + [Spotifyd](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/spotifyd.nix) + [MPD](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/mpd.nix) | +|🗨️ **Messaging** | [Matrix](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/matrix.nix) | |📁 **Filesharing** | [Nectcloud](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/nextcloud.nix) | -|📷 **Photos** | [Immich](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/immich.nix) | +|🎞️ **Photos** | [Immich](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/immich.nix) | |📄 **Documents** | [Paperless](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/paperless.nix) | |🔄 **File Sync** | [Syncthing](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/syncthing.nix) | -|💾 **Backups** | [Restic](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/restic.nix) | -|👁️ **Monitoring** | [Grafana](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/monitoring.nix) | -|🍴 **RSS** | [FreshRss](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/freshrss.nix) | -|🌳 **Git** | [Forgejo](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/forgejo.nix) | -|⚓ **Anki Sync** | [Anki Sync Server](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/ankisync.nix) | -|🪪 **SSO** | [Kanidm](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/kanidm.nix) + [oauth2-proxy](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/oauth2-proxy.nix) | -|💸 **Finance** | [Firefly-III](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/firefly-iii.nix) | -|🃏 **Collections** | [Koillection](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/koillection.nix) | -|🗃️ **Shell History** | [Atuin](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/atuin.nix) | -|📅 **CalDav/CardDav** | [Radicale](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/radicale.nix) | -|↔️ **P2P Filesharing** | [Croc](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/croc.nix) | -|✂️ **Paste Tool** | [Microbin](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/microbin.nix) | -|🔗 **Link Shortener** | [Shlink](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/shlink.nix) | +|💾 **Backups** | [Restic](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/restic.nix) | +|👁️ **Monitoring** | [Grafana](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/monitoring.nix) | +|🍴 **RSS** | [FreshRss](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/freshrss.nix) | +|🌳 **Git** | [Forgejo](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/forgejo.nix) | +|⚓ **Anki Sync** | [Anki Sync Server](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/ankisync.nix) | +|🪪 **SSO** | [Kanidm](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/kanidm.nix) + [oauth2-proxy](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/oauth2-proxy.nix) | +|💸 **Finance** | [Firefly-III](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/firefly-iii.nix) | +|🃏 **Collections** | [Koillection](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/koillection.nix) | +|🗃️ **Shell History** | [Atuin](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/atuin.nix) | +|📅 **CalDav/CardDav** | [Radicale](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/radicale.nix) | +|↔️ **P2P Filesharing** | [Croc](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/croc.nix) | +|✂️ **Paste Tool** | [Microbin](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/microbin.nix) | +|📸 **Image Sharing** | [Slink](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/slink.nix) | +|🔗 **Link Shortener** | [Shlink](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/shlink.nix) | ### Hosts diff --git a/SwarselSystems.org b/SwarselSystems.org index 6077782..86d4d15 100644 --- a/SwarselSystems.org +++ b/SwarselSystems.org @@ -268,29 +268,30 @@ Here I give a brief overview over the hostmachines that I am using. This is held :END: #+begin_src markdown :tangle no :noweb-ref services - | Topic | Program | - |-----------------------|---------------------------------| - |📖 **Books** | [Kavita](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/kavita.nix) | - |📼 **Videos** | [Jellyfin](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/jellyfin.nix) | - |🎵 **Music** | [Navidrome](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/navidrome.nix) + [Spotifyd](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/spotifyd.nix) + [MPD](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/mpd.nix) | - |🗨️ **Messaging** | [Matrix](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/matrix.nix) | + | Topic | Program | + |-----------------------|---------------------------------------------------------------------------------------------------------------------| + |📖 **Books** | [Kavita](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/kavita.nix) | + |📼 **Videos** | [Jellyfin](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/jellyfin.nix) | + |🎵 **Music** | [Navidrome](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/navidrome.nix) + [Spotifyd](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/spotifyd.nix) + [MPD](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/mpd.nix) | + |🗨️ **Messaging** | [Matrix](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/matrix.nix) | |📁 **Filesharing** | [Nectcloud](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/nextcloud.nix) | - |📷 **Photos** | [Immich](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/immich.nix) | + |🎞️ **Photos** | [Immich](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/immich.nix) | |📄 **Documents** | [Paperless](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/paperless.nix) | |🔄 **File Sync** | [Syncthing](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/syncthing.nix) | - |💾 **Backups** | [Restic](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/restic.nix) | - |👁️ **Monitoring** | [Grafana](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/monitoring.nix) | - |🍴 **RSS** | [FreshRss](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/freshrss.nix) | - |🌳 **Git** | [Forgejo](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/forgejo.nix) | - |⚓ **Anki Sync** | [Anki Sync Server](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/ankisync.nix) | - |🪪 **SSO** | [Kanidm](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/kanidm.nix) + [oauth2-proxy](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/oauth2-proxy.nix) | - |💸 **Finance** | [Firefly-III](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/firefly-iii.nix) | - |🃏 **Collections** | [Koillection](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/koillection.nix) | - |🗃️ **Shell History** | [Atuin](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/atuin.nix) | - |📅 **CalDav/CardDav** | [Radicale](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/radicale.nix) | - |↔️ **P2P Filesharing** | [Croc](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/croc.nix) | - |✂️ **Paste Tool** | [Microbin](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/microbin.nix) | - |🔗 **Link Shortener** | [Shlink](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/shlink.nix) | + |💾 **Backups** | [Restic](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/restic.nix) | + |👁️ **Monitoring** | [Grafana](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/monitoring.nix) | + |🍴 **RSS** | [FreshRss](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/freshrss.nix) | + |🌳 **Git** | [Forgejo](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/forgejo.nix) | + |⚓ **Anki Sync** | [Anki Sync Server](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/ankisync.nix) | + |🪪 **SSO** | [Kanidm](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/kanidm.nix) + [oauth2-proxy](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/oauth2-proxy.nix) | + |💸 **Finance** | [Firefly-III](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/firefly-iii.nix) | + |🃏 **Collections** | [Koillection](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/koillection.nix) | + |🗃️ **Shell History** | [Atuin](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/atuin.nix) | + |📅 **CalDav/CardDav** | [Radicale](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/radicale.nix) | + |↔️ **P2P Filesharing** | [Croc](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/croc.nix) | + |✂️ **Paste Tool** | [Microbin](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/microbin.nix) | + |📸 **Image Sharing** | [Slink](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/slink.nix) | + |🔗 **Link Shortener** | [Shlink](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/shlink.nix) | #+end_src * flake.nix @@ -8522,6 +8523,7 @@ To get other URLs (token, etc.), use https:///oauth2/openid//oauth2/openid//oauth2/openid//oauth2/openid//oauth2/openid/= + - make user admin: =podman exec -it slink slink user:grant:role --email= ROLE_ADMIN= + - finally, disable new user registration in web ui + +#+begin_src nix-ts :tangle modules/nixos/server/slink.nix + { self, lib, config, ... }: + let + servicePort = 3000; + serviceName = "slink"; + serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; + serviceDir = "/var/lib/slink"; + + containerRev = "sha256:98b9442696f0a8cbc92f0447f54fa4bad227af5dcfd6680545fedab2ed28ddd9"; + in + { + options = { + swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; + }; + config = lib.mkIf config.swarselmodules.server.${serviceName} { + + virtualisation.oci-containers.containers.${serviceName} = { + image = "anirdev/slink@${containerRev}"; + environment = { + "ORIGIN" = "https://${serviceDomain}"; + "TZ" = config.repo.secrets.common.location.timezone; + "STORAGE_PROVIDER" = "local"; + "IMAGE_MAX_SIZE" = "50M"; + "USER_APPROVAL_REQUIRED" = "true"; + }; + ports = [ "${builtins.toString servicePort}:${builtins.toString servicePort}" ]; + volumes = [ + "${serviceDir}/var/data:/app/var/data" + "${serviceDir}/images:/app/slink/images" + ]; + }; + + systemd.tmpfiles.rules = [ + "d ${serviceDir}/var/data 0750 root root - -" + "d ${serviceDir}/images 0750 root root - -" + ]; + + networking.firewall.allowedTCPPorts = [ servicePort ]; + + environment.persistence."/persist".directories = lib.mkIf config.swarselsystems.isImpermanence [ + { directory = serviceDir; } + ]; + + topology.self.services.${serviceName} = { + name = lib.swarselsystems.toCapitalized serviceName; + info = "https://${serviceDomain}"; + icon = "${self}/files/topology-images/shlink.png"; + }; + globals.services.${serviceName}.domain = serviceDomain; + + services.nginx = { + upstreams = { + ${serviceName} = { + servers = { + "localhost:${builtins.toString servicePort}" = { }; + }; + }; + }; + virtualHosts = { + "${serviceDomain}" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + oauth2.enable = true; + oauth2.allowedGroups = [ "slink_access" ]; + locations = { + "/" = { + proxyPass = "http://${serviceName}"; + setOauth2Headers = false; + }; + "/image" = { + proxyPass = "http://${serviceName}"; + setOauth2Headers = false; + bypassAuth = true; + }; + }; + }; + }; + }; + }; + } +#+end_src + *** Darwin :PROPERTIES: :CUSTOM_ID: h:ac0cd8b3-06cf-4dca-ba73-6100c8fedb47 @@ -16579,6 +16677,7 @@ Modules that need to be loaded on the NixOS level. Note that these will not be a croc = lib.mkDefault true; microbin = lib.mkDefault true; shlink = lib.mkDefault true; + slink = lib.mkDefault true; }; }; }; diff --git a/hosts/nixos/winters/secrets/pii.nix.enc b/hosts/nixos/winters/secrets/pii.nix.enc index 0a46cc3..9c0e82b 100644 --- a/hosts/nixos/winters/secrets/pii.nix.enc +++ b/hosts/nixos/winters/secrets/pii.nix.enc @@ -1,5 +1,5 @@ { - "data": "ENC[AES256_GCM,data:XEzKhJ1+iwQZ24wnf7FdThWMwOKEwaYr3ecGjjLpTqx4+kq3W9FWFDSKkHAwo6077tsVTu7NCGZfat/gAylg1xgqAZAHA8/PMQejowAgIPCG7eCQvQfTMepcuWjgc9BAyFYcBjPFmLnvX69LE80Fw0Io1QeKIa6CVJKp4P6eC8OIKeG1fTc3/sWxr+3ZKTzGPKiGCnSMo7qM0/2HlV0bhp8yWFni+2nZ8UlPJluEnmx0bBR0uZ6bdqzLX/fFrmgTd6m30+Zq8pjKVhiHpEQd9m5aU2inCWv4OeNE3EQsLYcnhcVdrcySZ6R4AZ1nlZZedDhf+Ee4AwcIPVsA8HHqlUEY1CayHF5wLpkxralOpt+RFZYJkvupmozP/uYRymoAA6YgJGesr2Oki0wT041nioB9AvpU9xFvfCqbqXXsBvwtvhxpwhEJJOogZENKnjvvoDoLGZlFVPzkfqDANuv5SAJQiWuFLWEdcmQncRlsjwSPOGOnI+r+puHszPOaDsZigF/yuL4rd5a0RkS6dCOfYtCvQBBAMfEAWX13AiKF0Dtz5/ijEEK7iojoMF/B6rnoENs2l0cSljq7TGV0DVRDjFUTiMNbfRxUJUkMuqJFnNzMwz4METmAwgqHn217uvUk2V8UJ5v9k4sapRmogPTfCwhvxGDV1e9AJvL6WXJ2m0ldhKOcQXFiO/+ZtNB1FHJ22ZFcxcSSOSRmEQsB5Yw3zPEjQ7sU93sKRLEPrTEqSSNG75iZ+vZm9iEI2trFhtEOlU98Ury/USC9sjPN/sxGGR5hcRZajY2HUxVTucMheIWJ7mxhxLHg7rz/qWLSqC6TqKwnyv+NdBnrVaLWaRnZGRCgtvN+oJKRjyxCsiDHVoY52cP5SmdXGn3yrVxDuYGrkLf/JOj76Hs/TCSsYptMvKqH4R4vT8SlDnplpcIfd5KTr4sM1n4q4sai1wRc1wlN0EkwK7+otTYcStxtvgUUtW/4jkk+73TdvvR9IkV0PqghKuB6FdAM6qRX1M8AebcXNyZYW9k7sHRVWGk+eMrMe/qX2IAY1WUgdt7hs2Ci9XoJqWKCG17rcKZ/ORu0utrJt+l0H9fZihO8+aYHabsHueiyJTKJvZx9+12r5a6deXShtdpsdQVnMlczWKGKUIdQ7TB12HILGPAvvZkJh0aDq043UU/4dUwyUezi70QnH/Z/GRip68kXH6njBmZRlmmNAFSCHkGxMKxpgssHyXXNEvET/TIVEJeR1vYjqFoGIBrFQV1Bqu2yk3A2pZLBEEbsCQrOuE2CPpfrn5kCUnw2sJOWpnVqyQ+SA4xZ7W1vw7i8aL2ThZlKE3zsriECpOfEaDWv8ME2HsiL42VUmgv+6zm/2hiVK+OayQucBUbY8IC6Yjl2kXB7EPvUsc60V2xOJl40KIAJxa0GNgaRMWkYoFtnVrYbY4yT37UjIAu2fJD725qgkXOJwbFPlX7na86PVVB0MQ==,iv:JSG8DynJg8t7HEDoW7IwYt189P22h4BPMFYsJmo3mcU=,tag:cHoNQBL2DCpntJyhqay54Q==,type:str]", + "data": "ENC[AES256_GCM,data:Ow17QtITRfXk4BJGWb4jw1gX8HRIAdpBDpyGtif3Vb42cCqSWpLaIR1KecbU3OPkGesC/vh2tgmfE/xlm6nUR8kvRDZdhFr7UckeKFiD4hlfHBhGckaCXOiRZ3ACHrHfM/a2CbKDl14xLDMFDervdMRfB/4vjvnn9xvhjepZ1e7+Iopd9agL8r658AMFF3j2tRYZp7l5GLArs+7mUsFfXujkui1Xgh5+TRvYPh3zQ18RWyP4QMYP6HPHgHRH7S6uciUb6Qq1yEytOy3pCJTb+gZaa9xI1azzP7r83b7KeXdukupR+9fNWN52swDHXRugThMrh4kOrBdyaafJ1v2w4FTTPwj6JQvkxT+vMV4+WIclhD/FWtEHtUqsHDFnMzTIQfyTMt42HZKNdQDwp2fwuV1BP0qOUqRnheaTPiyZiZL8I27xDfwp5zfSqxA7KEzLL2uRbIPefdqNFlcte/xL9IkbTux+AmHaIjQeLkiPyMf2wc2NTIabWPk/ILC917vnancdAQuujB38SaWPrKeCEqDKsArvgjnmn47ctBND73G4PszieneCnuq4sNT9QrC81mk/AQv5TFG0i0fvPoXfpO4ul7MgaP+iPodb2CThHKDAwL+Gnq/6+tg+vJbTKxrsixHvYViA9p/ke1YaRFg2R8V7NwRDl68NI2yCaqj3qlS6Sa8Mx7IUQR6aCN4nkpcJs2GLVCEhWKFBsNXXhPa01C/81JGJ6+/zfHuGbY6gGtS7k3cZ9+dfizN29nBlp+A1U+Noxjl9arYOX4gjEnQhvo9Tp/gBCmrvXjoE6ja8XXUv78/Bfnvup52gdNunYRYv3Aj91F0u6fH6UqoWPXVTy6eznV727Pj9H1BPzMEfmf3EPc6Qcg7AR7vKORfH3U0PFXxFMY0kGEJdug5jmLuwUoRR9zOyHOqdS4Tk9BcMT02gRyQf6LZ3drDoqC35L5hop/gfbleUXXODn3cE1mEuAuZ08OSavZ24IHo1aMr50gjn32WSBKkjfC0dobBWzWwykxhu0vZZI3xYJx/59qqsodDGAJAhry5N6vrwM/PK/O73L2QBqkaT50eImIQ4+vpahcvFPRIkHLvQkzxlyH0dafuGyyF2gfig5Rq3vw02pT8z06LjmJ/ehvteyy1BE9CdiUvYHjaBXiWg2n5FvAe5e8EQxUx9+SwfwEpnucuocp7g+d3W3cXJOU6m9JxwvK7LX95MbpKOtdPq11yi35LLb1TikFLdFZyu+SeaNFD3AzQMOSem6Lwc7N7WHC5z3Ah7FvqqnL2J51hDfMLW78A7DCI9GNRVoyVjx++gRr+ftEf8UUAYmkJdT/D898wtLWmBXikY/2e36q3QJV0Hy3fGBjxLmN/Tdz62jgCaXrNdJqJTFZ5h+hjd03GT6utmPwp0D2lBSM0LXsZsql+jI54P8cFdu3S4mNIjHZqV1kmsk0MCCVJKnGODeLQLxJhp287N3+0HJEm4KicGWMchef/YeiLLtD7mz7ov/9OI,iv:KvoTnlj+f+eMsFEZP8F1v0r/xZ4aVBUWmO+zsQCvhS4=,tag:U1ziE2832QfNkP0yjIzBeA==,type:str]", "sops": { "age": [ { @@ -7,8 +7,8 @@ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyK0w2RjJ5R1l2ay94QXRj\nekJwSlowcFVLc1cvWVFjNEVFUnFocEJHYlNnCnBnUEYvNWdNWE9BTjB5ODRuTlAw\nMUh4QmlTeVVYNHM0S1FwWG5qUG42VDgKLS0tIHh5VlU2dVZmUlRIMDRlVEJmNU55\ncFlXR1BzMkVnMkFWN3BBZWhHalltMlEKibdARxBcFqaXUhYp3KkrrvO9YgaBDacl\n8BEv4ph0f2baDN0dsymJjmdHStwKTjOwDspRtCTs5u75hR35a2xyFQ==\n-----END AGE ENCRYPTED FILE-----\n" } ], - "lastmodified": "2025-07-02T10:26:33Z", - "mac": "ENC[AES256_GCM,data:/rmQKH7up3IcAdyYpdpx6H6gdyiNsnPS6TaozSU0EXxoaods50xC5sf2/quqLaeSRJE/NjKvh+3BWchbFJMQZM4PvSML3XAO8w9t/GqmOwwLJrvnMyulqS5y7BVDJZysmDe9TFNz05UJfZdbvLrH8kyhTHF7ciA8HgJq5JzFiBc=,iv:ORyza5fzjptuq5WD3NA9/OTFbACtzHp5e6kNKT/EaTE=,tag:wsp3Z/ySHVmDC9uRCn30Uw==,type:str]", + "lastmodified": "2025-07-17T20:14:29Z", + "mac": "ENC[AES256_GCM,data:4YP1fp9Mcbx0pvS5l9Xzc5cbhFnBo5GkqyRvcEspNYQ3IW5LIWtPwItwLZH/ymfEkpwIVYOugnB12HJJo9jpudgfUMXtp43ImDUNVHs59qkNhJFmTSoEZMBHQjPtE/jE17OIAZzeA41EAItesrmExV1W8ePy7rTgHQl5BDooWME=,iv:vyZ0BGjMUDeoVMkDw6wLZK3KKIECK5caz/nQB3nXlGs=,tag:GAqKNcSf3Ny/WsB3tYM/og==,type:str]", "pgp": [ { "created_at": "2025-06-11T11:42:23Z", diff --git a/modules/nixos/server/kanidm.nix b/modules/nixos/server/kanidm.nix index 3f90e06..e9b97cb 100644 --- a/modules/nixos/server/kanidm.nix +++ b/modules/nixos/server/kanidm.nix @@ -84,6 +84,7 @@ in "freshrss.access" = { }; "firefly.access" = { }; "radicale.access" = { }; + "slink.access" = { }; }; inherit (config.repo.secrets.local) persons; @@ -204,6 +205,11 @@ in "email" "profile" ]; + "slink.access" = [ + "openid" + "email" + "profile" + ]; }; preferShortUsername = true; claimMaps.groups = { @@ -213,6 +219,7 @@ in "navidrome.access" = [ "navidrome_access" ]; "firefly.access" = [ "firefly_access" ]; "radicale.access" = [ "radicale_access" ]; + "slink.access" = [ "slink_access" ]; }; }; }; diff --git a/modules/nixos/server/radicale.nix b/modules/nixos/server/radicale.nix index c9a1a8e..2917064 100644 --- a/modules/nixos/server/radicale.nix +++ b/modules/nixos/server/radicale.nix @@ -76,7 +76,7 @@ in }; systemd.tmpfiles.rules = [ - "d '${cfg.settings.storage.filesystem_folder}' 0750 ${serviceUser} ${serviceGroup} - -" + "d ${cfg.settings.storage.filesystem_folder} 0750 ${serviceUser} ${serviceGroup} - -" ]; networking.firewall.allowedTCPPorts = [ servicePort ]; diff --git a/modules/nixos/server/slink.nix b/modules/nixos/server/slink.nix new file mode 100644 index 0000000..547a2c7 --- /dev/null +++ b/modules/nixos/server/slink.nix @@ -0,0 +1,80 @@ +{ self, lib, config, ... }: +let + servicePort = 3000; + serviceName = "slink"; + serviceDomain = config.repo.secrets.common.services.domains.${serviceName}; + serviceDir = "/var/lib/slink"; + + containerRev = "sha256:98b9442696f0a8cbc92f0447f54fa4bad227af5dcfd6680545fedab2ed28ddd9"; +in +{ + options = { + swarselmodules.server.${serviceName} = lib.mkEnableOption "enable ${serviceName} on server"; + }; + config = lib.mkIf config.swarselmodules.server.${serviceName} { + + virtualisation.oci-containers.containers.${serviceName} = { + image = "anirdev/slink@${containerRev}"; + environment = { + "ORIGIN" = "https://${serviceDomain}"; + "TZ" = config.repo.secrets.common.location.timezone; + "STORAGE_PROVIDER" = "local"; + "IMAGE_MAX_SIZE" = "50M"; + "USER_APPROVAL_REQUIRED" = "true"; + }; + ports = [ "${builtins.toString servicePort}:${builtins.toString servicePort}" ]; + volumes = [ + "${serviceDir}/var/data:/app/var/data" + "${serviceDir}/images:/app/slink/images" + ]; + }; + + systemd.tmpfiles.rules = [ + "d ${serviceDir}/var/data 0750 root root - -" + "d ${serviceDir}/images 0750 root root - -" + ]; + + networking.firewall.allowedTCPPorts = [ servicePort ]; + + environment.persistence."/persist".directories = lib.mkIf config.swarselsystems.isImpermanence [ + { directory = serviceDir; } + ]; + + topology.self.services.${serviceName} = { + name = lib.swarselsystems.toCapitalized serviceName; + info = "https://${serviceDomain}"; + icon = "${self}/files/topology-images/shlink.png"; + }; + globals.services.${serviceName}.domain = serviceDomain; + + services.nginx = { + upstreams = { + ${serviceName} = { + servers = { + "localhost:${builtins.toString servicePort}" = { }; + }; + }; + }; + virtualHosts = { + "${serviceDomain}" = { + enableACME = true; + forceSSL = true; + acmeRoot = null; + oauth2.enable = true; + oauth2.allowedGroups = [ "slink_access" ]; + locations = { + "/" = { + proxyPass = "http://${serviceName}"; + setOauth2Headers = false; + }; + "/image" = { + proxyPass = "http://${serviceName}"; + setOauth2Headers = false; + bypassAuth = true; + }; + }; + }; + }; + }; + }; +} diff --git a/profiles/nixos/moonside/default.nix b/profiles/nixos/moonside/default.nix index 34674fd..e678778 100644 --- a/profiles/nixos/moonside/default.nix +++ b/profiles/nixos/moonside/default.nix @@ -20,6 +20,7 @@ croc = lib.mkDefault true; microbin = lib.mkDefault true; shlink = lib.mkDefault true; + slink = lib.mkDefault true; }; }; }; diff --git a/secrets/repo/pii.nix.enc b/secrets/repo/pii.nix.enc index f7b6d86..0ce9956 100644 --- a/secrets/repo/pii.nix.enc +++ b/secrets/repo/pii.nix.enc @@ -1,5 +1,5 @@ { - "data": "ENC[AES256_GCM,data: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,iv:2AkTWN5WCYjf9DWJGCsmRf3CLA/EMgpLaORWcB2pGvs=,tag:cbwxW35me7T1OZipxdHfvw==,type:str]", + "data": "ENC[AES256_GCM,data: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,iv:0eoQAssueYsHRvU1qHTSdryeZxeTYv+mDMt2uQR64hA=,tag:N62wqpHutX4mUAFkfiS6vQ==,type:str]", "sops": { "age": [ { @@ -27,8 +27,8 @@ "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtU240VjVRZmJ5TGsrclJF\nRXRLbTRCZURtR0Z3d2E2eDNNeGRDODlXVEY4CllTeVFYbDJQWlRSS1RFLzAxSnlM\nZi9NU1c3cWo3YWRLcUJ2U2ZFWFBBVEEKLS0tIGtmZU9qSWdBT3RDeStaaFFDSWtk\ndkUzZXJwZUl4LzVxYXdidmxXRnNnclUKyAMZqCKSY/RQvTR4bbjLaPnGKwdBcHXc\nvtiVSrLdIdzMa6id/J07TJH5UesUmcp0wjU41MDa4aMBLy+cXhuBHA==\n-----END AGE ENCRYPTED FILE-----\n" } ], - "lastmodified": "2025-07-17T19:47:10Z", - "mac": "ENC[AES256_GCM,data:rSJPK8zMaT+VGj92885MrhHf3VPWKChIGeoWAjWYzGS0GNd+ENThrx7SmKSA2GRgcvT52Xap+wZi8Vzzl0wZPyKiM3LwtQ1JDH401m+RJzqYmEmSmNWtBaXijMYzlW82oG6dJxsHPNAiZfV3iIrXfq4mDIs8KEjl/PwiW+5n3Is=,iv:WRFWcPwFD+Al+EsUMDnCKzXLGiH+xQXMa1ZOGMKgDKI=,tag:h9ObzyxKSXhQB84XVGv6Vw==,type:str]", + "lastmodified": "2025-07-17T19:51:34Z", + "mac": "ENC[AES256_GCM,data:c+ayFaTrFkoUcXF2YU5boi4twMg3ZUEPwAc8CUvIjxZWDVgqb4WZHPJ9j9T4hdZZq0URGAPTi4x8EXGTxv0pl7EQnAEYZEXPFwFjbuMzBvmsRfCsxeGFkgX1R3wg2PPs5ssXP22+rm7nuLKa91bloX5h3H7b1VbFQkWDJMg5QtM=,iv:5SblNcf0wAYHGd8NvCvxKTsg3ktr96aF6nUBtuZnfoM=,tag:ZIbmfUuW97RYbEqZn7iEnA==,type:str]", "pgp": [ { "created_at": "2025-06-13T20:13:06Z",