feat: add slink

This commit is contained in:
Leon Schwarzäugl 2025-07-17 21:50:28 +02:00
parent 1468f3d0fc
commit 486f8b7732
Signed by: swarsel
GPG key ID: 26A54C31F2A4FD84
8 changed files with 236 additions and 48 deletions

5
.github/README.md vendored
View file

@ -122,13 +122,13 @@ Alternatively, to install this from any NixOS live ISO, run `nix run --experimen
### Services ### Services
| Topic | Program | | Topic | Program |
|-----------------------|---------------------------------| |-----------------------|---------------------------------------------------------------------------------------------------------------------|
|📖 **Books** | [Kavita](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/kavita.nix) | |📖 **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) | |📼 **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) | |🎵 **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) | |🗨️ **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) | |📁 **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) | |📄 **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) | |🔄 **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) | |💾 **Backups** | [Restic](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/restic.nix) |
@ -143,6 +143,7 @@ Alternatively, to install this from any NixOS live ISO, run `nix run --experimen
|📅 **CalDav/CardDav** | [Radicale](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/radicale.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) | |↔️ **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) | |✂️ **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) | |🔗 **Link Shortener** | [Shlink](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/shlink.nix) |
### Hosts ### Hosts

View file

@ -269,13 +269,13 @@ Here I give a brief overview over the hostmachines that I am using. This is held
#+begin_src markdown :tangle no :noweb-ref services #+begin_src markdown :tangle no :noweb-ref services
| Topic | Program | | Topic | Program |
|-----------------------|---------------------------------| |-----------------------|---------------------------------------------------------------------------------------------------------------------|
|📖 **Books** | [Kavita](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/kavita.nix) | |📖 **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) | |📼 **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) | |🎵 **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) | |🗨️ **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) | |📁 **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) | |📄 **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) | |🔄 **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) | |💾 **Backups** | [Restic](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/restic.nix) |
@ -290,6 +290,7 @@ Here I give a brief overview over the hostmachines that I am using. This is held
|📅 **CalDav/CardDav** | [Radicale](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/radicale.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) | |↔️ **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) | |✂️ **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) | |🔗 **Link Shortener** | [Shlink](https://github.com/Swarsel/.dotfiles/tree/main/modules/nixos/server/shlink.nix) |
#+end_src #+end_src
@ -8522,6 +8523,7 @@ To get other URLs (token, etc.), use https://<kanidmDomain>/oauth2/openid/<clien
"freshrss.access" = { }; "freshrss.access" = { };
"firefly.access" = { }; "firefly.access" = { };
"radicale.access" = { }; "radicale.access" = { };
"slink.access" = { };
}; };
inherit (config.repo.secrets.local) persons; inherit (config.repo.secrets.local) persons;
@ -8642,6 +8644,11 @@ To get other URLs (token, etc.), use https://<kanidmDomain>/oauth2/openid/<clien
"email" "email"
"profile" "profile"
]; ];
"slink.access" = [
"openid"
"email"
"profile"
];
}; };
preferShortUsername = true; preferShortUsername = true;
claimMaps.groups = { claimMaps.groups = {
@ -8651,6 +8658,7 @@ To get other URLs (token, etc.), use https://<kanidmDomain>/oauth2/openid/<clien
"navidrome.access" = [ "navidrome_access" ]; "navidrome.access" = [ "navidrome_access" ];
"firefly.access" = [ "firefly_access" ]; "firefly.access" = [ "firefly_access" ];
"radicale.access" = [ "radicale_access" ]; "radicale.access" = [ "radicale_access" ];
"slink.access" = [ "slink_access" ];
}; };
}; };
}; };
@ -9316,7 +9324,7 @@ To get other URLs (token, etc.), use https://<kanidmDomain>/oauth2/openid/<clien
}; };
systemd.tmpfiles.rules = [ 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 ]; networking.firewall.allowedTCPPorts = [ servicePort ];
@ -9658,6 +9666,96 @@ To get other URLs (token, etc.), use https://<kanidmDomain>/oauth2/openid/<clien
} }
#+end_src #+end_src
**** slink
Deployment notes:
- enable user: =podman exec -it slink slink user:activate --email=<mail>=
- make user admin: =podman exec -it slink slink user:grant:role --email=<mail> 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 *** Darwin
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: h:ac0cd8b3-06cf-4dca-ba73-6100c8fedb47 :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; croc = lib.mkDefault true;
microbin = lib.mkDefault true; microbin = lib.mkDefault true;
shlink = lib.mkDefault true; shlink = lib.mkDefault true;
slink = lib.mkDefault true;
}; };
}; };
}; };

View file

@ -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": { "sops": {
"age": [ "age": [
{ {
@ -7,8 +7,8 @@
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyK0w2RjJ5R1l2ay94QXRj\nekJwSlowcFVLc1cvWVFjNEVFUnFocEJHYlNnCnBnUEYvNWdNWE9BTjB5ODRuTlAw\nMUh4QmlTeVVYNHM0S1FwWG5qUG42VDgKLS0tIHh5VlU2dVZmUlRIMDRlVEJmNU55\ncFlXR1BzMkVnMkFWN3BBZWhHalltMlEKibdARxBcFqaXUhYp3KkrrvO9YgaBDacl\n8BEv4ph0f2baDN0dsymJjmdHStwKTjOwDspRtCTs5u75hR35a2xyFQ==\n-----END AGE ENCRYPTED FILE-----\n" "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyK0w2RjJ5R1l2ay94QXRj\nekJwSlowcFVLc1cvWVFjNEVFUnFocEJHYlNnCnBnUEYvNWdNWE9BTjB5ODRuTlAw\nMUh4QmlTeVVYNHM0S1FwWG5qUG42VDgKLS0tIHh5VlU2dVZmUlRIMDRlVEJmNU55\ncFlXR1BzMkVnMkFWN3BBZWhHalltMlEKibdARxBcFqaXUhYp3KkrrvO9YgaBDacl\n8BEv4ph0f2baDN0dsymJjmdHStwKTjOwDspRtCTs5u75hR35a2xyFQ==\n-----END AGE ENCRYPTED FILE-----\n"
} }
], ],
"lastmodified": "2025-07-02T10:26:33Z", "lastmodified": "2025-07-17T20:14:29Z",
"mac": "ENC[AES256_GCM,data:/rmQKH7up3IcAdyYpdpx6H6gdyiNsnPS6TaozSU0EXxoaods50xC5sf2/quqLaeSRJE/NjKvh+3BWchbFJMQZM4PvSML3XAO8w9t/GqmOwwLJrvnMyulqS5y7BVDJZysmDe9TFNz05UJfZdbvLrH8kyhTHF7ciA8HgJq5JzFiBc=,iv:ORyza5fzjptuq5WD3NA9/OTFbACtzHp5e6kNKT/EaTE=,tag:wsp3Z/ySHVmDC9uRCn30Uw==,type:str]", "mac": "ENC[AES256_GCM,data:4YP1fp9Mcbx0pvS5l9Xzc5cbhFnBo5GkqyRvcEspNYQ3IW5LIWtPwItwLZH/ymfEkpwIVYOugnB12HJJo9jpudgfUMXtp43ImDUNVHs59qkNhJFmTSoEZMBHQjPtE/jE17OIAZzeA41EAItesrmExV1W8ePy7rTgHQl5BDooWME=,iv:vyZ0BGjMUDeoVMkDw6wLZK3KKIECK5caz/nQB3nXlGs=,tag:GAqKNcSf3Ny/WsB3tYM/og==,type:str]",
"pgp": [ "pgp": [
{ {
"created_at": "2025-06-11T11:42:23Z", "created_at": "2025-06-11T11:42:23Z",

View file

@ -84,6 +84,7 @@ in
"freshrss.access" = { }; "freshrss.access" = { };
"firefly.access" = { }; "firefly.access" = { };
"radicale.access" = { }; "radicale.access" = { };
"slink.access" = { };
}; };
inherit (config.repo.secrets.local) persons; inherit (config.repo.secrets.local) persons;
@ -204,6 +205,11 @@ in
"email" "email"
"profile" "profile"
]; ];
"slink.access" = [
"openid"
"email"
"profile"
];
}; };
preferShortUsername = true; preferShortUsername = true;
claimMaps.groups = { claimMaps.groups = {
@ -213,6 +219,7 @@ in
"navidrome.access" = [ "navidrome_access" ]; "navidrome.access" = [ "navidrome_access" ];
"firefly.access" = [ "firefly_access" ]; "firefly.access" = [ "firefly_access" ];
"radicale.access" = [ "radicale_access" ]; "radicale.access" = [ "radicale_access" ];
"slink.access" = [ "slink_access" ];
}; };
}; };
}; };

View file

@ -76,7 +76,7 @@ in
}; };
systemd.tmpfiles.rules = [ 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 ]; networking.firewall.allowedTCPPorts = [ servicePort ];

View file

@ -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;
};
};
};
};
};
};
}

View file

@ -20,6 +20,7 @@
croc = lib.mkDefault true; croc = lib.mkDefault true;
microbin = lib.mkDefault true; microbin = lib.mkDefault true;
shlink = lib.mkDefault true; shlink = lib.mkDefault true;
slink = lib.mkDefault true;
}; };
}; };
}; };

View file

@ -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": { "sops": {
"age": [ "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" "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", "lastmodified": "2025-07-17T19:51:34Z",
"mac": "ENC[AES256_GCM,data:rSJPK8zMaT+VGj92885MrhHf3VPWKChIGeoWAjWYzGS0GNd+ENThrx7SmKSA2GRgcvT52Xap+wZi8Vzzl0wZPyKiM3LwtQ1JDH401m+RJzqYmEmSmNWtBaXijMYzlW82oG6dJxsHPNAiZfV3iIrXfq4mDIs8KEjl/PwiW+5n3Is=,iv:WRFWcPwFD+Al+EsUMDnCKzXLGiH+xQXMa1ZOGMKgDKI=,tag:h9ObzyxKSXhQB84XVGv6Vw==,type:str]", "mac": "ENC[AES256_GCM,data:c+ayFaTrFkoUcXF2YU5boi4twMg3ZUEPwAc8CUvIjxZWDVgqb4WZHPJ9j9T4hdZZq0URGAPTi4x8EXGTxv0pl7EQnAEYZEXPFwFjbuMzBvmsRfCsxeGFkgX1R3wg2PPs5ssXP22+rm7nuLKa91bloX5h3H7b1VbFQkWDJMg5QtM=,iv:5SblNcf0wAYHGd8NvCvxKTsg3ktr96aF6nUBtuZnfoM=,tag:ZIbmfUuW97RYbEqZn7iEnA==,type:str]",
"pgp": [ "pgp": [
{ {
"created_at": "2025-06-13T20:13:06Z", "created_at": "2025-06-13T20:13:06Z",