Compare commits

41 Commits

Author SHA1 Message Date
a9ea135cb9 Add ghidra, wireshark
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-10-12 22:00:44 +01:00
b8699ba0b6 Add usbutils
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-10-12 21:16:37 +01:00
eb3c301ef6 Fuck you nvidia
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-10-06 18:10:15 +01:00
a75875a311 Update
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-10-06 12:03:06 +00:00
822044423e Update nvf quit keybind
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-10-02 10:07:51 +00:00
63d2dd2e93 Relax smart temperature warnings
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-10-01 08:14:40 +01:00
8235bd4cdf Fix syncthing
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-30 13:25:52 +01:00
492b643d8b Add immich
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-30 10:13:59 +01:00
6ce084b652 Add nix-community cache
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-30 09:09:35 +01:00
c870442536 Switch to IONOS
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-29 20:57:37 +01:00
81b3faaf3e Update consent duration
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-29 20:49:14 +01:00
2f286e25bc Fix nvim img-clip notification
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-29 08:56:57 +00:00
871a8dcdbf Update TV DNS whitelist
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-27 10:44:07 +01:00
5191357fcd Add signal
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-27 09:47:31 +01:00
f1d0a8b2df Update nvf, add lazygit
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-26 12:59:03 +00:00
116de857eb Update nvf
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-25 10:42:25 +00:00
80bde87757 Add dig, httpie
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-24 14:19:12 +00:00
82496be4b3 Edit git, neovim
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-24 12:59:23 +00:00
fbe424384c Update
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-24 12:29:54 +00:00
3dba5ed833 Silence shellcheck
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-23 00:10:35 +01:00
e41e8c2078 Add plex
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-22 23:53:30 +01:00
248432b132 Refactor public ip handling
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-22 10:54:59 +01:00
3bf23f860a Update comentario
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-22 09:58:06 +01:00
fc8e2db679 Add beta media endpoint
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-19 21:13:11 +01:00
183b5e334f Add vps ssh config
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-19 14:14:37 +01:00
496027b505 Update recyclarr profiles
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-16 11:25:33 +01:00
35fd86138d Add systemd unit alerts
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-16 09:36:21 +01:00
88eead5aa4 Fix grafana notifications
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-15 22:26:25 +01:00
8e21efdc53 Fix inverted grafana panel
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-15 17:48:44 +01:00
71e13f1408 Update VPN
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-15 17:42:55 +01:00
f72943c905 Add media notifications
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-15 16:36:49 +01:00
4cd670bb27 Add grafana alerts
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-15 15:34:52 +01:00
310950de42 Update littlelink
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-15 12:08:07 +01:00
d418acb16a Make gitea two-factor
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-15 12:08:07 +01:00
8f2cea6abf Add blog
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-15 12:08:07 +01:00
43b6159feb Add gitea runner image
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-15 12:08:07 +01:00
6b38429bac 80TiB
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-15 12:08:07 +01:00
615524070b Update grafana dashboards
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-15 12:08:06 +01:00
1727785180 Add declarative attic cache
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-15 12:08:06 +01:00
ffafc81ed1 Add authelia consent duration
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-15 12:07:54 +01:00
367d65e1ba Add comentario
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-09-15 12:07:53 +01:00
148 changed files with 3999 additions and 3933 deletions

View File

@@ -25,7 +25,6 @@ NixOS dotfiles and configuration for various hosts and users.
- [`remove-host.sh`](./scripts/remove-host.sh): Remove references to a host.
- [`update-keys.sh`](./scripts/update-keys.sh): Update the encryption keys in all relevant files using `sops.yaml` configurations.
- [`update.sh`](./scripts/update.sh): Update flake and all packages.
- [`cache.sh`](./scripts/cache.sh): Build all `nixosConfiguration`s and push them to `attic`.
Any `options.nix` files create custom option definitions when present.

114
flake.lock generated
View File

@@ -10,11 +10,11 @@
]
},
"locked": {
"lastModified": 1756487002,
"narHash": "sha256-hN9RfNXy53qAkT68T+IYZpl68uE1uPOVMkw0MqC43KA=",
"lastModified": 1759227262,
"narHash": "sha256-ibKJckw+KWH6n+pscOA7DWImanr988zKB7R2Z6ZEMLM=",
"owner": "aylur",
"repo": "ags",
"rev": "8ff792dba6cc82eed10e760f551075564dd0a407",
"rev": "f68a0d03fbb94f4beacedd922ffaa0bf0f10397a",
"type": "github"
},
"original": {
@@ -30,11 +30,11 @@
]
},
"locked": {
"lastModified": 1756474652,
"narHash": "sha256-iiBU6itpEqE0spXeNJ3uJTfioSyKYjt5bNepykpDXTE=",
"lastModified": 1759688436,
"narHash": "sha256-EfTrJse33t3RP//DqESkTMCpMSdIi/wxxfa12+eP5jo=",
"owner": "aylur",
"repo": "astal",
"rev": "20bd8318e4136fbd3d4eb2d64dbabc3acbc915dd",
"rev": "12c15b44608422e494c387aba6adc1ab6315d925",
"type": "github"
},
"original": {
@@ -121,11 +121,11 @@
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1754487366,
"narHash": "sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8=",
"lastModified": 1759362264,
"narHash": "sha256-wfG0S7pltlYyZTM+qqlhJ7GMw2fTF4mLKCIVhLii/4M=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "af66ad14b28a127c5c0f3bbb298218fc63528a18",
"rev": "758cf7296bee11f1706a574c77d072b8a7baa881",
"type": "github"
},
"original": {
@@ -183,11 +183,11 @@
]
},
"locked": {
"lastModified": 1756579987,
"narHash": "sha256-duCce8zGsaMsrqqOmLOsuaV1PVIw/vXWnKuLKZClsGg=",
"lastModified": 1759711004,
"narHash": "sha256-B39NxeKCnK3DJlmJKIts6njcXcVVASLUChDNmRl4dxQ=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "99a69bdf8a3c6bf038c4121e9c4b6e99706a187a",
"rev": "6f4021da5d2bb5ea7cb782ff413ecb7062066820",
"type": "github"
},
"original": {
@@ -212,11 +212,11 @@
"rust-overlay": "rust-overlay"
},
"locked": {
"lastModified": 1754297745,
"narHash": "sha256-aD6/scLN3L4ZszmNbhhd3JQ9Pzv1ScYFphz14wHinfs=",
"lastModified": 1756744479,
"narHash": "sha256-EyZXusK/wRD3V9vDh00W2Re3Eg8UQ+LjVBQrrH9dq1U=",
"owner": "nix-community",
"repo": "lanzaboote",
"rev": "892cbdca865d6b42f9c0d222fe309f7720259855",
"rev": "747b7912f49e2885090c83364d88cf853a020ac1",
"type": "github"
},
"original": {
@@ -235,11 +235,11 @@
]
},
"locked": {
"lastModified": 1757531256,
"narHash": "sha256-aOqrRvKmHoPKVhEYgV/RbsMXYXy6W9Tt1uhGK3dWMlE=",
"lastModified": 1758632667,
"narHash": "sha256-C0aBPv8vqTI1QNVhygZxL0f49UERx2UejVdtyz67jhs=",
"ref": "refs/heads/main",
"rev": "be7b39f41a1137a68944fc73db5a24544e015eb6",
"revCount": 7,
"rev": "5e0737c20f3c265dbff604170a6433cc1e1a4b41",
"revCount": 8,
"type": "git",
"url": "https://git.karaolidis.com/karaolidis/nix-lib.git"
},
@@ -250,11 +250,11 @@
},
"mnw": {
"locked": {
"lastModified": 1748710831,
"narHash": "sha256-eZu2yH3Y2eA9DD3naKWy/sTxYS5rPK2hO7vj8tvUCSU=",
"lastModified": 1758834834,
"narHash": "sha256-Y7IvY4F8vajZyp3WGf+KaiIVwondEkMFkt92Cr9NZmg=",
"owner": "Gerg-L",
"repo": "mnw",
"rev": "cff958a4e050f8d917a6ff3a5624bc4681c6187d",
"rev": "cfbc7d1cc832e318d0863a5fc91d940a96034001",
"type": "github"
},
"original": {
@@ -289,11 +289,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1756542300,
"narHash": "sha256-tlOn88coG5fzdyqz6R93SQL5Gpq+m/DsWpekNFhqPQk=",
"lastModified": 1759381078,
"narHash": "sha256-gTrEEp5gEspIcCOx9PD8kMaF1iEmfBcTbO0Jag2QhQs=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d7600c775f877cd87b4f5a831c28aa94137377aa",
"rev": "7df7ff7d8e00218376575f0acdcc5d66741351ee",
"type": "github"
},
"original": {
@@ -305,11 +305,11 @@
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1753579242,
"narHash": "sha256-zvaMGVn14/Zz8hnp4VWT9xVnhc8vuL3TStRqwk22biA=",
"lastModified": 1754788789,
"narHash": "sha256-x2rJ+Ovzq0sCMpgfgGaaqgBSwY+LST+WbZ6TytnT9Rk=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "0f36c44e01a6129be94e3ade315a5883f0228a6e",
"rev": "a73b9c743612e4244d865a2fdee11865283c04e6",
"type": "github"
},
"original": {
@@ -328,11 +328,11 @@
]
},
"locked": {
"lastModified": 1756630008,
"narHash": "sha256-weZiVKbiWQzTifm6qCxzhxghEu5mbh9mWNUdkzOLCR0=",
"lastModified": 1759742968,
"narHash": "sha256-yk56xZpanCPlhowzIEdS2GfPDG0yQ4kE/j85lJbAX1Y=",
"owner": "nix-community",
"repo": "NUR",
"rev": "f6a5a7b60dd6065e78ef06390767e689ffa3c23f",
"rev": "9ea4f672c7138273a4131dd25038da49306685b8",
"type": "github"
},
"original": {
@@ -358,11 +358,11 @@
]
},
"locked": {
"lastModified": 1755463179,
"narHash": "sha256-5Ggb1Mhf7ZlRgGi2puCa2PvWs6KbMnWBlW6KW7Vf79Y=",
"lastModified": 1759469269,
"narHash": "sha256-DP833ejGUNRRHsJOB3WRTaWWXLNucaDga2ju/fGe+sc=",
"owner": "NotAShelf",
"repo": "nvf",
"rev": "03833118267ad32226b014b360692bdce9d6e082",
"rev": "e48638aef3a95377689de0ef940443c64f870a09",
"type": "github"
},
"original": {
@@ -381,11 +381,11 @@
]
},
"locked": {
"lastModified": 1756052001,
"narHash": "sha256-dlLqyHxqiFAoIwshKe9X3PzXcJ+up88Qb2JVQswFaNE=",
"lastModified": 1758268943,
"narHash": "sha256-ufkrvMWvS+tgzs5H5iRZn/okuvmSzRLeBf+zUxES6YE=",
"owner": "icewind1991",
"repo": "nvidia-patch-nixos",
"rev": "780af7357d942fad2ddd9f325615a5f6ea7e37ee",
"rev": "e7358911c8f611eb1eb8e0758aa668d4d2d55cd9",
"type": "github"
},
"original": {
@@ -422,11 +422,11 @@
},
"quadlet-nix": {
"locked": {
"lastModified": 1754008153,
"narHash": "sha256-MYT1mDtSkiVg343agxgBFsnuNU3xS8vRy399JXX1Vw0=",
"lastModified": 1758631655,
"narHash": "sha256-EGeZ963L7xsNAY7snvP1JHQe7LWLVCM6f49+PzWjhEE=",
"owner": "SEIAROTg",
"repo": "quadlet-nix",
"rev": "1b2d27d460d8c7e4da5ba44ede463b427160b5c4",
"rev": "2ebe01b175e2e1e6de3f172d23f0c3b88713eec9",
"type": "github"
},
"original": {
@@ -495,11 +495,11 @@
]
},
"locked": {
"lastModified": 1757531894,
"narHash": "sha256-GwV3ES7n/2mwPeu8FGfViI6QfzbTrvNob3OZOsPQId0=",
"lastModified": 1759752146,
"narHash": "sha256-g30leL+8jLxkYWiM5W2RjnhGyqBtErmeOX3ELK5CRAQ=",
"ref": "refs/heads/main",
"rev": "3d069983345ea83549c641dd3f8875e54aaf1c2b",
"revCount": 12,
"rev": "bc1564ea3eb472f7b843e3237da0d1cd2f6f8e37",
"revCount": 14,
"type": "git",
"url": "ssh://git@karaolidis.com/karaolidis/nix-sas.git"
},
@@ -511,11 +511,11 @@
"secrets": {
"flake": false,
"locked": {
"lastModified": 1757519344,
"narHash": "sha256-wLwVbKDPkFCPh9UYLDqCPb62hp6mHBAgjn3Dech54YU=",
"lastModified": 1759165833,
"narHash": "sha256-EYAVKr7gGY7MDmgPIYsW3yk96q51UT1vtzlupR8paKg=",
"ref": "refs/heads/main",
"rev": "8ae051ad0936cb8fbf10b3ab2130f09a07ca1ce6",
"revCount": 39,
"rev": "a5c1c552628492281e05e99458f1ca3ec272b448",
"revCount": 48,
"type": "git",
"url": "ssh://git@karaolidis.com/karaolidis/nix-secrets.git"
},
@@ -531,11 +531,11 @@
]
},
"locked": {
"lastModified": 1754988908,
"narHash": "sha256-t+voe2961vCgrzPFtZxha0/kmFSHFobzF00sT8p9h0U=",
"lastModified": 1759635238,
"narHash": "sha256-UvzKi02LMFP74csFfwLPAZ0mrE7k6EiYaKecplyX9Qk=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "3223c7a92724b5d804e9988c6b447a0d09017d48",
"rev": "6e5a38e08a2c31ae687504196a230ae00ea95133",
"type": "github"
},
"original": {
@@ -554,11 +554,11 @@
]
},
"locked": {
"lastModified": 1756614537,
"narHash": "sha256-qyszmZO9CEKAlj5NBQo1AIIADm5Fgqs5ZggW1sU1TVo=",
"lastModified": 1759638324,
"narHash": "sha256-bj0L3n2UWE/DjqFjsydWsSzO74+dqUA4tiOX4At6LbM=",
"owner": "Gerg-L",
"repo": "spicetify-nix",
"rev": "374eb5d97092b97f7aaafd58a2012943b388c0df",
"rev": "c39a58510e55c4970e57176ab14b722a978e5f01",
"type": "github"
},
"original": {
@@ -589,11 +589,11 @@
]
},
"locked": {
"lastModified": 1755934250,
"narHash": "sha256-CsDojnMgYsfshQw3t4zjRUkmMmUdZGthl16bXVWgRYU=",
"lastModified": 1758728421,
"narHash": "sha256-ySNJ008muQAds2JemiyrWYbwbG+V7S5wg3ZVKGHSFu8=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "74e1a52d5bd9430312f8d1b8b0354c92c17453e5",
"rev": "5eda4ee8121f97b218f7cc73f5172098d458f1d1",
"type": "github"
},
"original": {

View File

@@ -200,13 +200,17 @@
)
((pkgs.lib.filterAttrs (_: config: config.pkgs.system == system)) inputs.self.nixosConfigurations);
packages = pkgs.lib.mapAttrs' (n: pkgs.lib.nameValuePair "package-${n}") inputs.self.packages;
packages = pkgs.lib.mapAttrs' (
name: pkgs.lib.nameValuePair "package-${name}"
) inputs.self.packages.${system};
overlayPackages = pkgs.lib.mapAttrs' (n: pkgs.lib.nameValuePair "overlayPackage-${n}") (
import ./overlays/packages.nix { inherit pkgs; }
);
devShells = pkgs.lib.mapAttrs' (n: pkgs.lib.nameValuePair "devShell-${n}") inputs.self.devShells;
devShells = pkgs.lib.mapAttrs' (
name: pkgs.lib.nameValuePair "devShell-${name}"
) inputs.self.devShells.${system};
formatter.formatting = treefmt.config.build.check inputs.self;
in

View File

@@ -3,5 +3,6 @@
imports = [
./cpu/options.nix
./impermanence/options.nix
./networking/options.nix
];
}

View File

@@ -0,0 +1,17 @@
{ lib, ... }:
{
options.networking =
with lib;
with types;
{
publicIPv4 = mkOption {
type = nullOr str;
description = "The public IPv4 address of this device.";
};
publicIPv6 = mkOption {
type = nullOr str;
description = "The public IPv6 address of this device.";
};
};
}

View File

@@ -42,8 +42,14 @@
"flakes"
];
download-buffer-size = 524288000;
substituters = lib.mkBefore [ "https://nix.karaolidis.com/main" ];
trusted-public-keys = lib.mkBefore [ "main:nJVRBnv73MDkwuV5sgm52m4E2ImOhWHvY12qzjPegAk=" ];
substituters = lib.mkMerge [
(lib.mkBefore [ "https://nix.karaolidis.com/main" ])
(lib.mkAfter [ "https://nix-community.cachix.org/" ])
];
trusted-public-keys = lib.mkBefore [
"nix.karaolidis.com:1yz1tIVLGDEOFC1p/uYtR4Sx+nIbdYDqsDv4kkV0uyk="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
netrc-file = config.sops.templates.nix-netrc.path;
};

View File

@@ -0,0 +1,4 @@
{ pkgs, ... }:
{
environment.systemPackages = with pkgs; [ usbutils ];
}

View File

@@ -0,0 +1,8 @@
{ user, home }:
{ pkgs, ... }:
{
home-manager.users.${user}.home.packages = with pkgs; [
curl
httpie
];
}

View File

@@ -26,7 +26,10 @@ in
push.autoSetupRemote = true;
core.fsmonitor = true;
feature.manyFiles = true;
fetch.writeCommitGraph = true;
fetch = {
prune = true;
writeCommitGraph = true;
};
http.cookiefile = "${home}/.config/git/cookies";
advice.detachedHead = false;
};
@@ -40,6 +43,10 @@ in
}
);
};
aliases = {
adog = "log --all --decorate --oneline --graph";
};
};
home = {

View File

@@ -7,6 +7,7 @@
ipset
ethtool
tcpdump
dig
ipcalc
];
}

View File

@@ -0,0 +1,20 @@
{ user, home }:
{ ... }:
{
environment.persistence."/persist/state"."${home}/.local/state/lazygit" = { };
home-manager.users.${user}.programs.lazygit = {
enable = true;
settings = {
gui = {
showBottomLine = false;
nerdFontsVersion = "3";
animateExplosion = false;
};
disableStartupPopups = true;
};
};
}

View File

@@ -27,11 +27,15 @@
vimAlias = true;
autocomplete = {
blink-cmp.enable = true;
blink-cmp = {
enable = true;
setupOpts = {
signature.enabled = true;
};
};
};
binds = {
# hardtime-nvim.enable = true;
whichKey.enable = true;
};
@@ -45,34 +49,23 @@
comment-nvim.enable = true;
};
# dashboard = {
# alpha.enable = true;
# };
dashboard = {
alpha.enable = true;
};
filetree = {
neo-tree = {
diagnostics = {
enable = true;
setupOpts = {
git_status_async = true;
window.mappings = lib.generators.mkLuaInline ''
{
["<space>"] = "noop",
}
'';
config = {
virtual_text = true;
signs = true;
};
};
};
# formatter = {
# conform-nvim.enable = true;
# };
git = {
enable = true;
# git-conflict.enable = true;
git-conflict.enable = true;
gitsigns.enable = true;
# neogit.enable = true;
vim-fugitive.enable = true;
};
languages = {
@@ -116,14 +109,11 @@
lsp = {
enable = true;
formatOnSave = true;
# nvim-docs-view.enable = true;
# otter-nvim.enable = true;
# trouble.enable = true;
otter-nvim = {
enable = true;
setupOpts.handle_leading_whitespace = true;
};
};
# minimap = {
# codewindow.enable = true;
# };
notify = {
nvim-notify.enable = true;
@@ -136,16 +126,8 @@
smartindent = true;
};
# projects = {
# project-nvim.enable = true;
# };
searchCase = "smart";
# snippets = {
# luasnip.enable = true;
# };
tabline = {
nvimBufferline = {
enable = true;
@@ -160,7 +142,9 @@
telescope = {
enable = true;
setupOpts.defaults.file_ignore_patterns = [
setupOpts.defaults = {
wrap_results = true;
file_ignore_patterns = [
"node_modules"
"%.venv/"
"%.git/"
@@ -170,10 +154,12 @@
"result/"
];
};
};
terminal = {
toggleterm = {
enable = true;
lazygit.enable = true;
setupOpts.winbar.enabled = false;
};
};
@@ -186,41 +172,39 @@
};
ui = {
# breadcrumbs = {
# enable = true;
# navbuddy.enable = true;
# };
colorizer.enable = true;
# fastaction.enable = true;
# illuminate.enable = true;
illuminate.enable = true;
};
undoFile.enable = true;
utility = {
# diffview-nvim.enable = true;
# icon-picker.enable = true;
# images = {
# img-clip.enable = true;
# };
# mkdir.enable = true;
images = {
img-clip = {
enable = true;
setupOpts.default.verbose = false;
};
};
mkdir.enable = true;
motion = {
precognition.enable = true;
};
# nvim-biscuits.enable = true;
# smart-splits.enable = true;
surround.enable = true;
# undotree.enable = true;
# yazi-nvim.enable = true;
undotree.enable = true;
yazi-nvim = {
enable = true;
setupOpts.open_for_directories = true;
};
};
visuals = {
# cinnamon-nvim.enable = true;
# fidget-nvim.enable = true;
# highlight-undo.enable = true;
highlight-undo = {
enable = true;
setupOpts.duration = 250;
};
indent-blankline.enable = true;
nvim-cursorline.enable = true;
# nvim-scrollbar.enable = true;
nvim-scrollbar.enable = true;
nvim-web-devicons.enable = true;
};
@@ -267,23 +251,16 @@
{
mode = [ "n" ];
key = "<leader>wq";
action = "<cmd>wq<CR>";
action = "<cmd>x<CR>";
silent = true;
desc = "Save & Quit";
}
{
mode = [ "n" ];
key = "<leader>ee";
action = "<cmd>Neotree toggle<CR>";
key = "<leader>be";
action = "<cmd>enew<CR>";
silent = true;
desc = "Toggle Neo-tree";
}
{
mode = [ "n" ];
key = "<leader>ef";
action = "<cmd>Neotree reveal<CR>";
silent = true;
desc = "Reveal file in Neo-tree";
desc = "New buffer";
}
];
};

View File

@@ -29,7 +29,6 @@
enable = true;
key = config.sops.secrets."syncthing/key".path;
cert = config.sops.secrets."syncthing/cert".path;
extraOptions = [ "-no-default-folder" ];
settings = {
options.urAccepted = -1;

View File

@@ -8,6 +8,8 @@
settings = {
theme = "matugen";
default_mode = "locked";
pane_frames = false;
copy_command = "wl-copy";

View File

@@ -10,7 +10,7 @@ let
in
{
home-manager.users.${user} = {
programs.rofi.plugins = with pkgs; [ rofi-emoji-wayland ];
programs.rofi.plugins = with pkgs; [ rofi-emoji ];
wayland.windowManager.hyprland.settings.bind = [
# Super + Shift + :

View File

@@ -0,0 +1,7 @@
{ user, home }:
{ ... }:
{
programs.ghidra.enable = true;
environment.persistence."/persist/state"."${home}/.config/ghidra" = { };
}

View File

@@ -99,6 +99,8 @@
"$mod, mouse:273, resizewindow"
];
gesture = [ "3, horizontal, workspace" ];
input = {
accel_profile = "flat";
kb_layout = "us,gr";
@@ -114,8 +116,6 @@
};
gestures = {
workspace_swipe = true;
workspace_swipe_min_fingers = true;
workspace_swipe_forever = true;
workspace_swipe_cancel_ratio = 0.2;
};

View File

@@ -14,7 +14,7 @@ in
home-manager.users.${user} = {
programs.rofi = {
enable = true;
package = pkgs.rofi-wayland;
package = pkgs.rofi;
};
home.file.${hmConfig.programs.rofi.configPath}.enable = false;

View File

@@ -0,0 +1,7 @@
{ user, home }:
{ pkgs, ... }:
{
environment.persistence."/persist/state"."${home}/.config/Signal" = { };
home-manager.users.${user}.home.packages = with pkgs; [ signal-desktop ];
}

View File

@@ -0,0 +1,17 @@
{ user, home }:
{ pkgs, ... }:
{
programs.wireshark = {
enable = true;
dumpcap.enable = true;
usbmon.enable = true;
};
boot.kernelModules = [ "usbmon" ];
users.users.${user}.extraGroups = [ "wireshark" ];
environment.persistence."/persist/state"."${home}/.config/wireshark" = { };
home-manager.users.${user}.home.packages = with pkgs; [ wireshark ];
}

View File

@@ -36,7 +36,7 @@ in
programs = {
go = {
enable = true;
goPath = ".local/share/go";
env.GOPATH = "${home}/.local/share/go";
};
gradle = {

View File

@@ -131,6 +131,12 @@ in
identityFile = "${home}/.ssh/ssh_personal_ed25519_key";
};
"vps.karaolidis.com" = {
hostname = "vps.karaolidis.com";
user = "root";
identityFile = "${home}/.ssh/ssh_personal_ed25519_key";
};
"github.com" = {
hostname = "github.com";
user = "git";

View File

@@ -1,36 +1,5 @@
{ user, home }:
{ config, pkgs, ... }:
let
systemctl = "${pkgs.systemd}/bin/systemctl";
in
{ pkgs, ... }:
{
# FIXME: https://github.com/nix-community/NixOS-WSL/issues/375
# FIXME: https://github.com/Mic92/sops-nix/issues/687
# FIXME: https://github.com/microsoft/WSL/issues/8842
# FIXME: https://github.com/microsoft/WSL/issues/10205
# Fuck Microsoft.
security.sudo.extraRules = [
{
users = [ config.users.users.${user}.name ];
commands = [
{
command = "${systemctl} restart user@${toString config.users.users.${user}.uid}.service";
options = [ "NOPASSWD" ];
}
{
command = "${systemctl} restart user@${toString config.users.users.${user}.uid}";
options = [ "NOPASSWD" ];
}
];
}
];
users.users.${user}.shell = pkgs.writeShellApplication {
name = "wsl-zsh";
runtimeInputs = with pkgs; [ systemd ];
text = builtins.readFile ./wsl-zsh.sh;
passthru.shellPath = "/bin/wsl-zsh";
};
home-manager.users.${user}.home.packages = with pkgs; [ wsl-wl-clipboard ];
}

View File

@@ -1,15 +0,0 @@
# shellcheck shell=bash
user_bus="${DBUS_SESSION_BUS_ADDRESS#unix:path=}"
if [ -S "$user_bus" ]; then
exec zsh
fi
until [ -S /run/dbus/system_bus_socket ]; do
sleep 0.1
done
sudo systemctl restart "user@${UID}.service"
exec zsh

View File

@@ -16,6 +16,7 @@ in
(import ../../../common/configs/user/console/attic { inherit user home; })
(import ../../../common/configs/user/console/btop { inherit user home; })
(import ../../../common/configs/user/console/curl { inherit user home; })
(import ../../../common/configs/user/console/dive { inherit user home; })
(import ../../../common/configs/user/console/fastfetch { inherit user home; })
(import ../../../common/configs/user/console/ffmpeg { inherit user home; })
@@ -26,6 +27,7 @@ in
(import ../../../common/configs/user/console/ip { inherit user home; })
(import ../../../common/configs/user/console/jq { inherit user home; })
(import ../../../common/configs/user/console/kubernetes { inherit user home; })
(import ../../../common/configs/user/console/lazygit { inherit user home; })
(import ../../../common/configs/user/console/lsof { inherit user home; })
(import ../../../common/configs/user/console/mprocs { inherit user home; })
(import ../../../common/configs/user/console/ncdu { inherit user home; })

View File

@@ -45,6 +45,7 @@
../common/configs/system/system
../common/configs/system/timezone
../common/configs/system/upower
../common/configs/system/usb
../common/configs/system/users
../common/configs/system/zsh

View File

@@ -62,10 +62,6 @@
name = "alc285-fixup";
patch = ./gu605c-spi-cs-gpio/alc285-fixup.patch;
}
{
name = "iwlwifi-no-disable-all-chans";
patch = ./iwlwifi/iwlwifi-no-disable-all-chans.patch;
}
];
initrd = {

View File

@@ -1,26 +0,0 @@
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
index 6adcfa6e214a..4512d846629c 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
@@ -622,7 +622,7 @@ int iwl_fill_lari_config(struct iwl_fw_runtime *fwrt,
cmd->oem_uhb_allow_bitmap = cpu_to_le32(value);
ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_FORCE_DISABLE_CHANNELS, &value);
- if (!ret)
+ if (!ret && value != 0xFFFFFFFF)
cmd->force_disable_channels_bitmap = cpu_to_le32(value);
ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENERGY_DETECTION_THRESHOLD,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
index a75af8c1e8ab..e055a946b9e6 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c
@@ -259,7 +259,7 @@ void iwl_mld_configure_lari(struct iwl_mld *mld)
cmd.oem_uhb_allow_bitmap = cpu_to_le32(value);
ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_FORCE_DISABLE_CHANNELS, &value);
- if (!ret)
+ if (!ret && value != 0xFFFFFFFF)
cmd.force_disable_channels_bitmap = cpu_to_le32(value);
ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENERGY_DETECTION_THRESHOLD,

View File

@@ -75,6 +75,12 @@ in
identityFile = "${home}/.ssh/ssh_personal_ed25519_key";
};
"vps.karaolidis.com" = {
hostname = "vps.karaolidis.com";
user = "root";
identityFile = "${home}/.ssh/ssh_personal_ed25519_key";
};
"github.com" = {
hostname = "github.com";
user = "git";

View File

@@ -18,6 +18,7 @@ in
(import ../../../common/configs/user/console/attic { inherit user home; })
(import ../../../common/configs/user/console/brightnessctl { inherit user home; })
(import ../../../common/configs/user/console/btop { inherit user home; })
(import ../../../common/configs/user/console/curl { inherit user home; })
(import ../../../common/configs/user/console/dive { inherit user home; })
(import ../../../common/configs/user/console/fastfetch { inherit user home; })
(import ../../../common/configs/user/console/ffmpeg { inherit user home; })
@@ -27,6 +28,7 @@ in
(import ../../../common/configs/user/console/imagemagick { inherit user home; })
(import ../../../common/configs/user/console/ip { inherit user home; })
(import ../../../common/configs/user/console/jq { inherit user home; })
(import ../../../common/configs/user/console/lazygit { inherit user home; })
(import ../../../common/configs/user/console/libvirt { inherit user home; })
(import ../../../common/configs/user/console/lsof { inherit user home; })
(import ../../../common/configs/user/console/mprocs { inherit user home; })
@@ -68,6 +70,7 @@ in
(import ../../../common/configs/user/gui/gaming/prismlauncher { inherit user home; })
(import ../../../common/configs/user/gui/gaming/proton { inherit user home; })
(import ../../../common/configs/user/gui/gaming/wivrn { inherit user home; })
(import ../../../common/configs/user/gui/ghidra { inherit user home; })
(import ../../../common/configs/user/gui/gtk { inherit user home; })
(import ../../../common/configs/user/gui/hypridle { inherit user home; })
(import ../../../common/configs/user/gui/hyprland { inherit user home; })
@@ -85,11 +88,13 @@ in
(import ../../../common/configs/user/gui/qt { inherit user home; })
(import ../../../common/configs/user/gui/rofi { inherit user home; })
(import ../../../common/configs/user/gui/rquickshare { inherit user home; })
(import ../../../common/configs/user/gui/signal { inherit user home; })
(import ../../../common/configs/user/gui/swww { inherit user home; })
(import ../../../common/configs/user/gui/theme { inherit user home; })
(import ../../../common/configs/user/gui/transmission { inherit user home; })
(import ../../../common/configs/user/gui/vscode { inherit user home; })
(import ../../../common/configs/user/gui/wev { inherit user home; })
(import ../../../common/configs/user/gui/wireshark { inherit user home; })
(import ../../../common/configs/user/gui/wl-clipboard { inherit user home; })
(import ../../../common/configs/user/gui/x11 { inherit user home; })
(import ../../../common/configs/user/gui/xdg { inherit user home; })

View File

@@ -37,6 +37,7 @@
../common/configs/system/sudo
../common/configs/system/system
../common/configs/system/timezone
../common/configs/system/usb
../common/configs/system/users
../common/configs/system/zsh

View File

@@ -74,6 +74,12 @@ in
identityFile = "${home}/.ssh/ssh_personal_ed25519_key";
};
"vps.karaolidis.com" = {
hostname = "vps.karaolidis.com";
user = "root";
identityFile = "${home}/.ssh/ssh_personal_ed25519_key";
};
"github.com" = {
hostname = "github.com";
user = "git";

View File

@@ -2,7 +2,6 @@
let
jupiterConfig = inputs.self.nixosConfigurations.jupiter.config;
wireguardPort = 51821;
jupiterPublicIPv4 = "51.89.210.124";
in
{
boot.kernel.sysctl = {
@@ -29,7 +28,7 @@ in
name = "jupiter";
allowedIPs = [
"10.0.0.2/32"
"${jupiterPublicIPv4}/32"
"${jupiterConfig.networking.publicIPv4}/32"
];
publicKey = builtins.readFile "${inputs.secrets}/hosts/jupiter/wireguard_key.pub";
}

View File

@@ -33,7 +33,10 @@
./configs/wireguard
];
networking.hostName = "jupiter-vps";
networking = {
hostName = "jupiter-vps";
publicIPv4 = "217.154.55.15";
};
environment.impermanence.enable = lib.mkForce false;

View File

@@ -1,7 +1,7 @@
{
disko.devices = {
disk.main = {
device = "/dev/sda";
device = "/dev/vda";
type = "disk";
content = {
type = "gpt";

View File

@@ -17,13 +17,13 @@ Remember to update [format.nix](format.nix).
### Adding a new drive
Create a `format.nix` containing the new disk layout. Do not include already existing disks in this file, nor the global pool.
```
parted /dev/sdd -- mklabel gpt
parted /dev/sdd -- mkpart primary 0% 100%
cryptsetup luksFormat /dev/sdd
cryptsetup open /dev/sdd storage2
disko -m "destroy" --yes-wipe-all-disks format.nix
disko -m "format" --yes-wipe-all-disks format.nix
btrfs device add /dev/mapper/storage2 /mnt/storage
btrfs balance start -dconvert=raid1 -mconvert=raid1 /mnt/storage
btrfs balance start -dconvert=raid1 -mconvert=raid1 /mnt/storage --bg
```
### Removing an old drive

View File

@@ -117,62 +117,87 @@ in
filters = [ ];
whitelist_filters = [ ];
user_rules = [
"||*^"
# Personal
"@@||karaolidis.com^$important"
# Connectivity Check
"@@||clients3.google.com^"
"@@||clients.l.google.com^"
"@@||connectivitycheck.gstatic.com^"
"@@||connectivitycheck.android.com^"
# NTP
"@@||pool.ntp.org^$important"
"@@||time.android.com^$important"
"@@||time.akamai.com^$important"
# Plex
"@@||plex.tv^$important"
"@@||plex.direct^$important"
# YouTube
"@@||youtube.com^$important"
"@@||yt.be^$important"
"@@||ytimg.com^$important"
"@@||googlevideo.com^$important"
# YouTube Extensions
"@@||returnyoutubedislikeapi.com^$important"
"@@||sponsor.ajay.app^$important"
# Google Misc
"@@||accounts.google.com^$important"
"@@||www.gstatic.com^$important"
"@@||content-autofill.googleapis.com^$important"
# Google Play
"@@||play.google.com^$important"
"@@||android.googleapis.com^$important"
"@@||androidtvsetupwraithfe-pa.googleapis.com^$important"
"@@||play-fe.googleapis.com^$important"
"@@||play-lh.googleusercontent.com^$important"
"@@||play.googleapis.com^$important"
"@@||android.apis.google.com^$important"
"@@||playatoms-pa.googleapis.com^$important"
"@@||gvt1.com^$important"
# Spotify
"@@||spotify.com^$important"
"@@||spotify.dev^$important"
"@@||scdn.co^$important"
"@@||tospotify.com^$important"
"@@||spotifycdn.com^$important"
# Twitch
"@@||twitch.tv^$important"
"@@||ttvnw.net^$important"
"@@||static-cdn.jtvnw.net^$important"
# Cosmote TV
"@@||account.cosmote.gr^$important"
"@@||cosmotetvott.gr^$important"
"@@||msvdn.net^$important"
"@@||theplatform.eu^$important"
"@@||theplatform.com^$important"
filtering.rewrites = [
{
domain = "beta.media.karaolidis.com";
answer = inboundGateway;
}
];
user_rules =
let
domains = [
# Personal
"beta.media.karaolidis.com"
# Connectivity Check
"clients3.google.com"
"clients.l.google.com"
"connectivitycheck.gstatic.com"
"connectivitycheck.android.com"
# NTP
"pool.ntp.org"
"time.android.com"
"time.akamai.com"
# Plex
"plex.tv"
"plex.direct"
# YouTube
"youtube.com"
"yt.be"
"ytimg.com"
"googlevideo.com"
# YouTube Extensions
"returnyoutubedislikeapi.com"
"sponsor.ajay.app"
# Google Misc
"accounts.google.com"
"www.gstatic.com"
"content-autofill.googleapis.com"
# Google Play
"play.google.com"
"android.googleapis.com"
"androidtvsetupwraithfe-pa.googleapis.com"
"play-fe.googleapis.com"
"play-lh.googleusercontent.com"
"play.googleapis.com"
"android.apis.google.com"
"playatoms-pa.googleapis.com"
"gvt1.com"
# Spotify
"spotify.com"
"spotify.dev"
"scdn.co"
"tospotify.com"
"spotifycdn.com"
# Twitch
"twitch.tv"
"ttvnw.net"
"static-cdn.jtvnw.net"
# Cosmote TV
"account.cosmote.gr"
"cosmotetvott.gr"
"msvdn.net"
"theplatform.eu"
"theplatform.com"
# Releases
"github.com"
"release-assets.githubusercontent.com"
];
in
[ "||*^" ] ++ (map (domain: "@@||${domain}^$important") domains);
schema_version = 29;
};
in

View File

@@ -7,8 +7,6 @@
let
jupiterVpsConfig = inputs.self.nixosConfigurations.jupiter-vps.config;
wireguardPort = jupiterVpsConfig.networking.wireguard.interfaces.wg0.listenPort;
jupiterVpsPublicIPv4 = "51.75.170.190";
jupiterPublicIPv4 = "51.89.210.124";
in
{
sops.secrets."wireguard/client/vps" = { };
@@ -29,21 +27,21 @@ in
{
ips = [
"10.0.0.2/24"
"${jupiterPublicIPv4}/32"
"${config.networking.publicIPv4}/32"
];
privateKeyFile = config.sops.secrets."wireguard/client/vps".path;
inherit table;
postSetup = [ "${ip} rule add from ${jupiterPublicIPv4} table ${table}" ];
postShutdown = [ "${ip} rule del from ${jupiterPublicIPv4} table ${table}" ];
postSetup = [ "${ip} rule add from ${config.networking.publicIPv4} table ${table}" ];
postShutdown = [ "${ip} rule del from ${config.networking.publicIPv4} table ${table}" ];
peers = [
{
name = "jupiter-vps";
allowedIPs = [ "0.0.0.0/0" ];
publicKey = builtins.readFile "${inputs.secrets}/hosts/jupiter-vps/wireguard_key.pub";
endpoint = "${jupiterVpsPublicIPv4}:${builtins.toString wireguardPort}";
endpoint = "${jupiterVpsConfig.networking.publicIPv4}:${builtins.toString wireguardPort}";
persistentKeepalive = 25;
}
];

View File

@@ -39,6 +39,7 @@
../common/configs/system/sshd
../common/configs/system/sudo
../common/configs/system/system
../common/configs/system/usb
../common/configs/system/users
../common/configs/system/zsh
@@ -52,13 +53,18 @@
./users/tv
];
networking.hostName = "jupiter";
networking = {
hostName = "jupiter";
publicIPv4 = "87.106.36.59";
};
boot.initrd = {
luks.devices = {
main.keyFile = "/usb/keyfile";
storage0.keyFile = "/usb/keyfile";
storage1.keyFile = "/usb/keyfile";
storage2.keyFile = "/usb/keyfile";
storage3.keyFile = "/usb/keyfile";
};
systemd.contents."/etc/fstab".text = ''

View File

@@ -123,10 +123,66 @@
settings = {
allowDiscards = true;
};
};
};
};
};
};
storage2 = {
device = "/dev/disk/by-id/ata-TOSHIBA_MG11ACA24TE_7512A15XF6AL";
type = "disk";
content = {
type = "gpt";
partitions = {
root = {
name = "root";
size = "100%";
content = {
name = "storage2";
type = "luks";
passwordFile = "/tmp/keyfile";
settings = {
allowDiscards = true;
};
};
};
};
};
};
storage3 = {
device = "/dev/disk/by-id/ata-TOSHIBA_MG11ACA24TE_7512A192F6AL";
type = "disk";
content = {
type = "gpt";
partitions = {
root = {
name = "root";
size = "100%";
content = {
name = "storage3";
type = "luks";
passwordFile = "/tmp/keyfile";
settings = {
allowDiscards = true;
};
content = {
type = "btrfs";
extraArgs = [ "-f -L storage -m raid1 -d raid1 /dev/mapper/storage0" ];
extraArgs = [
"-f"
"-L"
"storage"
"-m"
"raid1"
"-d"
"raid1"
"/dev/mapper/storage0"
"/dev/mapper/storage1"
"/dev/mapper/storage2"
# Implicit /dev/mapper/storage3
];
subvolumes =
let
mountOptions = [

View File

@@ -22,12 +22,22 @@
# FIXME: https://github.com/icewind1991/nvidia-patch-nixos/issues/9
package =
let
nvidiaStable = config.boot.kernelPackages.nvidiaPackages.stable;
# FIXME: HDMI Crash, God knows when it will be reported and/or fixed
nvidiaStable = config.boot.kernelPackages.nvidiaPackages.mkDriver {
version = "580.82.09";
sha256_64bit = "sha256-Puz4MtouFeDgmsNMKdLHoDgDGC+QRXh6NVysvltWlbc=";
sha256_aarch64 = "sha256-6tHiAci9iDTKqKrDIjObeFdtrlEwjxOHJpHfX4GMEGQ=";
openSha256 = "sha256-YB+mQD+oEDIIDa+e8KX1/qOlQvZMNKFrI5z3CoVKUjs=";
settingsSha256 = "sha256-um53cr2Xo90VhZM1bM2CH4q9b/1W2YOqUcvXPV6uw2s=";
persistencedSha256 = "sha256-lbYSa97aZ+k0CISoSxOMLyyMX//Zg2Raym6BC4COipU=";
};
maybeFbc =
if builtins.hasAttr nvidiaStable.version pkgs.nvidia-patch-list.fbc then
pkgs.nvidia-patch.patch-fbc nvidiaStable
else
nvidiaStable;
nvidiaStableFinal =
if builtins.hasAttr nvidiaStable.version pkgs.nvidia-patch-list.nvenc then
pkgs.nvidia-patch.patch-nvenc maybeFbc
@@ -53,8 +63,6 @@
graphics = {
enable32Bit = true;
extraPackages = with pkgs; [
amdvlk
driversi686Linux.amdvlk
rocmPackages.clr
rocmPackages.clr.icd
];
@@ -92,10 +100,7 @@
];
};
nixpkgs.config = {
cudaSupport = true;
rocmSupport = true;
};
nixpkgs.config.cudaSupport = true;
services = {
xserver.videoDrivers = [ "nvidia" ];

View File

@@ -16,6 +16,7 @@ in
"attic/postgresql".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"attic/rs256".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"attic/admin".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"attic/keypairs/main".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
};
templates = {
@@ -94,6 +95,7 @@ in
[
"/mnt/storage/private/storm/containers/storage/volumes/attic/_data:/var/lib/attic"
"${hmConfig.sops.templates.attic-server.path}:/etc/attic/server.toml:ro"
"${hmConfig.sops.secrets."attic/keypairs/main".path}:/etc/attic/keypairs/main:ro"
"${postStart}:/etc/attic/post-start.sh:ro"
];
environmentFiles = [ hmConfig.sops.templates.attic-env.path ];

View File

@@ -11,9 +11,11 @@ while true; do
set -o errexit
if [ $status -eq 0 ]; then
attic cache configure "$CACHE_NAME" --keypair-path "/etc/attic/keypairs/$CACHE_NAME"
break
elif echo "$out" | grep -q "NoSuchCache"; then
attic cache create "$CACHE_NAME"
attic cache create "$CACHE_NAME" --keypair-path "/etc/attic/keypairs/$CACHE_NAME"
break
elif echo "$out" | grep -q "404"; then
sleep 0.1
else

View File

@@ -132,10 +132,11 @@ in
"media"
"vaultwarden"
"nextcloud"
"jellyfin"
"gitea"
"outline"
"shlink"
"comentario"
"immich"
];
};
}

View File

@@ -0,0 +1,66 @@
{ user, home }:
{
config,
inputs,
lib,
pkgs,
...
}:
let
hmConfig = config.home-manager.users.${user};
inherit (hmConfig.virtualisation.quadlet) volumes networks;
in
{
home-manager.users.${user} = {
sops = {
secrets."blog/apiKey".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
templates.blog-receiver-env.content = ''
AUTH_KEY=${hmConfig.sops.placeholder."blog/apiKey"}
'';
};
virtualisation.quadlet = {
volumes.blog = { };
containers = {
blog.containerConfig = {
image = "docker-archive:${pkgs.dockerImages.nginx}";
networks = [ networks.traefik.ref ];
volumes = [ "${volumes.blog.ref}:/var/www/nginx:ro" ];
labels = [
"traefik.enable=true"
"traefik.http.routers.blog.rule=Host(`blog.karaolidis.com`)"
"traefik.http.routers.root.rule=Host(`karaolidis.com`) || Host(`www.karaolidis.com`)"
"traefik.http.routers.root.middlewares=redirect-root-to-blog"
"traefik.http.routers.root.service=noop@internal"
"traefik.http.middlewares.redirect-root-to-blog.redirectregex.regex=^https://(www\.)?karaolidis\.com(/.*)?$"
"traefik.http.middlewares.redirect-root-to-blog.redirectregex.replacement=https://blog.karaolidis.com$${2}"
"traefik.http.middlewares.redirect-root-to-blog.redirectregex.permanent=false"
];
};
blog-receiver = {
containerConfig = {
image = "docker-archive:${pkgs.dockerImages.nginx-receiver}";
networks = [ networks.traefik.ref ];
volumes = [ "${volumes.blog.ref}:/var/www/nginx" ];
environments = {
TARGET_DIR = "/var/www/nginx";
SUBPATH = "/upload";
};
environmentFiles = [ hmConfig.sops.templates.blog-receiver-env.path ];
labels = [
"traefik.enable=true"
"traefik.http.routers.blog-receiver.rule=Host(`blog.karaolidis.com`) && PathPrefix(`/upload`)"
];
};
unitConfig.After = [ "sops-nix.service" ];
};
};
};
};
}

View File

@@ -0,0 +1,171 @@
{ user, home }:
{
config,
inputs,
pkgs,
lib,
...
}:
let
hmConfig = config.home-manager.users.${user};
inherit (hmConfig.virtualisation.quadlet) containers volumes networks;
autheliaClientId = "d16NArYYcTbDU0YTQEwmlvzWAzJKhbIbe4s8wGENSRTK40gvAwGbYO0fCSq4rh6pjNxI0ZuH1cM8XnADCgSV9SHRzgX9MqcFre5r";
in
{
home-manager.users.${user} = {
sops = {
secrets = {
"comentario/postgresql".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"comentario/authelia/password".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"comentario/authelia/digest".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"comentario/smtp".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
};
templates = {
comentario-postgresql-env.content = ''
POSTGRES_PASSWORD=${hmConfig.sops.placeholder."comentario/postgresql"}
'';
comentario-secrets.content = builtins.readFile (
(pkgs.formats.yaml { }).generate "secrets.yaml" {
postgres = {
host = "comentario-postgresql";
port = 5432;
database = "comentario";
username = "comentario";
password = hmConfig.sops.placeholder."comentario/postgresql";
};
smtpServer = {
host = "smtp.protonmail.ch";
port = 587;
username = "jupiter@karaolidis.com";
password = hmConfig.sops.placeholder."comentario/smtp";
};
idp.oidc = [
{
id = "authelia";
name = "Authelia";
url = "https://id.karaolidis.com";
scopes = [
"openid"
"profile"
"email"
"is_admin"
];
key = autheliaClientId;
secret = hmConfig.sops.placeholder."comentario/authelia/password";
superuserClaim = "is_admin";
}
];
}
);
authelia-comentario.content = builtins.readFile (
(pkgs.formats.yaml { }).generate "comentario.yaml" {
identity_providers.oidc = {
authorization_policies.comentario = {
default_policy = "deny";
rules = [
{
policy = "one_factor";
subject = "group:comentario";
}
];
};
clients = [
{
client_id = autheliaClientId;
client_name = "Comentario";
client_secret = hmConfig.sops.placeholder."comentario/authelia/digest";
redirect_uris = [ "https://comments.karaolidis.com/api/oauth/oidc:authelia/callback" ];
authorization_policy = "comentario";
claims_policy = "is_admin";
scopes = [
"openid"
"profile"
"email"
"is_admin"
];
pre_configured_consent_duration = "1 year";
}
];
};
}
);
};
};
virtualisation.quadlet = {
networks.comentario = { };
volumes.comentario-postgresql = { };
containers = {
comentario = {
containerConfig = {
image = "docker-archive:${pkgs.dockerImages.comentario}";
networks = [
networks.comentario.ref
networks.traefik.ref
];
volumes =
let
config = (pkgs.formats.yaml { }).generate "config.yaml" {
baseUrl = "https://comments.karaolidis.com";
log.noColor = true;
dynamicConfigDefaults.auth = {
emailUpdate.enabled = true;
signup = {
confirm.commenter = false;
enabled = false;
sso.enabled = true;
};
};
};
in
[
"${config}:/etc/comentario/config.yaml:ro"
"${hmConfig.sops.templates.comentario-secrets.path}:/etc/comentario/secrets.yaml:ro"
];
labels = [
"traefik.enable=true"
"traefik.http.routers.comentario.rule=Host(`comments.karaolidis.com`)"
];
};
unitConfig = {
After = [
"${containers.comentario-postgresql._serviceName}.service"
"sops-nix.service"
];
Requires = [ "${containers.comentario-postgresql._serviceName}.service" ];
};
};
comentario-postgresql = {
containerConfig = {
image = "docker-archive:${pkgs.dockerImages.postgresql}";
networks = [ networks.comentario.ref ];
volumes = [ "${volumes.comentario-postgresql.ref}:/var/lib/postgresql/data" ];
environments = {
POSTGRES_DB = "comentario";
POSTGRES_USER = "comentario";
};
environmentFiles = [ hmConfig.sops.templates.comentario-postgresql-env.path ];
};
unitConfig.After = [ "sops-nix.service" ];
};
authelia.containerConfig.volumes = [
"${hmConfig.sops.templates.authelia-comentario.path}:/etc/authelia/conf.d/comentario.yaml:ro"
];
};
};
};
}

View File

@@ -12,8 +12,11 @@ in
imports = [
(import ./attic { inherit user home; })
(import ./authelia { inherit user home; })
(import ./blog { inherit user home; })
(import ./comentario { inherit user home; })
(import ./gitea { inherit user home; })
(import ./grafana { inherit user home; })
(import ./immich { inherit user home; })
(import ./littlelink { inherit user home; })
(import ./lore { inherit user home; })
(import ./media { inherit user home; })

View File

@@ -61,7 +61,12 @@ in
home-manager.users.${user} =
let
autheliaClientId = "I2ZYDFGWP1bzfiauXe94IaiReZF6SqoEskSp6phoL2L8l16Cq7YX3Vr4pkQOSYfNDOwuFjTRIpqQ8eAqK0M93NeEgpr8YoPhKHyR";
inherit (hmConfig.virtualisation.quadlet) containers volumes networks;
inherit (hmConfig.virtualisation.quadlet)
containers
volumes
networks
images
;
in
{
sops = {
@@ -178,7 +183,7 @@ in
default_policy = "deny";
rules = [
{
policy = "one_factor";
policy = "two_factor";
subject = "group:gitea";
}
];
@@ -191,6 +196,7 @@ in
client_secret = hmConfig.sops.placeholder."gitea/authelia/digest";
redirect_uris = [ "https://git.karaolidis.com/user/oauth2/authelia/callback" ];
authorization_policy = "gitea";
pre_configured_consent_duration = "1 year";
}
];
};
@@ -213,6 +219,16 @@ in
gitea-act-runner-cache = { };
};
images.gitea-act-runner-worker.imageConfig = {
image = "docker-archive:${pkgs.dockerImages.gitea-act-runner-worker}";
tag =
let
name = pkgs.dockerImages.gitea-act-runner-worker.passthru.buildArgs.name;
tag = pkgs.dockerImages.gitea-act-runner-worker.passthru.imageTag;
in
"localhost/${name}:${tag}";
};
containers = {
gitea = {
containerConfig = {
@@ -277,8 +293,22 @@ in
volumes =
let
uid = builtins.toString config.users.users.${user}.uid;
runnerConfig = (pkgs.formats.yaml { }).generate "config.yaml" {
runner = {
file = "/var/lib/gitea-act-runner/registration";
capacity = 4;
labels = [ "nix:docker://${images.gitea-act-runner-worker.imageConfig.tag}" ];
};
cache.dir = "/tmp/gitea-act-runner/";
container = {
privileged = true;
docker_host = "-";
};
};
in
[
"${runnerConfig}:/etc/gitea-act-runner/config.yaml:ro"
"/run/user/${uid}/podman/podman.sock:/var/run/docker.sock"
"${volumes.gitea-act-runner-data.ref}:/var/lib/gitea-act-runner"
"${volumes.gitea-act-runner-cache.ref}:/tmp/gitea-act-runner"

View File

@@ -0,0 +1,10 @@
apiVersion: 1
policies:
- orgId: 1
receiver: ntfy.sh
group_by:
- grafana_folder
- alertname
group_wait: 0s
group_interval: 1m
repeat_interval: 1h

View File

@@ -0,0 +1,454 @@
apiVersion: 1
groups:
- orgId: 1
name: Default
folder: System
interval: 10s
rules:
- uid: cpu-usage
title: CPU Usage
condition: C
data:
- refId: A
relativeTimeRange:
from: 600
to: 0
datasourceUid: prometheus
model:
editorMode: code
expr: 1 - avg by(hostname) (rate(node_cpu_seconds_total{mode="idle"}[1h]))
instant: true
intervalMs: 1000
legendFormat: __auto
maxDataPoints: 43200
range: false
refId: A
- refId: C
datasourceUid: __expr__
model:
conditions:
- evaluator:
params:
- 0.9
type: gt
operator:
type: and
query:
params:
- C
reducer:
params: []
type: last
type: query
datasource:
type: __expr__
uid: __expr__
expression: A
intervalMs: 1000
maxDataPoints: 43200
refId: C
type: threshold
noDataState: NoData
execErrState: Error
for: 30m
keepFiringFor: 5m
isPaused: false
notification_settings:
receiver: ntfy.sh
- uid: memory-usage
title: Memory Usage
condition: C
data:
- refId: A
relativeTimeRange:
from: 600
to: 0
datasourceUid: prometheus
model:
editorMode: code
expr: 1 - (node_memory_MemAvailable_bytes{} / node_memory_MemTotal_bytes{})
instant: true
intervalMs: 1000
legendFormat: __auto
maxDataPoints: 43200
range: false
refId: A
- refId: C
datasourceUid: __expr__
model:
conditions:
- evaluator:
params:
- 0.9
type: gt
operator:
type: and
query:
params:
- C
reducer:
params: []
type: last
type: query
datasource:
type: __expr__
uid: __expr__
expression: A
intervalMs: 1000
maxDataPoints: 43200
refId: C
type: threshold
noDataState: NoData
execErrState: Error
for: 5m
isPaused: false
notification_settings:
receiver: ntfy.sh
- uid: cpu-temperature
title: CPU Temperature
condition: C
data:
- refId: A
relativeTimeRange:
from: 600
to: 0
datasourceUid: prometheus
model:
editorMode: code
expr: node_hwmon_temp_celsius{chip="pci0000:00_0000:00:18_3", sensor="temp1"}
instant: true
intervalMs: 1000
legendFormat: __auto
maxDataPoints: 43200
range: false
refId: A
- refId: C
datasourceUid: __expr__
model:
conditions:
- evaluator:
params:
- 75
type: gt
operator:
type: and
query:
params:
- C
reducer:
params: []
type: last
type: query
datasource:
type: __expr__
uid: __expr__
expression: A
intervalMs: 1000
maxDataPoints: 43200
refId: C
type: threshold
noDataState: NoData
execErrState: Error
for: 30m
keepFiringFor: 5m
isPaused: false
notification_settings:
receiver: ntfy.sh
- uid: amabient-temperature
title: Ambient Temperature
condition: C
data:
- refId: A
relativeTimeRange:
from: 600
to: 0
datasourceUid: prometheus
model:
editorMode: code
expr: avg(node_hwmon_temp_celsius{chip="thermal_thermal_zone0"})
instant: true
intervalMs: 1000
legendFormat: __auto
maxDataPoints: 43200
range: false
refId: A
- refId: C
datasourceUid: __expr__
model:
conditions:
- evaluator:
params:
- 70
type: gt
operator:
type: and
query:
params:
- C
reducer:
params: []
type: last
type: query
datasource:
type: __expr__
uid: __expr__
expression: A
intervalMs: 1000
maxDataPoints: 43200
refId: C
type: threshold
noDataState: NoData
execErrState: Error
for: 15m
keepFiringFor: 5m
isPaused: false
notification_settings:
receiver: ntfy.sh
- uid: smart-status
title: SMART Status
condition: C
data:
- refId: A
relativeTimeRange:
from: 600
to: 0
datasourceUid: prometheus
model:
editorMode: code
expr: smartctl_device_smart_status
instant: true
intervalMs: 1000
legendFormat: __auto
maxDataPoints: 43200
range: false
refId: A
- refId: C
datasourceUid: __expr__
model:
conditions:
- evaluator:
params:
- 1
type: lt
operator:
type: and
query:
params:
- C
reducer:
params: []
type: last
type: query
datasource:
type: __expr__
uid: __expr__
expression: A
intervalMs: 1000
maxDataPoints: 43200
refId: C
type: threshold
noDataState: NoData
execErrState: Error
keepFiringFor: 1h
isPaused: false
notification_settings:
receiver: ntfy.sh
- uid: smart-errors
title: SMART Errors
condition: C
data:
- refId: A
relativeTimeRange:
from: 600
to: 0
datasourceUid: prometheus
model:
editorMode: code
expr: "sum(\n increase(smartctl_device_attribute{attribute_value_type=\"raw\", attribute_name=~\"Raw_Read_Error_Rate|Seek_Error_Rate|Offline_Uncorrectable\"}[1h])\n) + \nsum(\n increase(smartctl_device_media_errors[1h])\n)"
instant: true
intervalMs: 1000
legendFormat: __auto
maxDataPoints: 43200
range: false
refId: A
- refId: C
datasourceUid: __expr__
model:
conditions:
- evaluator:
params:
- 0
type: gt
operator:
type: and
query:
params:
- C
reducer:
params: []
type: last
type: query
datasource:
type: __expr__
uid: __expr__
expression: A
intervalMs: 1000
maxDataPoints: 43200
refId: C
type: threshold
noDataState: NoData
execErrState: Error
keepFiringFor: 1h
isPaused: false
notification_settings:
receiver: ntfy.sh
- uid: smart-temperature
title: SMART Temperature
condition: C
data:
- refId: A
relativeTimeRange:
from: 600
to: 0
datasourceUid: prometheus
model:
editorMode: code
expr: smartctl_device_temperature
instant: true
intervalMs: 1000
legendFormat: __auto
maxDataPoints: 43200
range: false
refId: A
- refId: C
datasourceUid: __expr__
model:
conditions:
- evaluator:
params:
- 50
type: gt
operator:
type: and
query:
params:
- C
reducer:
params: []
type: last
type: query
datasource:
type: __expr__
uid: __expr__
expression: A
intervalMs: 1000
maxDataPoints: 43200
refId: C
type: threshold
noDataState: NoData
execErrState: Error
for: 15m
keepFiringFor: 5m
isPaused: false
notification_settings:
receiver: ntfy.sh
- uid: btrfs-errors
title: BTRFS Errors
condition: C
data:
- refId: A
relativeTimeRange:
from: 600
to: 0
datasourceUid: prometheus
model:
editorMode: code
expr: |-
sum by (btrfs_dev_uuid) (
increase(node_btrfs_device_errors_total[1h])
)
instant: true
intervalMs: 1000
legendFormat: __auto
maxDataPoints: 43200
range: false
refId: A
- refId: C
datasourceUid: __expr__
model:
conditions:
- evaluator:
params:
- 0
type: gt
operator:
type: and
query:
params:
- C
reducer:
params: []
type: last
type: query
datasource:
type: __expr__
uid: __expr__
expression: A
intervalMs: 1000
maxDataPoints: 43200
refId: C
type: threshold
noDataState: NoData
execErrState: Error
isPaused: false
notification_settings:
receiver: ntfy.sh
- uid: systemd-units
title: SystemD Units
condition: C
data:
- refId: A
relativeTimeRange:
from: 600
to: 0
datasourceUid: prometheus
model:
editorMode: code
expr: node_systemd_units{state="failed"}
instant: true
intervalMs: 1000
legendFormat: __auto
maxDataPoints: 43200
range: false
refId: A
- refId: C
datasourceUid: __expr__
model:
conditions:
- evaluator:
params:
- 0
type: gt
operator:
type: and
query:
params:
- C
reducer:
params: []
type: last
type: query
datasource:
type: __expr__
uid: __expr__
expression: A
intervalMs: 1000
maxDataPoints: 43200
refId: C
type: threshold
noDataState: NoData
execErrState: Error
keepFiringFor: 1h
isPaused: false
notification_settings:
receiver: ntfy.sh

View File

@@ -867,6 +867,6 @@
"timepicker": {},
"timezone": "browser",
"title": "Traefik",
"uid": "9789120c-ed69-448a-9ef8-77e73b5a25e2",
"version": 7
"uid": "traefik",
"version": 1
}

View File

@@ -18,6 +18,7 @@ in
"grafana/authelia/password".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"grafana/authelia/digest".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"grafana/smtp".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"ntfy/tokens/jupiter/grafana".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
};
templates = {
@@ -32,6 +33,7 @@ in
authorization_policy = "admin_one_factor";
require_pkce = true;
pkce_challenge_method = "S256";
pre_configured_consent_duration = "1 year";
}
];
}
@@ -113,6 +115,37 @@ in
};
}
);
grafana-to-ntfy-env.content = ''
BAUTH_PASS=${hmConfig.sops.placeholder."ntfy/tokens/jupiter/grafana"}
NTFY_BAUTH_PASS=${hmConfig.sops.placeholder."ntfy/tokens/jupiter/grafana"}
'';
grafana-contact-points.content = builtins.readFile (
(pkgs.formats.yaml { }).generate "contact-points.yaml" {
apiVersion = 1;
contactPoints = [
{
orgId = 1;
name = "ntfy.sh";
receivers = [
{
uid = "ntfy";
type = "webhook";
settings = {
httpMethod = "POST";
url = "http://grafana-to-ntfy:8080";
username = "jupiter";
password = hmConfig.sops.placeholder."ntfy/tokens/jupiter/grafana";
headers = { };
};
disableResolveMessage = false;
}
];
}
];
}
);
};
};
@@ -135,7 +168,7 @@ in
providers = [
{
name = "Default";
folder = "";
folder = "System";
type = "file";
url = "http://prometheus:9090";
options.path = "/var/lib/grafana/dashboards";
@@ -147,6 +180,9 @@ in
"${hmConfig.sops.templates.grafana.path}:/etc/grafana/grafana.ini:ro"
"${dashboards}:/etc/grafana/conf/provisioning/dashboards/default.yaml:ro"
"${./dashboards}:/var/lib/grafana/dashboards:ro"
"${./alerting/policies.yaml}:/etc/grafana/conf/provisioning/alerting/policies.yaml:ro"
"${./alerting/rules.yaml}:/etc/grafana/conf/provisioning/alerting/rules.yaml:ro"
"${hmConfig.sops.templates.grafana-contact-points.path}:/etc/grafana/conf/provisioning/alerting/contact-points.yaml:ro"
];
labels = [
"traefik.enable=true"
@@ -162,6 +198,16 @@ in
networks = [ networks.grafana.ref ];
};
grafana-to-ntfy.containerConfig = {
image = "docker-archive:${pkgs.dockerImages.grafana-to-ntfy}";
networks = [ networks.grafana.ref ];
environments = {
"NTFY_URL" = "https://ntfy.karaolidis.com/grafana";
"BAUTH_USER" = "jupiter";
};
environmentFiles = [ hmConfig.sops.templates.grafana-to-ntfy-env.path ];
};
authelia.containerConfig.volumes = [
"${hmConfig.sops.templates.authelia-grafana.path}:/etc/authelia/conf.d/grafana.yaml:ro"
];

View File

@@ -0,0 +1,215 @@
{ user, home }:
{
config,
inputs,
pkgs,
lib,
...
}:
let
hmConfig = config.home-manager.users.${user};
inherit (hmConfig.virtualisation.quadlet) volumes containers networks;
autheliaClientId = "kwrm5k1Bgwqd4BCXiWp0feL6adpthOn0GGgQ9iIVW7IH1UIj7bA2HVj9Jv42hUheoYoE8wWJpQi8woPomrSJIauTmsBMMFTTrI6r";
in
{
home-manager.users.${user} = {
sops = {
secrets = {
"immich/smtp".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"immich/postgresql".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"immich/admin".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"immich/authelia/password".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"immich/authelia/digest".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
};
templates = {
immich-postgresql-env.content = ''
POSTGRES_PASSWORD=${hmConfig.sops.placeholder."immich/postgresql"}
'';
immich-env.content = ''
DB_PASSWORD=${hmConfig.sops.placeholder."immich/postgresql"}
IMMICH_ADMIN_PASSWORD=${hmConfig.sops.placeholder."immich/admin"}
'';
immich.content = builtins.readFile (
(pkgs.formats.json { }).generate "config.json" {
ffmpeg = {
accel = "nvenc";
accelDecode = true;
};
oauth = {
enabled = true;
buttonText = "Login with Authelia";
clientId = autheliaClientId;
clientSecret = hmConfig.sops.placeholder."immich/authelia/password";
issuerUrl = "https://id.karaolidis.com/.well-known/openid-configuration";
scope = lib.strings.concatStringsSep " " [
"openid"
"profile"
"email"
];
};
passwordLogin.enabled = true;
newVersionCheck.enabled = false;
library.watch.enabled = true;
server.externalDomain = "https://photos.karaolidis.com";
notifications.smtp = {
enabled = true;
from = "jupiter@karaolidis.com";
transport = {
host = "smtp.protonmail.ch";
port = 587;
username = "jupiter@karaolidis.com";
password = hmConfig.sops.placeholder."immich/smtp";
};
};
}
);
authelia-immich.content = builtins.readFile (
(pkgs.formats.yaml { }).generate "immich.yaml" {
identity_providers.oidc = {
authorization_policies.immich = {
default_policy = "deny";
rules = [
{
policy = "one_factor";
subject = "group:immich";
}
];
};
clients = [
{
client_id = autheliaClientId;
client_name = "immich";
client_secret = hmConfig.sops.placeholder."immich/authelia/digest";
redirect_uris = [
"https://photos.karaolidis.com/auth/login"
"https://photos.karaolidis.com/user-settings"
"app.immich:///oauth-callback"
];
authorization_policy = "immich";
scopes = [
"openid"
"profile"
"email"
];
token_endpoint_auth_method = "client_secret_post";
pre_configured_consent_duration = "1 year";
}
];
};
}
);
};
};
systemd.user.tmpfiles.rules = [
"d /mnt/storage/private/storm/containers/storage/volumes/immich/_data 700 storm storm"
];
virtualisation.quadlet = {
networks.immich = { };
volumes = {
immich-redis = { };
immich-postgresql = { };
immich-machine-learning-cache = { };
};
containers = {
immich = {
containerConfig = {
image = "docker-archive:${pkgs.dockerImages.immich}";
volumes =
let
postStart = pkgs.writeTextFile {
name = "post-start.sh";
executable = true;
text = builtins.readFile ./post-start.sh;
};
in
[
"${hmConfig.sops.templates.immich.path}:/etc/immich/config.json:ro"
"${postStart}:/etc/immich/post-start.sh:ro"
"/mnt/storage/private/storm/containers/storage/volumes/immich/_data:/var/lib/immich"
];
networks = [
networks.immich.ref
networks.traefik.ref
];
labels = [
"traefik.enable=true"
"traefik.http.routers.immich.rule=Host(`photos.karaolidis.com`)"
];
environments = {
DB_HOSTNAME = "immich-postgresql";
DB_USERNAME = "immich";
DB_DATABASE_NAME = "immich";
REDIS_HOSTNAME = "immich-redis";
IMMICH_ADMIN_EMAIL = "jupiter@karaolidis.com";
IMMICH_ADMIN_NAME = "Admin";
};
environmentFiles = [ hmConfig.sops.templates.immich-env.path ];
podmanArgs = [ "--cdi-spec-dir=/run/cdi" ];
devices = [ "nvidia.com/gpu=all" ];
};
unitConfig = {
After = [
"${containers.immich-postgresql._serviceName}.service"
"${containers.immich-redis._serviceName}.service"
"sops-nix.service"
];
Requires = [
"${containers.immich-postgresql._serviceName}.service"
"${containers.immich-redis._serviceName}.service"
];
};
};
immich-machine-learning.containerConfig = {
image = "docker-archive:${pkgs.dockerImages.immich-machine-learning}";
volumes = [ "${volumes.immich-machine-learning-cache.ref}:/tmp/immich-machine-learning" ];
networks = [ networks.immich.ref ];
podmanArgs = [ "--cdi-spec-dir=/run/cdi" ];
devices = [ "nvidia.com/gpu=all" ];
};
immich-postgresql = {
containerConfig = {
image = "docker-archive:${pkgs.dockerImages.postgresql-vectorchord}";
networks = [ networks.immich.ref ];
volumes = [ "${volumes.immich-postgresql.ref}:/var/lib/postgresql/data" ];
environments = {
POSTGRES_DB = "immich";
POSTGRES_USER = "immich";
};
environmentFiles = [ hmConfig.sops.templates.immich-postgresql-env.path ];
};
unitConfig.After = [ "sops-nix.service" ];
};
immich-redis.containerConfig = {
image = "docker-archive:${pkgs.dockerImages.redis}";
networks = [ networks.immich.ref ];
volumes = [ "${volumes.immich-redis.ref}:/var/lib/redis" ];
exec = [ "--save 60 1" ];
};
authelia.containerConfig.volumes = [
"${hmConfig.sops.templates.authelia-immich.path}:/etc/authelia/conf.d/immich.yaml:ro"
];
};
};
};
}

View File

@@ -0,0 +1,22 @@
# shellcheck shell=sh
IMMICH_HOST="${IMMICH_HOST:-http://localhost:2283}"
IMMICH_ADMIN_NAME="${IMMICH_ADMIN_NAME:-Admin}"
until response="$(curl -sf "$IMMICH_HOST/api/server/config")"; do
echo "Waiting for Immich to be ready..."
sleep 1
done
is_initialized="$(echo "$response" | jq -r '.isInitialized')"
if [ "$is_initialized" = "false" ]; then
curl -sf "$IMMICH_HOST/api/auth/admin-sign-up" \
-X POST \
-H 'Content-Type: application/json' \
--data-raw '{
"email":"'"$IMMICH_ADMIN_EMAIL"'",
"password":"'"$IMMICH_ADMIN_PASSWORD"'",
"name":"'"$IMMICH_ADMIN_NAME"'"
}'
fi

View File

@@ -38,7 +38,7 @@ in
AVATAR_ALT = "Nick Karaolidis Profile Picture";
BUTTON_ORDER = lib.strings.concatStringsSep "," [
"GHOST"
"BLOG"
"EMAIL"
"LINKED_IN"
"GITEA"
@@ -50,7 +50,14 @@ in
"STEAM"
];
GHOST = "https://blog.karaolidis.com/";
CUSTOM_BUTTON_TEXT = "Blog";
CUSTOM_BUTTON_URL = "https://blog.karaolidis.com";
CUSTOM_BUTTON_COLOR = "#000000";
CUSTOM_BUTTON_TEXT_COLOR = "#ffffff";
CUSTOM_BUTTON_ALT_TEXT = "Blog";
CUSTOM_BUTTON_NAME = "BLOG";
CUSTOM_BUTTON_ICON = "fas fa-pen-nib";
EMAIL = "nick@karaolidis.com";
EMAIL_TEXT = "E-mail";
LINKED_IN = "https://www.linkedin.com/in/nikolaos-karaolidis";

View File

@@ -1,5 +1,10 @@
{ user, home }:
{ config, ... }:
{
config,
inputs,
pkgs,
...
}:
let
hmConfig = config.home-manager.users.${user};
@@ -17,7 +22,7 @@ let
in
{
imports = [
(import ./jellyfin { inherit user home; })
(import ./plex { inherit user home; })
(import ./jellyseerr {
inherit
user
@@ -56,6 +61,34 @@ in
"d /mnt/storage/private/storm/containers/storage/volumes/media/_data/libraries/anime/shows 755 storm storm"
];
virtualisation.quadlet.networks.media = { };
sops.secrets."ntfy/tokens/jupiter/media".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
virtualisation.quadlet = {
networks.media = { };
containers.authelia.containerConfig.volumes =
let
mediaConfig = (pkgs.formats.yaml { }).generate "media.yaml" {
access_control.rules = [
{
domain = "beta.media.karaolidis.com";
policy = "one_factor";
resources = [ "^/manage([/?].*)?$" ];
subject = [ "group:media" ];
}
{
domain = "beta.media.karaolidis.com";
policy = "deny";
resources = [ "^/manage([/?].*)?$" ];
}
{
domain = "beta.media.karaolidis.com";
policy = "bypass";
}
];
};
in
[ "${mediaConfig}:/etc/authelia/conf.d/media.yaml:ro" ];
};
};
}

View File

@@ -1,150 +0,0 @@
{ user, home }:
{
config,
inputs,
pkgs,
...
}:
let
hmConfig = config.home-manager.users.${user};
inherit (hmConfig.virtualisation.quadlet) volumes networks;
jellyfinAutheliaClientId = "59TRpNutxEeRRCAZbDsK7rsnrA5NC69HAdAO45CEfc740xl4hgIacDy2u03oiFc89Exb67udBQvmfwxgeAQtJPiNAJxA5OzGmdQf";
in
{
home-manager.users.${user} = {
sops = {
secrets = {
"jellyfin/admin".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"jellyfin/authelia/password".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"jellyfin/authelia/digest".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"opensubtitles/username".sopsFile = "${inputs.secrets}/domains/personal/secrets.yaml";
"opensubtitles/password".sopsFile = "${inputs.secrets}/domains/personal/secrets.yaml";
};
templates = {
jellyfin-env.content = ''
JELLYFIN_ADMIN_PASSWORD=${hmConfig.sops.placeholder."jellyfin/admin"}
JELLYFIN_OIDC_SECRET=${hmConfig.sops.placeholder."jellyfin/authelia/password"}
OPENSUBTITLES_USERNAME=${hmConfig.sops.placeholder."opensubtitles/username"}
OPENSUBTITLES_PASSWORD=${hmConfig.sops.placeholder."opensubtitles/password"}
'';
authelia-jellyfin.content = builtins.readFile (
(pkgs.formats.yaml { }).generate "jellyfin.yaml" {
identity_providers.oidc = {
authorization_policies.jellyfin = {
default_policy = "deny";
rules = [
{
policy = "one_factor";
subject = "group:jellyfin";
}
];
};
clients = [
{
client_id = jellyfinAutheliaClientId;
client_name = "Jellyfin";
client_secret = hmConfig.sops.placeholder."jellyfin/authelia/digest";
redirect_uris = [ "https://media.karaolidis.com/sso/OID/redirect/authelia" ];
authorization_policy = "jellyfin";
require_pkce = true;
pkce_challenge_method = "S256";
scopes = [
"openid"
"profile"
"groups"
];
token_endpoint_auth_method = "client_secret_post";
}
];
};
}
);
};
};
virtualisation.quadlet = {
networks.jellyfin = { };
volumes = {
jellyfin-config = { };
jellyfin-data = { };
jellyfin-metadata = { };
jellyfin-root = { };
jellyfin-log = { };
jellyfin-cache = { };
};
containers = {
jellyfin = {
containerConfig = {
image = "docker-archive:${pkgs.dockerImages.jellyfin}";
networks = [
networks.jellyfin.ref
networks.traefik.ref
];
volumes =
let
setup = pkgs.writeTextFile {
name = "setup.sh";
executable = true;
text = builtins.readFile ./setup.sh;
};
in
[
"/mnt/storage/private/storm/containers/storage/volumes/media/_data:/var/lib/media"
"${setup}:/etc/jellyfin/setup.sh:ro"
"${./libraries}:/etc/jellyfin/libraries:ro"
"${volumes.jellyfin-config.ref}:/etc/jellyfin"
"${volumes.jellyfin-data.ref}:/var/lib/jellyfin/data"
"${volumes.jellyfin-metadata.ref}:/var/lib/jellyfin/metadata"
"${volumes.jellyfin-root.ref}:/var/lib/jellyfin/root"
"${volumes.jellyfin-log.ref}:/var/log/jellyfin"
"${volumes.jellyfin-cache.ref}:/tmp/jellyfin"
];
environments.JELLYFIN_OIDC_CLIENT_ID = jellyfinAutheliaClientId;
environmentFiles = [ hmConfig.sops.templates.jellyfin-env.path ];
labels = [
"traefik.enable=true"
"traefik.http.routers.jellyfin.rule=Host(`media.karaolidis.com`)"
];
podmanArgs = [ "--cdi-spec-dir=/run/cdi" ];
devices = [ "nvidia.com/gpu=all" ];
};
unitConfig.After = [ "sops-nix.service" ];
};
authelia.containerConfig.volumes =
let
mediaConfig = (pkgs.formats.yaml { }).generate "media.yaml" {
access_control.rules = [
{
domain = "media.karaolidis.com";
policy = "one_factor";
resources = [ "^/manage([/?].*)?$" ];
subject = [ "group:media" ];
}
{
domain = "media.karaolidis.com";
policy = "deny";
resources = [ "^/manage([/?].*)?$" ];
}
{
domain = "media.karaolidis.com";
policy = "bypass";
}
];
};
in
[
"${mediaConfig}:/etc/authelia/conf.d/media.yaml:ro"
"${hmConfig.sops.templates.authelia-jellyfin.path}:/etc/authelia/conf.d/jellyfin.yaml:ro"
];
};
};
};
}

View File

@@ -1,128 +0,0 @@
{
"LibraryOptions": {
"Enabled": true,
"EnableArchiveMediaFiles": false,
"EnablePhotos": true,
"EnableRealtimeMonitor": true,
"EnableLUFSScan": true,
"ExtractTrickplayImagesDuringLibraryScan": false,
"SaveTrickplayWithMedia": true,
"EnableTrickplayImageExtraction": true,
"ExtractChapterImagesDuringLibraryScan": false,
"EnableChapterImageExtraction": true,
"EnableInternetProviders": true,
"SaveLocalMetadata": true,
"EnableAutomaticSeriesGrouping": false,
"PreferredMetadataLanguage": "en",
"MetadataCountryCode": "JP",
"SeasonZeroDisplayName": "Specials",
"AutomaticRefreshIntervalDays": 30,
"EnableEmbeddedTitles": false,
"EnableEmbeddedExtrasTitles": false,
"EnableEmbeddedEpisodeInfos": false,
"AllowEmbeddedSubtitles": "AllowAll",
"SkipSubtitlesIfEmbeddedSubtitlesPresent": false,
"SkipSubtitlesIfAudioTrackMatches": false,
"SaveSubtitlesWithMedia": true,
"SaveLyricsWithMedia": false,
"RequirePerfectSubtitleMatch": true,
"AutomaticallyAddToCollection": true,
"PreferNonstandardArtistsTag": false,
"UseCustomTagDelimiters": false,
"MetadataSavers": ["Nfo"],
"TypeOptions": [
{
"Type": "Movie",
"MetadataFetchers": [
"TheMovieDb",
"The Open Movie Database",
"TheTVDB"
],
"MetadataFetcherOrder": [
"TheMovieDb",
"The Open Movie Database",
"TheTVDB"
],
"ImageFetchers": [
"TheMovieDb",
"The Open Movie Database",
"TheTVDB",
"Embedded Image Extractor",
"Screen Grabber"
],
"ImageFetcherOrder": [
"TheMovieDb",
"The Open Movie Database",
"TheTVDB",
"Embedded Image Extractor",
"Screen Grabber"
],
"ImageOptions": [
{
"Type": "Primary",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Art",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "BoxRear",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Banner",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Box",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Disc",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Logo",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Menu",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Thumb",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Backdrop",
"Limit": "1",
"MinWidth": "1280"
}
]
}
],
"LocalMetadataReaderOrder": ["Nfo"],
"SubtitleDownloadLanguages": [],
"CustomTagDelimiters": ["/", "|", ";", "\\"],
"DelimiterWhitelist": [],
"DisabledSubtitleFetchers": [],
"SubtitleFetcherOrder": [],
"DisabledLyricFetchers": [],
"LyricFetcherOrder": [],
"PathInfos": [
{
"Path": "/var/lib/media/libraries/anime/films"
}
]
}
}

View File

@@ -1,128 +0,0 @@
{
"LibraryOptions": {
"Enabled": true,
"EnableArchiveMediaFiles": false,
"EnablePhotos": true,
"EnableRealtimeMonitor": true,
"EnableLUFSScan": true,
"ExtractTrickplayImagesDuringLibraryScan": false,
"SaveTrickplayWithMedia": true,
"EnableTrickplayImageExtraction": true,
"ExtractChapterImagesDuringLibraryScan": false,
"EnableChapterImageExtraction": true,
"EnableInternetProviders": true,
"SaveLocalMetadata": true,
"EnableAutomaticSeriesGrouping": false,
"PreferredMetadataLanguage": "en",
"MetadataCountryCode": "US",
"SeasonZeroDisplayName": "Specials",
"AutomaticRefreshIntervalDays": 30,
"EnableEmbeddedTitles": false,
"EnableEmbeddedExtrasTitles": false,
"EnableEmbeddedEpisodeInfos": false,
"AllowEmbeddedSubtitles": "AllowAll",
"SkipSubtitlesIfEmbeddedSubtitlesPresent": false,
"SkipSubtitlesIfAudioTrackMatches": false,
"SaveSubtitlesWithMedia": true,
"SaveLyricsWithMedia": false,
"RequirePerfectSubtitleMatch": true,
"AutomaticallyAddToCollection": true,
"PreferNonstandardArtistsTag": false,
"UseCustomTagDelimiters": false,
"MetadataSavers": ["Nfo"],
"TypeOptions": [
{
"Type": "Movie",
"MetadataFetchers": [
"TheMovieDb",
"The Open Movie Database",
"TheTVDB"
],
"MetadataFetcherOrder": [
"TheMovieDb",
"The Open Movie Database",
"TheTVDB"
],
"ImageFetchers": [
"TheMovieDb",
"The Open Movie Database",
"TheTVDB",
"Embedded Image Extractor",
"Screen Grabber"
],
"ImageFetcherOrder": [
"TheMovieDb",
"The Open Movie Database",
"TheTVDB",
"Embedded Image Extractor",
"Screen Grabber"
],
"ImageOptions": [
{
"Type": "Primary",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Art",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "BoxRear",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Banner",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Box",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Disc",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Logo",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Menu",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Thumb",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Backdrop",
"Limit": "1",
"MinWidth": "1280"
}
]
}
],
"LocalMetadataReaderOrder": ["Nfo"],
"SubtitleDownloadLanguages": [],
"CustomTagDelimiters": ["/", "|", ";", "\\"],
"DelimiterWhitelist": [],
"DisabledSubtitleFetchers": [],
"SubtitleFetcherOrder": [],
"DisabledLyricFetchers": [],
"LyricFetcherOrder": [],
"PathInfos": [
{
"Path": "/var/lib/media/libraries/films"
}
]
}
}

View File

@@ -1,204 +0,0 @@
{
"LibraryOptions": {
"Enabled": true,
"EnableArchiveMediaFiles": false,
"EnablePhotos": true,
"EnableRealtimeMonitor": true,
"EnableLUFSScan": true,
"ExtractTrickplayImagesDuringLibraryScan": false,
"SaveTrickplayWithMedia": true,
"EnableTrickplayImageExtraction": true,
"ExtractChapterImagesDuringLibraryScan": false,
"EnableChapterImageExtraction": true,
"EnableInternetProviders": true,
"SaveLocalMetadata": true,
"EnableAutomaticSeriesGrouping": true,
"PreferredMetadataLanguage": "en",
"MetadataCountryCode": "JP",
"SeasonZeroDisplayName": "Specials",
"AutomaticRefreshIntervalDays": 30,
"EnableEmbeddedTitles": false,
"EnableEmbeddedExtrasTitles": false,
"EnableEmbeddedEpisodeInfos": false,
"AllowEmbeddedSubtitles": "AllowAll",
"SkipSubtitlesIfEmbeddedSubtitlesPresent": false,
"SkipSubtitlesIfAudioTrackMatches": false,
"SaveSubtitlesWithMedia": true,
"SaveLyricsWithMedia": false,
"RequirePerfectSubtitleMatch": true,
"AutomaticallyAddToCollection": false,
"PreferNonstandardArtistsTag": false,
"UseCustomTagDelimiters": false,
"MetadataSavers": ["Nfo"],
"TypeOptions": [
{
"Type": "Series",
"MetadataFetchers": [
"TheTVDB",
"TheMovieDb",
"The Open Movie Database",
"Missing Episode Fetcher"
],
"MetadataFetcherOrder": [
"TheTVDB",
"TheMovieDb",
"The Open Movie Database",
"Missing Episode Fetcher"
],
"ImageFetchers": ["TheTVDB", "TheMovieDb"],
"ImageFetcherOrder": ["TheTVDB", "TheMovieDb"],
"ImageOptions": [
{
"Type": "Primary",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Art",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "BoxRear",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Banner",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Box",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Disc",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Logo",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Menu",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Thumb",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Backdrop",
"Limit": "1",
"MinWidth": "1280"
}
]
},
{
"Type": "Season",
"MetadataFetchers": ["TheTVDB", "TheMovieDb"],
"MetadataFetcherOrder": ["TheTVDB", "TheMovieDb"],
"ImageFetchers": ["TheTVDB", "TheMovieDb"],
"ImageFetcherOrder": ["TheTVDB", "TheMovieDb"],
"ImageOptions": [
{
"Type": "Primary",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Art",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "BoxRear",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Banner",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Box",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Disc",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Logo",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Menu",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Thumb",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Backdrop",
"Limit": "0",
"MinWidth": "1280"
}
]
},
{
"Type": "Episode",
"MetadataFetchers": [
"TheTVDB",
"TheMovieDb",
"The Open Movie Database"
],
"MetadataFetcherOrder": [
"TheTVDB",
"TheMovieDb",
"The Open Movie Database"
],
"ImageFetchers": [
"TheTVDB",
"TheMovieDb",
"The Open Movie Database",
"Embedded Image Extractor",
"Screen Grabber"
],
"ImageFetcherOrder": [
"TheTVDB",
"TheMovieDb",
"The Open Movie Database",
"Embedded Image Extractor",
"Screen Grabber"
]
}
],
"LocalMetadataReaderOrder": ["Nfo"],
"SubtitleDownloadLanguages": [],
"CustomTagDelimiters": ["/", "|", ";", "\\"],
"DelimiterWhitelist": [],
"DisabledSubtitleFetchers": [],
"SubtitleFetcherOrder": [],
"DisabledLyricFetchers": [],
"LyricFetcherOrder": [],
"PathInfos": [
{
"Path": "/var/lib/media/libraries/anime/shows"
}
]
}
}

View File

@@ -1,204 +0,0 @@
{
"LibraryOptions": {
"Enabled": true,
"EnableArchiveMediaFiles": false,
"EnablePhotos": true,
"EnableRealtimeMonitor": true,
"EnableLUFSScan": true,
"ExtractTrickplayImagesDuringLibraryScan": false,
"SaveTrickplayWithMedia": true,
"EnableTrickplayImageExtraction": true,
"ExtractChapterImagesDuringLibraryScan": false,
"EnableChapterImageExtraction": true,
"EnableInternetProviders": true,
"SaveLocalMetadata": true,
"EnableAutomaticSeriesGrouping": true,
"PreferredMetadataLanguage": "en",
"MetadataCountryCode": "US",
"SeasonZeroDisplayName": "Specials",
"AutomaticRefreshIntervalDays": 30,
"EnableEmbeddedTitles": false,
"EnableEmbeddedExtrasTitles": false,
"EnableEmbeddedEpisodeInfos": false,
"AllowEmbeddedSubtitles": "AllowAll",
"SkipSubtitlesIfEmbeddedSubtitlesPresent": false,
"SkipSubtitlesIfAudioTrackMatches": false,
"SaveSubtitlesWithMedia": true,
"SaveLyricsWithMedia": false,
"RequirePerfectSubtitleMatch": true,
"AutomaticallyAddToCollection": false,
"PreferNonstandardArtistsTag": false,
"UseCustomTagDelimiters": false,
"MetadataSavers": ["Nfo"],
"TypeOptions": [
{
"Type": "Series",
"MetadataFetchers": [
"TheTVDB",
"TheMovieDb",
"The Open Movie Database",
"Missing Episode Fetcher"
],
"MetadataFetcherOrder": [
"TheTVDB",
"TheMovieDb",
"The Open Movie Database",
"Missing Episode Fetcher"
],
"ImageFetchers": ["TheTVDB", "TheMovieDb"],
"ImageFetcherOrder": ["TheTVDB", "TheMovieDb"],
"ImageOptions": [
{
"Type": "Primary",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Art",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "BoxRear",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Banner",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Box",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Disc",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Logo",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Menu",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Thumb",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Backdrop",
"Limit": "1",
"MinWidth": "1280"
}
]
},
{
"Type": "Season",
"MetadataFetchers": ["TheTVDB", "TheMovieDb"],
"MetadataFetcherOrder": ["TheTVDB", "TheMovieDb"],
"ImageFetchers": ["TheTVDB", "TheMovieDb"],
"ImageFetcherOrder": ["TheTVDB", "TheMovieDb"],
"ImageOptions": [
{
"Type": "Primary",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Art",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "BoxRear",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Banner",
"Limit": 1,
"MinWidth": 0
},
{
"Type": "Box",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Disc",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Logo",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Menu",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Thumb",
"Limit": 0,
"MinWidth": 0
},
{
"Type": "Backdrop",
"Limit": "0",
"MinWidth": "1280"
}
]
},
{
"Type": "Episode",
"MetadataFetchers": [
"TheTVDB",
"TheMovieDb",
"The Open Movie Database"
],
"MetadataFetcherOrder": [
"TheTVDB",
"TheMovieDb",
"The Open Movie Database"
],
"ImageFetchers": [
"TheTVDB",
"TheMovieDb",
"The Open Movie Database",
"Embedded Image Extractor",
"Screen Grabber"
],
"ImageFetcherOrder": [
"TheTVDB",
"TheMovieDb",
"The Open Movie Database",
"Embedded Image Extractor",
"Screen Grabber"
]
}
],
"LocalMetadataReaderOrder": ["Nfo"],
"SubtitleDownloadLanguages": [],
"CustomTagDelimiters": ["/", "|", ";", "\\"],
"DelimiterWhitelist": [],
"DisabledSubtitleFetchers": [],
"SubtitleFetcherOrder": [],
"DisabledLyricFetchers": [],
"LyricFetcherOrder": [],
"PathInfos": [
{
"Path": "/var/lib/media/libraries/shows"
}
]
}
}

View File

@@ -1,214 +0,0 @@
# shellcheck shell=sh
JELLYFIN_HOST="${JELLYFIN_HOST:-http://localhost:8096}"
JELLYFIN_ADMIN_USERNAME="${JELLYFIN_ADMIN_USERNAME:-admin}"
until response="$(curl -sf "$JELLYFIN_HOST/System/Info/Public")"; do
echo "Waiting for Jellyfin to be ready..."
sleep 1
done
setup="$(echo "$response" | jq -r '.StartupWizardCompleted')"
if [ "$setup" = "false" ]; then
curl -sf "$JELLYFIN_HOST/Startup/Configuration" \
-X POST \
-H 'Content-Type: application/json' \
--data-raw '{"UICulture":"en-US","MetadataCountryCode":"US","PreferredMetadataLanguage":"en"}'
curl -sf "$JELLYFIN_HOST/Startup/User"
curl -sf "$JELLYFIN_HOST/Startup/User" \
-X POST \
-H 'Content-Type: application/json' \
--data-raw '{"Name":"'"$JELLYFIN_ADMIN_USERNAME"'","Password":"'"$JELLYFIN_ADMIN_PASSWORD"'"}'
curl -sf "$JELLYFIN_HOST/Startup/RemoteAccess" \
-X POST \
-H 'Content-Type: application/json' \
--data-raw '{"EnableRemoteAccess":true,"EnableAutomaticPortMapping":false}'
curl -sf "$JELLYFIN_HOST/Startup/Complete" \
-X POST
fi
token="$(curl -sf "$JELLYFIN_HOST/Users/AuthenticateByName" \
-X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: MediaBrowser Client="jellyfin-init", Device="sh", DeviceId="sh", Version="1.0"' \
--data-raw '{"Username":"'"$JELLYFIN_ADMIN_USERNAME"'","Pw":"'"$JELLYFIN_ADMIN_PASSWORD"'"}' \
| jq -r '.AccessToken')"
curl -sf "$JELLYFIN_HOST/System/Configuration" \
-H 'Authorization: MediaBrowser Token="'"$token"'"' \
| jq '.EnableMetrics = true
| .ServerName = "jupiter"
| .RemoteClientBitrateLimit = 1024000000
| .TrickplayOptions.EnableHwAcceleration = true
| .TrickplayOptions.EnableHwEncoding = true' \
| curl -sf "$JELLYFIN_HOST/System/Configuration" \
-X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: MediaBrowser Token="'"$token"'"' \
--data-binary @-
curl -sf "$JELLYFIN_HOST/System/Configuration/encoding" \
-H 'Authorization: MediaBrowser Token="'"$token"'"' \
| jq '.EnableThrottling = true
| .HardwareAccelerationType = "nvenc"
| .EnableTonemapping = true
| .EnableDecodingColorDepth12HevcRext = true
| .AllowHevcEncoding = true
| .HardwareDecodingCodecs = ["h264", "hevc", "mpeg2video", "mpeg4", "vc1", "vp8", "vp9", "av1"]' \
| curl -sf "$JELLYFIN_HOST/System/Configuration/encoding" \
-X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: MediaBrowser Token="'"$token"'"' \
--data-binary @-
curl -sf "$JELLYFIN_HOST/Plugins/c83d86bb-a1e0-4c35-a113-e2101cf4ee6b/Configuration" \
-H 'Authorization: MediaBrowser Token="'"$token"'"' \
| jq '.AnalyzeSeasonZero = true
| .AnalyzeMovies = true' \
| curl -sf "$JELLYFIN_HOST/Plugins/c83d86bb-a1e0-4c35-a113-e2101cf4ee6b/Configuration" \
-X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: MediaBrowser Token="'"$token"'"' \
--data-binary @-
curl -sf "$JELLYFIN_HOST/Plugins/4b9ed42f-5185-48b5-9803-6ff2989014c4/Configuration" \
-H 'Authorization: MediaBrowser Token="'"$token"'"' \
| jq --arg username "$OPENSUBTITLES_USERNAME" \
--arg password "$OPENSUBTITLES_PASSWORD" \
'.Username = $username
| .Password = $password' \
| curl -sf "$JELLYFIN_HOST/Plugins/4b9ed42f-5185-48b5-9803-6ff2989014c4/Configuration" \
-X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: MediaBrowser Token="'"$token"'"' \
--data-binary @-
curl -sf "$JELLYFIN_HOST/Plugins/b8715ed1-6c47-4528-9ad3-f72deb539cd4/Configuration" \
-H 'Authorization: MediaBrowser Token="'"$token"'"' \
| jq '.IncludeAdult = true' \
| curl -sf "$JELLYFIN_HOST/Plugins/b8715ed1-6c47-4528-9ad3-f72deb539cd4/Configuration" \
-X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: MediaBrowser Token="'"$token"'"' \
--data-binary @-
curl -sf "$JELLYFIN_HOST/Plugins/505ce9d1-d916-42fa-86ca-673ef241d7df/Configuration" \
-X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: MediaBrowser Token="'"$token"'"' \
--data-binary @- <<EOF
{
"SamlConfigs": {},
"OidConfigs": {
"authelia": {
"OidProviderName": "authelia",
"OidEndpoint": "https://id.karaolidis.com",
"OidClientId": "$JELLYFIN_OIDC_CLIENT_ID",
"OidSecret": "$JELLYFIN_OIDC_SECRET",
"RoleClaim": "groups",
"DefaultUsernameClaim": "preferred_username",
"Enabled": true,
"EnableAuthorization": true,
"EnableAllFolders": true,
"EnableFolderRoles": false,
"EnableLiveTvRoles": false,
"EnableLiveTv": false,
"EnableLiveTvManagement": false,
"DisableHttps": false,
"DoNotValidateEndpoints": false,
"DoNotValidateIssuerName": false,
"Roles": [
"jellyfin"
],
"AdminRoles": [
"admin"
],
"LiveTvRoles": [],
"LiveTvManagementRoles": [],
"OidScopes": [
"groups"
],
"EnabledFolders": [],
"FolderRoleMapping": [],
"SchemeOverride": "https"
}
}
}
EOF
# https://github.com/9p4/jellyfin-plugin-sso/issues/16#issuecomment-2953811762
custom_css=$(cat <<EOF
a.raised.emby-button,
.loginDisclaimerContainer,
.loginDisclaimer,
.manualLoginForm {
all: unset;
}
.btnQuick,
.btnSelectServer,
.btnForgotPassword,
a.raised.emby-button,
.emby-button.block,
.loginDisclaimerContainer,
.loginDisclaimer {
margin-left: auto;
margin-right: auto;
margin-bottom: 1em;
color: inherit !important;
}
.btnForgotPassword {
display: none !important;
}
.manualLoginForm > :not(:first-child) {
display: none !important;
}
EOF
)
login_disclaimer=$(cat <<EOF
<form action="https://media.karaolidis.com/sso/OID/start/authelia">
<button class="raised block emby-button button-submit">
Sign in with Authelia
</button>
</form>
EOF
)
curl -sf "$JELLYFIN_HOST/System/Configuration/branding" \
-H "Authorization: MediaBrowser Token=$token" |
jq --arg custom_css "$custom_css" \
--arg login_disclaimer "$login_disclaimer" \
'.CustomCss = $custom_css | .LoginDisclaimer = $login_disclaimer' |
curl -sf "$JELLYFIN_HOST/System/Configuration/branding" \
-X POST \
-H 'Content-Type: application/json' \
-H "Authorization: MediaBrowser Token=$token" \
--data-binary @-
existing_libraries="$(curl -sf "$JELLYFIN_HOST/Library/VirtualFolders" \
-H 'Authorization: MediaBrowser Token="'"$token"'"')"
find /etc/jellyfin/libraries -name "*.json" | sort -V | while IFS= read -r filepath; do
collectionType=$(jq -rn --arg s "$(basename "$(dirname "$filepath")")" '$s|@uri')
name=$(jq -rn --arg s "$(basename "$filepath" .json)" '$s|@uri')
if echo "$existing_libraries" | jq -e --arg name "$name" 'any(.[]; .Name | @uri == $name)'; then
echo "Skipping existing virtual folder: $name"
continue
fi
echo "Creating virtual folder: $name"
curl -sf "$JELLYFIN_HOST/Library/VirtualFolders?collectionType=$collectionType&name=$name" \
-X POST \
-H "Content-Type: application/json" \
-H 'Authorization: MediaBrowser Token="'"$token"'"' \
--data-binary @"$filepath"
done

View File

@@ -14,24 +14,14 @@
let
hmConfig = config.home-manager.users.${user};
inherit (hmConfig.virtualisation.quadlet) containers volumes networks;
arrs = radarrs ++ sonarrs;
jellyseerrAutheliaClientId = "s8QyVqBdiEStH5WXeEYNSrEh8ls2xHif0qyTGbC7V8nHNcqHi5NhqHUapCHuVFT4kEtngqgLry2SKOKepQl3AiqCWlhTjlIxr7LI";
in
{
home-manager.users.${user} = {
sops = {
secrets = {
"jellyseerr/smtp".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"jellyseerr/authelia/password".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"jellyseerr/authelia/digest".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
};
secrets."jellyseerr/smtp".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
templates = {
jellyseerr-env.content = ''
JELLYFIN_ADMIN_PASSWORD=${hmConfig.sops.placeholder."jellyfin/admin"}
'';
jellyseerr.content = builtins.readFile (
(pkgs.formats.json { }).generate "setings.json" {
main = {
@@ -43,38 +33,51 @@ in
# 32 | 4194304 | 67108864
defaultPermissions = 71303200;
localLogin = false;
mediaServerLogin = false;
oidcLogin = true;
newPlexLogin = false;
mediaServerType = 2;
mediaServerLogin = true;
oidcLogin = false;
newPlexLogin = true;
mediaServerType = 1;
partialRequestsEnabled = true;
enableSpecialEpisodes = true;
};
jellyfin = {
plex = {
name = "jupiter";
ip = "jellyfin";
port = 8096;
externalHostname = "https://media.karaolidis.com";
jellyfinForgotPasswordUrl = "https://id.karaolidis.com/reset-password/step1";
};
oidc.providers = [
ip = "beta.media.karaolidis.com";
port = 443;
useSsl = true;
libraries = [
{
slug = "authelia";
name = "Authelia";
issuerUrl = "https://id.karaolidis.com";
clientId = jellyseerrAutheliaClientId;
clientSecret = hmConfig.sops.placeholder."jellyseerr/authelia/password";
scopes = lib.strings.concatStringsSep " " [
"openid"
"profile"
"email"
"groups"
];
newUserLogin = true;
id = "1";
name = "Films";
enabled = true;
type = "movie";
}
{
id = "2";
name = "Shows";
enabled = true;
type = "show";
}
{
id = "3";
name = "Films (Anime)";
enabled = true;
type = "movie";
}
{
id = "4";
name = "Shows (Anime)";
enabled = true;
type = "show";
}
];
externalHostname = "https://beta.media.karaolidis.com";
webAppUrl = "https://beta.media.karaolidis.com";
machineId = hmConfig.sops.placeholder."plex/processedMachineIdentifier";
};
jellyfin = { };
radarr = [ ];
@@ -82,7 +85,8 @@ in
public.initialized = true;
notifications.agents.email = {
notifications.agents = {
email = {
enabled = true;
options = {
emailFrom = "jupiter@karaolidis.com";
@@ -94,36 +98,23 @@ in
};
};
ntfy = {
enabled = true;
embedPoster = true;
types = 2970;
options = {
url = "https://ntfy.karaolidis.com";
topic = "media";
authMethodUsernamePassword = false;
authMethodToken = true;
token = hmConfig.sops.placeholder."ntfy/tokens/jupiter/media";
};
};
};
network.trustProxy = true;
}
);
authelia-jellyseerr.content = builtins.readFile (
(pkgs.formats.yaml { }).generate "jellyseerr.yaml" {
identity_providers.oidc = {
authorization_policies.jellyseerr = {
default_policy = "deny";
rules = [
{
policy = "one_factor";
subject = "group:jellyfin";
}
];
};
clients = [
{
client_id = jellyseerrAutheliaClientId;
client_name = "jellyseerr";
client_secret = hmConfig.sops.placeholder."jellyseerr/authelia/digest";
redirect_uris = [ "https://request.karaolidis.com/login?provider=authelia&callback=true" ];
authorization_policy = "jellyseerr";
token_endpoint_auth_method = "client_secret_post";
}
];
};
}
);
}
// builtins.listToAttrs (
builtins.map (arr: {
@@ -138,12 +129,10 @@ in
virtualisation.quadlet = {
volumes.jellyseerr = { };
containers = {
jellyseerr = {
containers.jellyseerr = {
containerConfig = {
image = "docker-archive:${pkgs.dockerImages.jellyseerr}";
networks = [
networks.jellyfin.ref
networks.media.ref
networks.traefik.ref
];
@@ -172,7 +161,6 @@ in
hmConfig.sops.templates."jellyseerr-${sonarr.hostName}".path
}:/etc/jellyseerr/apps/sonarr/${sonarr.hostName}.json:ro"
) sonarrs;
environmentFiles = [ hmConfig.sops.templates.jellyseerr-env.path ];
labels = [
"traefik.enable=true"
"traefik.http.routers.jellyseerr.rule=Host(`request.karaolidis.com`)"
@@ -185,11 +173,6 @@ in
in
[ "sops-nix.service" ] ++ arrServices;
};
authelia.containerConfig.volumes = [
"${hmConfig.sops.templates.authelia-jellyseerr.path}:/etc/authelia/conf.d/jellyseerr.yaml:ro"
];
};
};
};
}

View File

@@ -1,59 +1,5 @@
# shellcheck shell=sh
JELLYFIN_HOST="${JELLYFIN_HOST:-http://jellyfin:8096}"
JELLYFIN_ADMIN_USERNAME="${JELLYFIN_ADMIN_USERNAME:-admin}"
until public="$(curl -sf "$JELLYFIN_HOST/System/Info/Public")"; do
echo "Waiting for Jellyfin to be ready..."
sleep 1
done
until [ "$(echo "$public" | jq -r '.StartupWizardCompleted')" = "true" ]; do
echo "Waiting for Jellyfin setup wizard to finish..."
sleep 1
public="$(curl -sf "${JELLYFIN_HOST}/System/Info/Public")"
done
token="$(curl -sf "$JELLYFIN_HOST/Users/AuthenticateByName" \
-X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: MediaBrowser Client="jellyseerr-init", Device="sh", DeviceId="sh", Version="1.0"' \
--data-raw '{"Username":"'"$JELLYFIN_ADMIN_USERNAME"'","Pw":"'"$JELLYFIN_ADMIN_PASSWORD"'"}' \
| jq -r '.AccessToken')"
keys="$(curl -sf "$JELLYFIN_HOST/Auth/Keys" \
-H 'Authorization: MediaBrowser Token="'"$token"'"')"
jellyseerr_key="$(echo "$keys" | jq -r '.Items[] | select(.AppName=="Jellyseerr") | .AccessToken')"
if [ -z "$jellyseerr_key" ] || [ "$jellyseerr_key" = "null" ]; then
curl -sf "$JELLYFIN_HOST/Auth/Keys?App=Jellyseerr" \
-X POST \
-H 'Authorization: MediaBrowser Token="'"$token"'"'
keys="$(curl -sf "$JELLYFIN_HOST/Auth/Keys" \
-H 'Authorization: MediaBrowser Token="'"$token"'"')"
jellyseerr_key="$(echo "$keys" | jq -r '.Items[] | select(.AppName=="Jellyseerr") | .AccessToken')"
fi
serverId="$(echo "$public" | jq -r '.Id')"
libraries="$(curl -sf "$JELLYFIN_HOST/Library/VirtualFolders" \
-H 'Authorization: MediaBrowser Token="'"$token"'"')"
libraries="$(
echo "$libraries" | jq '[
.[]
| {
id: .ItemId,
name: .Name,
enabled: .LibraryOptions.Enabled,
type: ( .CollectionType | if . == "movies" then "movie" elif . == "tvshows" then "show" else . end )
}
]'
)"
try_forever() {
until "$@" 2>&1; do
echo "Try failed: $* - retrying in 1s"
@@ -129,15 +75,9 @@ done
tmpfile=$(mktemp)
jq -s \
--argjson libs "$libraries" \
--argjson radarr "$radarr_json" \
--argjson sonarr "$sonarr_json" \
--arg serverId "$serverId" \
--arg apiKey "$jellyseerr_key" \
'.[0] * .[1]
| .jellyfin.serverId = $serverId
| .jellyfin.apiKey = $apiKey
| .jellyfin.libraries = $libs
| .radarr = $radarr
| .sonarr = $sonarr' \
/var/lib/jellyseerr/settings.json \

View File

@@ -0,0 +1,109 @@
{ user, home }:
{
config,
inputs,
pkgs,
...
}:
let
hmConfig = config.home-manager.users.${user};
inherit (hmConfig.virtualisation.quadlet) volumes networks;
in
{
networking.firewall.allowedTCPPorts = [ 32400 ];
home-manager.users.${user} = {
sops = {
secrets = {
"plex/machineIdentifier".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"plex/processedMachineIdentifier".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"plex/anonymousMachineIdentifier".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"plex/certificateUuid".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"plex/plexOnlineToken".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
};
templates.plex.content = ''
<?xml version="1.0" encoding="utf-8"?>
<Preferences
MachineIdentifier="${hmConfig.sops.placeholder."plex/machineIdentifier"}"
ProcessedMachineIdentifier="${hmConfig.sops.placeholder."plex/processedMachineIdentifier"}"
AnonymousMachineIdentifier="${hmConfig.sops.placeholder."plex/anonymousMachineIdentifier"}"
CertificateUUID="${hmConfig.sops.placeholder."plex/certificateUuid"}"
PlexOnlineToken="${hmConfig.sops.placeholder."plex/plexOnlineToken"}"
FriendlyName="jupiter"
PlexOnlineUsername="karaolidis"
PlexOnlineMail="nick@karaolidis.com"
PlexOnlineHome="1"
PublishServerOnPlexOnlineKey="1"
AcceptedEULA="1"
DlnaEnabled="0"
customConnections="https://beta.media.karaolidis.com:443"
secureConnections="1"
IPNetworkType="v4only"
PushNotificationsEnabled="1"
logDebug="0"
sendCrashReports="0"
WanTotalMaxUploadRate="1000000"
GdmEnabled="0"
RelayEnabled="0"
FSEventLibraryPartialScanEnabled="1"
FSEventLibraryUpdatesEnabled="1"
GenerateAdMarkerBehavior="asap"
GenerateBIFBehavior="asap"
GenerateChapterThumbBehavior="asap"
GenerateVADBehavior="asap"
LoudnessAnalysisBehavior="asap"
MusicAnalysisBehavior="asap"
ScheduledLibraryUpdatesEnabled="1"
watchMusicSections="1"
HardwareDevicePath="10de:24dd:17aa:3a54@0000:01:00.0"
OptimizerTranscodeCountLimit="0"
ButlerTaskRefreshLibraries="1"
CinemaTrailersFromBluRay="1"
CinemaTrailersFromTheater="1"
CinemaTrailersType="0"
/>
'';
};
virtualisation.quadlet = {
networks.plex = { };
volumes.plex = { };
containers.plex = {
containerConfig = {
image = "docker-archive:${pkgs.dockerImages.plex}";
networks = [
networks.plex.ref
networks.traefik.ref
];
volumes =
let
postStart = pkgs.writeTextFile {
name = "post-start.sh";
executable = true;
text = builtins.readFile ./post-start.sh;
};
in
[
"${hmConfig.sops.templates.plex.path}:/etc/plex/Preferences.xml:ro"
"${postStart}:/etc/plex/post-start.sh:ro"
"/mnt/storage/private/storm/containers/storage/volumes/media/_data:/var/lib/media"
"${volumes.plex.ref}:/var/lib/plex"
];
labels = [
"traefik.enable=true"
"traefik.http.routers.plex.rule=Host(`beta.media.karaolidis.com`)"
];
podmanArgs = [ "--cdi-spec-dir=/run/cdi" ];
devices = [ "nvidia.com/gpu=all" ];
addCapabilities = [ "SYS_ADMIN" ];
publishPorts = [ "32400:32400/tcp" ];
};
unitConfig.After = [ "sops-nix.service" ];
};
};
};
}

View File

@@ -0,0 +1,112 @@
# shellcheck shell=sh
HOST="http://localhost:32400"
TOKEN="$(getPref "PlexOnlineToken")"
try_forever() {
until "$@" 2>&1; do
echo "Try failed: $* - retrying in 1s"
sleep 1
done
}
wait_for_api() {
try_forever curl -sf -H "X-Plex-Token: $TOKEN" "$HOST/identity"
echo "API is up!"
}
call() {
method="$1"
path="$2"
params="${3:-}"
curl -sf \
-X "$method" \
-H "Accept: application/json" \
-H "X-Plex-Token: $TOKEN" \
"$HOST/$path?$params"
}
get_resources() {
endpoint="$1"
call GET "$endpoint" | jq -r '.MediaContainer.Directory // []'
}
get_resource_id() {
endpoint="$1"
ident_field="$2"
name="$3"
get_resources "$endpoint" | jq -r --arg field "$ident_field" --arg name "$name" '.[] | select(.[$field] == $name) | .key // empty'
}
upsert_resource() {
endpoint="$1"
ident_field="$2"
name="$3"
params="$4"
id="$(get_resource_id "$endpoint" "$ident_field" "$name")"
if [ -n "$id" ] && [ "$id" != "null" ]; then
echo "Updating library '$name' (id=$id)"
call PUT "$endpoint/$id" "$params"
else
echo "Creating library '$name'"
call POST "$endpoint" "$params"
fi
}
prune_resources() {
endpoint="$1"
ident_field="$2"
shift 2
keep_list="$(printf '%s\n' "$@" | jq -R . | jq -s .)"
resources="$(get_resources "$endpoint")"
printf '%s' "$resources" | jq -c '.[]' | while IFS= read -r library; do
name="$(printf '%s' "$library" | jq -r --arg field "$ident_field" '.[$field]')"
id="$(printf '%s' "$library" | jq -r '.key')"
found="$(printf '%s' "$keep_list" | jq -r --arg name "$name" 'index($name)')"
if [ "$found" = "null" ]; then
echo "Deleting extra library '$name' (id=$id)"
call DELETE "$endpoint/$id" || echo "failed to delete $name, continuing"
fi
done
}
build_films_payload() {
cat <<-EOF
name=Films&type=movie&agent=tv.plex.agents.movie&scanner=Plex%20Movie&language=en-US&location=%2Fvar%2Flib%2Fmedia%2Flibraries%2Ffilms&prefs%5BuseRedbandTrailers%5D=1&prefs%5BincludeAdultContent%5D=1&prefs%5BautoCollectionThreshold%5D=2&prefs%5BcollectionMode%5D=1&prefs%5BenableAdMarkerGeneration%5D=2
EOF
}
build_shows_payload() {
cat <<-EOF
name=Shows&type=show&agent=tv.plex.agents.series&scanner=Plex%20TV%20Series&language=en-US&location=%2Fvar%2Flib%2Fmedia%2Flibraries%2Fshows&prefs%5BshowOrdering%5D=aired&prefs%5BuseSeasonTitles%5D=1&prefs%5BuseRedbandTrailers%5D=1&prefs%5BincludeAdultContent%5D=1&prefs%5BcollectionMode%5D=1&prefs%5BenableAdMarkerGeneration%5D=2
EOF
}
build_anime_films_payload() {
cat <<-EOF
name=Films%20%28Anime%29&type=movie&agent=tv.plex.agents.movie&scanner=Plex%20Movie&language=en-US&location=%2Fvar%2Flib%2Fmedia%2Flibraries%2Fanime%2Ffilms&prefs%5Bcountry%5D=JP&prefs%5BuseRedbandTrailers%5D=1&prefs%5BincludeAdultContent%5D=1&prefs%5BautoCollectionThreshold%5D=2&prefs%5BcollectionMode%5D=1&prefs%5BenableAdMarkerGeneration%5D=2
EOF
}
build_anime_shows_payload() {
cat <<-EOF
name=Shows%20%28Anime%29&type=show&agent=tv.plex.agents.series&scanner=Plex%20TV%20Series&language=en-US&location=%2Fvar%2Flib%2Fmedia%2Flibraries%2Fanime%2Fshows&prefs%5Bcountry%5D=JP&prefs%5BshowOrdering%5D=aired&prefs%5BuseSeasonTitles%5D=1&prefs%5BuseRedbandTrailers%5D=1&prefs%5BincludeAdultContent%5D=1&prefs%5BcollectionMode%5D=1&prefs%5BenableAdMarkerGeneration%5D=2
EOF
}
wait_for_api
try_forever upsert_resource "library/sections" "title" "Films" "$(build_films_payload)"
try_forever upsert_resource "library/sections" "title" "Shows" "$(build_shows_payload)"
try_forever upsert_resource "library/sections" "title" "Films (Anime)" "$(build_anime_films_payload)"
try_forever upsert_resource "library/sections" "title" "Shows (Anime)" "$(build_anime_shows_payload)"
prune_resources "library/sections" "title" "Films" "Shows" "Films (Anime)" "Shows (Anime)"

View File

@@ -23,6 +23,7 @@ in
templates = {
prowlarr-env.content = ''
API_KEY=${hmConfig.sops.placeholder."prowlarr/apiKey"}
NTFY_TOKEN=${hmConfig.sops.placeholder."ntfy/tokens/jupiter/media"}
'';
}
// builtins.listToAttrs (
@@ -77,7 +78,7 @@ in
environmentFiles = [ hmConfig.sops.templates.prowlarr-env.path ];
labels = [
"traefik.enable=true"
"traefik.http.routers.prowlarr.rule=Host(`media.karaolidis.com`) && PathPrefix(`/manage/indexers`)"
"traefik.http.routers.prowlarr.rule=Host(`beta.media.karaolidis.com`) && PathPrefix(`/manage/indexers`)"
"traefik.http.routers.prowlarr.middlewares=authelia@docker"
];
};

View File

@@ -205,8 +205,34 @@ build_cardigann_indexer_payload() {
'
}
build_ntfy_payload() {
cat <<-EOF
{
"onGrab": false,
"onHealthIssue": true,
"onHealthRestored": true,
"onApplicationUpdate": false,
"includeManualGrabs": false,
"includeHealthWarnings": false,
"name": "ntfy.sh",
"fields": [
{ "name": "serverUrl", "value": "https://ntfy.karaolidis.com" },
{ "name": "accessToken", "value": "$NTFY_TOKEN" },
{ "name": "topics", "value": "media" },
{ "name": "priority", "value": 3 },
{ "name": "tags", "value": "satellite" },
],
"implementation": "Ntfy",
"configContract": "NtfySettings"
}
EOF
}
wait_for_api
try_forever upsert_resource "notification" "name" "ntfy.sh" "$(build_ntfy_payload)"
prune_resources "notification" "name" "ntfy.sh"
try_forever upsert_resource "downloadclient" "name" "Transmission" "$(build_transmission_payload)"
prune_resources "downloadclient" "name" "Transmission"

View File

@@ -83,7 +83,7 @@ rec {
activeDirectory = "/var/lib/media/libraries${mediaFolderBase}";
minimumAvailability = "released";
isDefault = !isAnime;
externalUrl = "https://media.karaolidis.com${urlBase}";
externalUrl = "https://beta.media.karaolidis.com${urlBase}";
syncEnabled = true;
};
}

View File

@@ -20,6 +20,7 @@ rec {
apiKey = hmConfig.sops.placeholder."${hostName}/apiKey";
};
# https://github.com/recyclarr/config-templates/blob/master/radarr/templates/anime-radarr.yml
recyclarrConfig = mkRecyclarrConfig {
inherit hostName port urlBase;

View File

@@ -25,6 +25,7 @@ rec {
apiKey = hmConfig.sops.placeholder."${hostName}/apiKey";
# https://github.com/recyclarr/config-templates/blob/master/radarr/templates/uhd-bluray-web.yml
extraConfig = {
include = [
{ template = "radarr-quality-definition-movie"; }
@@ -72,9 +73,9 @@ rec {
}
{
trash_ids = [
"923b6abef9b17f937fab56cfcf89e1f1" # DV (WEBDL)
"b17886cb4158d9fea189859409975758" # HDR10Plus Boost
"55a5b50cb416dea5a50c4955896217ab" # DV HDR10+ Boost
"923b6abef9b17f937fab56cfcf89e1f1" # DV (w/o HDR fallback)
"b337d6812e06c200ec9a2d3cfa9d20a7" # DV Boost
"caa37d0df9c348912df1fb1d88f9273a" # HDR10+ Boost
];
assign_scores_to = [ { name = "UHD Bluray + WEB"; } ];
}

View File

@@ -25,6 +25,7 @@ rec {
apiKey = hmConfig.sops.placeholder."${hostName}/apiKey";
# https://github.com/recyclarr/config-templates/blob/master/radarr/templates/hd-bluray-web.yml
extraConfig = {
include = [
{ template = "radarr-quality-definition-movie"; }

View File

@@ -28,6 +28,7 @@ in
name = "${radarr.hostName}-env";
value.content = ''
API_KEY=${hmConfig.sops.placeholder."${radarr.hostName}/apiKey"}
NTFY_TOKEN=${hmConfig.sops.placeholder."ntfy/tokens/jupiter/media"}
'';
}) radarrs
);
@@ -81,7 +82,7 @@ in
environmentFiles = [ hmConfig.sops.templates."${radarr.hostName}-env".path ];
labels = [
"traefik.enable=true"
"traefik.http.routers.${radarr.hostName}.rule=Host(`media.karaolidis.com`) && PathPrefix(`${radarr.urlBase}`)"
"traefik.http.routers.${radarr.hostName}.rule=Host(`beta.media.karaolidis.com`) && PathPrefix(`${radarr.urlBase}`)"
"traefik.http.routers.${radarr.hostName}.middlewares=authelia@docker"
];
};

View File

@@ -146,8 +146,41 @@ build_rootfolder_payload() {
EOF
}
build_ntfy_payload() {
cat <<-EOF
{
"onGrab": true,
"onDownload": true,
"onUpgrade": true,
"onRename": false,
"onMovieAdded": true,
"onMovieDelete": true,
"onMovieFileDelete": true,
"onMovieFileDeleteForUpgrade": false,
"onHealthIssue": true,
"includeHealthWarnings": false,
"onHealthRestored": true,
"onApplicationUpdate": false,
"onManualInteractionRequired": true,
"name": "ntfy.sh",
"fields": [
{ "name": "serverUrl", "value": "https://ntfy.karaolidis.com" },
{ "name": "accessToken", "value": "$NTFY_TOKEN" },
{ "name": "priority", "value": 2 },
{ "name": "topics", "value": ["media"] },
{ "name": "tags", "value": ["clapper"] }
],
"implementation": "Ntfy",
"configContract": "NtfySettings"
}
EOF
}
wait_for_api
try_forever upsert_resource "notification" "name" "ntfy.sh" "$(build_ntfy_payload)"
prune_resources "notification" "name" "ntfy.sh"
if [ -n "$ROOT_FOLDER" ]; then
insert_or_skip_resource "rootfolder" "path" "$ROOT_FOLDER" "$(build_rootfolder_payload)"
prune_resources "rootfolder" "path" "$ROOT_FOLDER"

View File

@@ -85,7 +85,7 @@ rec {
activeAnimeDirectory = "/var/lib/media/libraries${mediaFolderBase}";
isDefault = !isAnime;
enableSeasonFolders = true;
externalUrl = "https://media.karaolidis.com${urlBase}";
externalUrl = "https://beta.media.karaolidis.com${urlBase}";
syncEnabled = true;
};
}

View File

@@ -20,6 +20,7 @@ rec {
apiKey = hmConfig.sops.placeholder."${hostName}/apiKey";
};
# https://github.com/recyclarr/config-templates/blob/master/sonarr/templates/anime-sonarr-v4.yml
recyclarrConfig = mkRecyclarrConfig {
inherit hostName port urlBase;

View File

@@ -20,6 +20,7 @@ rec {
apiKey = hmConfig.sops.placeholder."${hostName}/apiKey";
};
# https://github.com/recyclarr/config-templates/blob/master/sonarr/templates/web-2160p-v4.yml
recyclarrConfig = mkRecyclarrConfig {
inherit hostName port urlBase;
@@ -35,9 +36,9 @@ rec {
custom_formats = [
{
trash_ids = [
"9b27ab6498ec0f31a3353992e19434ca" # DV (WEBDL)
"0dad0a507451acddd754fe6dc3a7f5e7" # HDR10+ Boost
"385e9e8581d33133c3961bdcdeffb7b4" # DV HDR10+ Boost
"9b27ab6498ec0f31a3353992e19434ca" # DV (w/o HDR fallback)
"0c4b99df9206d2cfac3c05ab897dd62a" # HDR10+ Boost
"7c3a61a9c6cb04f52f1544be6d44a026" # DV Boost
];
assign_scores_to = [ { name = "WEB-2160p"; } ];
}

View File

@@ -20,6 +20,7 @@ rec {
apiKey = hmConfig.sops.placeholder."${hostName}/apiKey";
};
# https://github.com/recyclarr/config-templates/blob/master/sonarr/templates/web-1080p-v4.yml
recyclarrConfig = mkRecyclarrConfig {
inherit hostName port urlBase;

View File

@@ -28,6 +28,7 @@ in
name = "${sonarr.hostName}-env";
value.content = ''
API_KEY=${hmConfig.sops.placeholder."${sonarr.hostName}/apiKey"}
NTFY_TOKEN=${hmConfig.sops.placeholder."ntfy/tokens/jupiter/media"}
'';
}) sonarrs
);
@@ -81,7 +82,7 @@ in
environmentFiles = [ hmConfig.sops.templates."${sonarr.hostName}-env".path ];
labels = [
"traefik.enable=true"
"traefik.http.routers.${sonarr.hostName}.rule=Host(`media.karaolidis.com`) && PathPrefix(`${sonarr.urlBase}`)"
"traefik.http.routers.${sonarr.hostName}.rule=Host(`beta.media.karaolidis.com`) && PathPrefix(`${sonarr.urlBase}`)"
"traefik.http.routers.${sonarr.hostName}.middlewares=authelia@docker"
];
};

View File

@@ -146,8 +146,42 @@ build_rootfolder_payload() {
EOF
}
build_ntfy_payload() {
cat <<-EOF
{
"onGrab": true,
"onDownload": true,
"onUpgrade": true,
"onImportComplete": false,
"onRename": false,
"onSeriesAdd": true,
"onSeriesDelete": true,
"onEpisodeFileDelete": true,
"onEpisodeFileDeleteForUpgrade": false,
"onHealthIssue": true,
"includeHealthWarnings": false,
"onHealthRestored": true,
"onApplicationUpdate": false,
"onManualInteractionRequired": true,
"name": "ntfy.sh",
"fields": [
{ "name": "serverUrl", "value": "https://ntfy.karaolidis.com" },
{ "name": "accessToken", "value": "$NTFY_TOKEN" },
{ "name": "priority", "value": 2 },
{ "name": "topics", "value": ["media"] },
{ "name": "tags", "value": ["tv"] }
],
"implementation": "Ntfy",
"configContract": "NtfySettings"
}
EOF
}
wait_for_api
try_forever upsert_resource "notification" "name" "ntfy.sh" "$(build_ntfy_payload)"
prune_resources "notification" "name" "ntfy.sh"
if [ -n "$ROOT_FOLDER" ]; then
insert_or_skip_resource "rootfolder" "path" "$ROOT_FOLDER" "$(build_rootfolder_payload)"
prune_resources "rootfolder" "path" "$ROOT_FOLDER"

View File

@@ -33,8 +33,7 @@ in
volumes =
let
config = (pkgs.formats.json { }).generate "settings.override.json" {
ratio-limit-enabled = true;
ratio-limit = 5;
ratio-limit-enabled = false;
download-queue-enabled = false;
peer-limit-per-torrent = 100;
peer-limit-global = 1000;
@@ -50,12 +49,12 @@ in
"/mnt/storage/private/storm/containers/storage/volumes/media/_data:/var/lib/media"
];
environments = {
WIREGUARD_PUBLIC_KEY = "zctOjv4DH2gzXtLQy86Tp0vnT+PNpMsxecd2vUX/i0U=";
WIREGUARD_ENDPOINT = "146.70.179.50:51820";
WIREGUARD_PUBLIC_KEY = "jSrxQTLrvNPkljpa+F0OZT53mgTTwQA65oTMkqf382A=";
WIREGUARD_ENDPOINT = "46.29.25.4:51820";
};
labels = [
"traefik.enable=true"
"traefik.http.routers.transmission.rule=Host(`media.karaolidis.com`) && PathPrefix(`/manage/torrents`)"
"traefik.http.routers.transmission.rule=Host(`beta.media.karaolidis.com`) && PathPrefix(`/manage/torrents`)"
"traefik.http.routers.transmission.middlewares=authelia@docker"
];
};

View File

@@ -137,6 +137,7 @@ in
"groups"
"is_admin"
];
pre_configured_consent_duration = "1 year";
}
];
};
@@ -169,14 +170,14 @@ in
];
volumes =
let
post-setup = pkgs.writeTextFile {
postSetup = pkgs.writeTextFile {
name = "post-setup.sh";
executable = true;
text = builtins.readFile ./post-setup.sh;
};
in
[
"${post-setup}:/etc/nextcloud/post-setup.sh:ro"
"${postSetup}:/etc/nextcloud/post-setup.sh:ro"
"/mnt/storage/private/storm/containers/storage/volumes/nextcloud-data/_data:/var/lib/nextcloud"
"${volumes.nextcloud-log.ref}:/var/log/nextcloud"
"${volumes.nextcloud-config.ref}:/var/www/nextcloud/config"

View File

@@ -17,6 +17,9 @@ in
"ntfy/webPush/publicKey".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"ntfy/webPush/privateKey".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"ntfy/users/karaolidis".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"ntfy/users/jupiter".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"ntfy/tokens/jupiter/grafana".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
"ntfy/tokens/jupiter/media".sopsFile = "${inputs.secrets}/hosts/jupiter/secrets.yaml";
};
templates = {
@@ -43,7 +46,20 @@ in
auth-default-access = "deny-all";
auth-startup-queries = dbStartupQueries;
auth-users = [ "karaolidis:${hmConfig.sops.placeholder."ntfy/users/karaolidis"}:admin" ];
auth-users = [
"jupiter:${hmConfig.sops.placeholder."ntfy/users/jupiter"}:user"
"karaolidis:${hmConfig.sops.placeholder."ntfy/users/karaolidis"}:admin"
];
auth-access = [
"jupiter:grafana:wo"
"jupiter:media:wo"
];
auth-tokens = [
"jupiter:${hmConfig.sops.placeholder."ntfy/tokens/jupiter/grafana"}"
"jupiter:${hmConfig.sops.placeholder."ntfy/tokens/jupiter/media"}"
];
behind-proxy = true;

View File

@@ -65,6 +65,7 @@ in
];
response_types = [ "code" ];
token_endpoint_auth_method = "client_secret_post";
pre_configured_consent_duration = "1 year";
}
];
};

View File

@@ -10,16 +10,10 @@ let
inherit (hmConfig.virtualisation.quadlet) networks volumes containers;
in
{
networking.firewall = {
allowedTCPPorts = [
networking.firewall.allowedTCPPorts = [
80
443
];
allowedUDPPorts = [
80
443
];
};
home-manager.users.${user} = {
sops = {
@@ -76,7 +70,7 @@ in
"--entrypoints.https.http.tls=true"
"--entrypoints.https.http.tls.certResolver=letsencrypt"
"--entrypoints.https.http.tls.domains[0].main=karaolidis.com"
"--entrypoints.https.http.tls.domains[0].sans=*.karaolidis.com,*.tunnel.karaolidis.com,*.gaming.karaolidis.com"
"--entrypoints.https.http.tls.domains[0].sans=*.karaolidis.com,*.tunnel.karaolidis.com,*.gaming.karaolidis.com,beta.media.karaolidis.com"
"--entrypoints.https.http.tls.domains[1].main=krlds.com"
"--entrypoints.https.http.tls.domains[1].sans=*.krlds.com"
"--entryPoints.https.http3"
@@ -116,7 +110,7 @@ in
"traefik.http.middlewares.security-headers.headers.stsIncludeSubdomains=true"
"traefik.http.middlewares.security-headers.headers.stsPreload=true"
"traefik.http.middlewares.security-headers.headers.contentTypeNosniff=true"
"traefik.http.middlewares.security-headers.headers.frameDeny=true"
"traefik.http.middlewares.security-headers.headers.customFrameOptionsValue=SAMEORIGIN"
];
environmentFiles = [ hmConfig.sops.templates.traefik-env.path ];
};

View File

@@ -64,6 +64,7 @@ in
"offline_access"
];
response_types = [ "code" ];
pre_configured_consent_duration = "1 year";
}
];
};

View File

@@ -0,0 +1,162 @@
diff --git a/client/src/cli.rs b/client/src/cli.rs
index ee86783..e96b1b6 100644
--- a/client/src/cli.rs
+++ b/client/src/cli.rs
@@ -9,6 +9,7 @@ use enum_as_inner::EnumAsInner;
use crate::command::cache::{self, Cache};
use crate::command::get_closure::{self, GetClosure};
+use crate::command::key::{self, Key};
use crate::command::login::{self, Login};
use crate::command::push::{self, Push};
use crate::command::r#use::{self, Use};
@@ -30,6 +31,7 @@ pub enum Command {
Push(Push),
Cache(Cache),
WatchStore(WatchStore),
+ Key(Key),
#[clap(hide = true)]
GetClosure(GetClosure),
@@ -57,6 +59,7 @@ pub async fn run() -> Result<()> {
Command::Cache(_) => cache::run(opts).await,
Command::WatchStore(_) => watch_store::run(opts).await,
Command::GetClosure(_) => get_closure::run(opts).await,
+ Command::Key(_) => key::run(opts).await,
}
}
diff --git a/client/src/command/cache.rs b/client/src/command/cache.rs
index af01378..af24d8c 100644
--- a/client/src/command/cache.rs
+++ b/client/src/command/cache.rs
@@ -7,8 +7,11 @@ use crate::api::ApiClient;
use crate::cache::CacheRef;
use crate::cli::Opts;
use crate::config::Config;
-use attic::api::v1::cache_config::{
- CacheConfig, CreateCacheRequest, KeypairConfig, RetentionPeriodConfig,
+use attic::{
+ api::v1::cache_config::{
+ CacheConfig, CreateCacheRequest, KeypairConfig, RetentionPeriodConfig,
+ },
+ signing::NixKeypair,
};
/// Manage caches on an Attic server.
@@ -72,6 +75,12 @@ struct Create {
default_value = "cache.nixos.org-1"
)]
upstream_cache_key_names: Vec<String>,
+
+ /// The signing keypair to use for the cache.
+ ///
+ /// If not specified, a new keypair will be generated.
+ #[clap(long)]
+ keypair_path: Option<String>,
}
/// Configure a cache.
@@ -91,6 +100,14 @@ struct Configure {
#[clap(long)]
regenerate_keypair: bool,
+ /// Set a keypair for the cache.
+ ///
+ /// The server-side signing key will be set to the
+ /// specified keypair. This is useful for setting up
+ /// a cache with a pre-existing keypair.
+ #[clap(long, conflicts_with = "regenerate_keypair")]
+ keypair_path: Option<String>,
+
/// Make the cache public.
///
/// Use `--private` to make it private.
@@ -179,9 +196,15 @@ async fn create_cache(sub: Create) -> Result<()> {
let (server_name, server, cache) = config.resolve_cache(&sub.cache)?;
let api = ApiClient::from_server_config(server.clone())?;
+ let keypair = if let Some(keypair_path) = &sub.keypair_path {
+ let contents = std::fs::read_to_string(keypair_path)?;
+ KeypairConfig::Keypair(NixKeypair::from_str(&contents)?)
+ } else {
+ KeypairConfig::Generate
+ };
+
let request = CreateCacheRequest {
- // TODO: Make this configurable?
- keypair: KeypairConfig::Generate,
+ keypair,
is_public: sub.public,
priority: sub.priority,
store_dir: sub.store_dir,
@@ -230,6 +253,10 @@ async fn configure_cache(sub: Configure) -> Result<()> {
if sub.regenerate_keypair {
patch.keypair = Some(KeypairConfig::Generate);
+ } else if let Some(keypair_path) = &sub.keypair_path {
+ let contents = std::fs::read_to_string(keypair_path)?;
+ let keypair = KeypairConfig::Keypair(NixKeypair::from_str(&contents)?);
+ patch.keypair = Some(keypair);
}
patch.store_dir = sub.store_dir;
diff --git a/client/src/command/key.rs b/client/src/command/key.rs
new file mode 100644
index 0000000..807d8a7
--- /dev/null
+++ b/client/src/command/key.rs
@@ -0,0 +1,42 @@
+use anyhow::Result;
+use clap::{Parser, Subcommand};
+
+use crate::cli::Opts;
+use attic::signing::NixKeypair;
+
+/// Manage signing keys.
+#[derive(Debug, Parser)]
+pub struct Key {
+ #[clap(subcommand)]
+ command: KeyCommand,
+}
+
+#[derive(Debug, Subcommand)]
+enum KeyCommand {
+ Generate(Generate),
+}
+
+/// Generate a key.
+#[derive(Debug, Clone, Parser)]
+pub struct Generate {
+ /// Name of the key (must not contain colons).
+ name: String,
+}
+
+pub async fn run(opts: Opts) -> Result<()> {
+ let sub = opts.command.as_key().unwrap();
+ match &sub.command {
+ KeyCommand::Generate(sub) => generate_key(sub).await,
+ }
+}
+
+async fn generate_key(sub: &Generate) -> Result<()> {
+ let keypair = NixKeypair::generate(&sub.name)?;
+
+ println!("🔑 Generated keypair \"{}\"", sub.name);
+ println!();
+ println!(" Private key: {}", keypair.export_keypair());
+ println!(" Public key: {}", keypair.export_public_key());
+
+ Ok(())
+}
diff --git a/client/src/command/mod.rs b/client/src/command/mod.rs
index cca423f..26b105a 100644
--- a/client/src/command/mod.rs
+++ b/client/src/command/mod.rs
@@ -1,5 +1,6 @@
pub mod cache;
pub mod get_closure;
+pub mod key;
pub mod login;
pub mod push;
pub mod r#use;

View File

@@ -1,5 +1,11 @@
final: prev:
# FIXME: https://github.com/zhaofengli/attic/pull/280
prev.attic-client.overrideAttrs (oldAttrs: {
patches = oldAttrs.patches or [ ] ++ [ ./stdout-logging.patch ];
patches = oldAttrs.patches or [ ] ++ [
# fix: log non-errors to stdout
(builtins.fetchurl {
url = "https://github.com/zhaofengli/attic/pull/280.patch";
sha256 = "sha256:0j6ay6d9is7053sq5njakjmlpwk24db296rma694jggpl19ibxjv";
})
./declarative-key-pair.patch
];
})

View File

@@ -1,321 +0,0 @@
diff --git a/client/src/command/cache.rs b/client/src/command/cache.rs
index af01378..0602b3b 100644
--- a/client/src/command/cache.rs
+++ b/client/src/command/cache.rs
@@ -189,7 +189,7 @@ async fn create_cache(sub: Create) -> Result<()> {
};
api.create_cache(cache, request).await?;
- eprintln!(
+ println!(
"✨ Created cache \"{}\" on \"{}\"",
cache.as_str(),
server_name.as_str()
@@ -239,7 +239,7 @@ async fn configure_cache(sub: Configure) -> Result<()> {
let api = ApiClient::from_server_config(server.clone())?;
api.configure_cache(cache, &patch).await?;
- eprintln!(
+ println!(
"✅ Configured \"{}\" on \"{}\"",
cache.as_str(),
server_name.as_str()
@@ -254,12 +254,12 @@ async fn destroy_cache(sub: Destroy) -> Result<()> {
let (server_name, server, cache) = config.resolve_cache(&sub.cache)?;
if !sub.no_confirm {
- eprintln!("When you destory a cache:");
- eprintln!();
- eprintln!("1. Everyone will lose access.");
- eprintln!("2. The underlying data won't be deleted immediately.");
- eprintln!("3. You may not be able to create a cache of the same name.");
- eprintln!();
+ println!("When you destory a cache:");
+ println!();
+ println!("1. Everyone will lose access.");
+ println!("2. The underlying data won't be deleted immediately.");
+ println!("3. You may not be able to create a cache of the same name.");
+ println!();
let answer: String = Input::new()
.with_prompt(format!(
@@ -278,7 +278,7 @@ async fn destroy_cache(sub: Destroy) -> Result<()> {
let api = ApiClient::from_server_config(server.clone())?;
api.destroy_cache(cache).await?;
- eprintln!("🗑️ The cache was destroyed.");
+ println!("🗑️ The cache was destroyed.");
Ok(())
}
@@ -291,40 +291,40 @@ async fn show_cache_config(sub: Info) -> Result<()> {
let cache_config = api.get_cache_config(cache).await?;
if let Some(is_public) = cache_config.is_public {
- eprintln!(" Public: {}", is_public);
+ println!(" Public: {}", is_public);
}
if let Some(public_key) = cache_config.public_key {
- eprintln!(" Public Key: {}", public_key);
+ println!(" Public Key: {}", public_key);
}
if let Some(substituter_endpoint) = cache_config.substituter_endpoint {
- eprintln!("Binary Cache Endpoint: {}", substituter_endpoint);
+ println!("Binary Cache Endpoint: {}", substituter_endpoint);
}
if let Some(api_endpoint) = cache_config.api_endpoint {
- eprintln!(" API Endpoint: {}", api_endpoint);
+ println!(" API Endpoint: {}", api_endpoint);
}
if let Some(store_dir) = cache_config.store_dir {
- eprintln!(" Store Directory: {}", store_dir);
+ println!(" Store Directory: {}", store_dir);
}
if let Some(priority) = cache_config.priority {
- eprintln!(" Priority: {}", priority);
+ println!(" Priority: {}", priority);
}
if let Some(upstream_cache_key_names) = cache_config.upstream_cache_key_names {
- eprintln!(" Upstream Cache Keys: {:?}", upstream_cache_key_names);
+ println!(" Upstream Cache Keys: {:?}", upstream_cache_key_names);
}
if let Some(retention_period) = cache_config.retention_period {
match retention_period {
RetentionPeriodConfig::Period(period) => {
- eprintln!(" Retention Period: {:?}", period);
+ println!(" Retention Period: {:?}", period);
}
RetentionPeriodConfig::Global => {
- eprintln!(" Retention Period: Global Default");
+ println!(" Retention Period: Global Default");
}
}
}
diff --git a/client/src/command/login.rs b/client/src/command/login.rs
index 9abcea7..6cadd59 100644
--- a/client/src/command/login.rs
+++ b/client/src/command/login.rs
@@ -28,7 +28,7 @@ pub async fn run(opts: Opts) -> Result<()> {
let mut config_m = config.as_mut();
if let Some(server) = config_m.servers.get_mut(&sub.name) {
- eprintln!("✍️ Overwriting server \"{}\"", sub.name.as_str());
+ println!("✍️ Overwriting server \"{}\"", sub.name.as_str());
server.endpoint = sub.endpoint.to_owned();
@@ -38,7 +38,7 @@ pub async fn run(opts: Opts) -> Result<()> {
});
}
} else {
- eprintln!("✍️ Configuring server \"{}\"", sub.name.as_str());
+ println!("✍️ Configuring server \"{}\"", sub.name.as_str());
config_m.servers.insert(
sub.name.to_owned(),
diff --git a/client/src/command/push.rs b/client/src/command/push.rs
index b2bb661..5d39549 100644
--- a/client/src/command/push.rs
+++ b/client/src/command/push.rs
@@ -91,7 +91,7 @@ impl PushContext {
return Ok(());
} else {
- eprintln!("⚙️ Pushing {num_missing_paths} paths to \"{cache}\" on \"{server}\" ({num_already_cached} already cached, {num_upstream} in upstream)...",
+ println!("⚙️ Pushing {num_missing_paths} paths to \"{cache}\" on \"{server}\" ({num_already_cached} already cached, {num_upstream} in upstream)...",
cache = self.cache_name.as_str(),
server = self.server_name.as_str(),
num_missing_paths = plan.store_path_map.len(),
diff --git a/client/src/command/use.rs b/client/src/command/use.rs
index 37d8cd6..d87f65e 100644
--- a/client/src/command/use.rs
+++ b/client/src/command/use.rs
@@ -34,15 +34,15 @@ pub async fn run(opts: Opts) -> Result<()> {
let public_key = cache_config.public_key
.ok_or_else(|| anyhow!("The server did not tell us which public key it uses. Is signing managed by the client?"))?;
- eprintln!(
+ println!(
"Configuring Nix to use \"{cache}\" on \"{server_name}\":",
cache = cache.as_str(),
server_name = server_name.as_str(),
);
// Modify nix.conf
- eprintln!("+ Substituter: {}", substituter);
- eprintln!("+ Trusted Public Key: {}", public_key);
+ println!("+ Substituter: {}", substituter);
+ println!("+ Trusted Public Key: {}", public_key);
let mut nix_config = NixConfig::load().await?;
nix_config.add_substituter(&substituter);
@@ -50,7 +50,7 @@ pub async fn run(opts: Opts) -> Result<()> {
// Modify netrc
if let Some(token) = server.token()? {
- eprintln!("+ Access Token");
+ println!("+ Access Token");
let mut nix_netrc = NixNetrc::load().await?;
let host = Url::parse(&substituter)?
diff --git a/client/src/command/watch_store.rs b/client/src/command/watch_store.rs
index 24eaf7a..aec0c33 100644
--- a/client/src/command/watch_store.rs
+++ b/client/src/command/watch_store.rs
@@ -91,7 +91,7 @@ pub async fn run(opts: Opts) -> Result<()> {
watcher.watch(&store_dir, RecursiveMode::NonRecursive)?;
- eprintln!(
+ println!(
"👀 Pushing new store paths to \"{cache}\" on \"{server}\"",
cache = cache.as_str(),
server = server_name.as_str(),
diff --git a/client/src/push.rs b/client/src/push.rs
index 309bd4b..2fea414 100644
--- a/client/src/push.rs
+++ b/client/src/push.rs
@@ -595,7 +595,7 @@ pub async fn upload_path(
};
mp.suspend(|| {
- eprintln!(
+ println!(
"✅ {} ({})",
path.as_os_str().to_string_lossy(),
info_string
diff --git a/server/src/database/migration/m20230112_000004_migrate_nar_remote_files_to_chunks.rs b/server/src/database/migration/m20230112_000004_migrate_nar_remote_files_to_chunks.rs
index 42d70a6..6bbe585 100644
--- a/server/src/database/migration/m20230112_000004_migrate_nar_remote_files_to_chunks.rs
+++ b/server/src/database/migration/m20230112_000004_migrate_nar_remote_files_to_chunks.rs
@@ -24,7 +24,7 @@ impl MigrationTrait for Migration {
// When this migration is run, we assume that there are no
// preexisting chunks.
- eprintln!("* Migrating NARs to chunks...");
+ println!("* Migrating NARs to chunks...");
// Add a temporary column into `chunk` to store the related `nar_id`.
manager
diff --git a/server/src/database/migration/m20230112_000005_drop_old_nar_columns.rs b/server/src/database/migration/m20230112_000005_drop_old_nar_columns.rs
index 9d29b66..7436b4a 100644
--- a/server/src/database/migration/m20230112_000005_drop_old_nar_columns.rs
+++ b/server/src/database/migration/m20230112_000005_drop_old_nar_columns.rs
@@ -16,7 +16,7 @@ impl MigrationName for Migration {
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
- eprintln!("* Migrating NAR schema...");
+ println!("* Migrating NAR schema...");
if manager.get_database_backend() == DatabaseBackend::Sqlite {
// Just copy all data to a new table
diff --git a/server/src/lib.rs b/server/src/lib.rs
index 0314e69..89644e1 100644
--- a/server/src/lib.rs
+++ b/server/src/lib.rs
@@ -217,7 +217,7 @@ async fn fallback(_: Uri) -> ServerResult<()> {
/// Runs the API server.
pub async fn run_api_server(cli_listen: Option<SocketAddr>, config: Config) -> Result<()> {
- eprintln!("Starting API server...");
+ println!("Starting API server...");
let state = StateInner::new(config).await;
@@ -239,7 +239,7 @@ pub async fn run_api_server(cli_listen: Option<SocketAddr>, config: Config) -> R
.layer(TraceLayer::new_for_http())
.layer(CatchPanicLayer::new());
- eprintln!("Listening on {:?}...", listen);
+ println!("Listening on {:?}...", listen);
let listener = TcpListener::bind(&listen).await?;
@@ -256,7 +256,7 @@ pub async fn run_api_server(cli_listen: Option<SocketAddr>, config: Config) -> R
/// Runs database migrations.
pub async fn run_migrations(config: Config) -> Result<()> {
- eprintln!("Running migrations...");
+ println!("Running migrations...");
let state = StateInner::new(config).await;
let db = state.database().await?;
diff --git a/server/src/main.rs b/server/src/main.rs
index c5f08df..3a37c23 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -121,14 +121,14 @@ fn init_logging(tokio_console: bool) {
.init();
if tokio_console {
- eprintln!("Note: tokio-console is enabled");
+ println!("Note: tokio-console is enabled");
}
}
fn dump_version() {
#[cfg(debug_assertions)]
- eprintln!("Attic Server {} (debug)", env!("CARGO_PKG_VERSION"));
+ println!("Attic Server {} (debug)", env!("CARGO_PKG_VERSION"));
#[cfg(not(debug_assertions))]
- eprintln!("Attic Server {} (release)", env!("CARGO_PKG_VERSION"));
+ println!("Attic Server {} (release)", env!("CARGO_PKG_VERSION"));
}
diff --git a/server/src/oobe.rs b/server/src/oobe.rs
index d3d912d..98ef88c 100644
--- a/server/src/oobe.rs
+++ b/server/src/oobe.rs
@@ -77,25 +77,25 @@ pub async fn run_oobe() -> Result<()> {
token.encode(&SignatureType::RS256(key), &None, &None)?
};
- eprintln!();
- eprintln!("-----------------");
- eprintln!("Welcome to Attic!");
- eprintln!();
- eprintln!("A simple setup using SQLite and local storage has been configured for you in:");
- eprintln!();
- eprintln!(" {}", config_path.to_str().unwrap());
- eprintln!();
- eprintln!("Run the following command to log into this server:");
- eprintln!();
- eprintln!(" attic login local http://localhost:8080 {root_token}");
- eprintln!();
- eprintln!("Documentations and guides:");
- eprintln!();
- eprintln!(" https://docs.attic.rs");
- eprintln!();
- eprintln!("Enjoy!");
- eprintln!("-----------------");
- eprintln!();
+ println!();
+ println!("-----------------");
+ println!("Welcome to Attic!");
+ println!();
+ println!("A simple setup using SQLite and local storage has been configured for you in:");
+ println!();
+ println!(" {}", config_path.to_str().unwrap());
+ println!();
+ println!("Run the following command to log into this server:");
+ println!();
+ println!(" attic login local http://localhost:8080 {root_token}");
+ println!();
+ println!("Documentations and guides:");
+ println!();
+ println!(" https://docs.attic.rs");
+ println!();
+ println!("Enjoy!");
+ println!("-----------------");
+ println!();
Ok(())
}

View File

@@ -3,7 +3,7 @@ final: prev:
android-tools = import ./android-tools final prev;
attic-client = import ./attic-client final prev;
darktable = import ./darktable final prev;
hyprland = import ./hyprland final prev;
go-swagger = import ./go-swagger final prev;
mpv = import ./mpv final prev;
spicetify-cli = import ./spicetify-cli final prev;
tea = import ./tea final prev;
@@ -20,9 +20,12 @@ final: prev:
flaresolverr = final.docker-image-flaresolverr;
gitea = final.docker-image-gitea;
gitea-act-runner = final.docker-image-gitea-act-runner;
grafana = final.docker-image-grafana;
gitea-act-runner-worker = final.docker-image-gitea-act-runner-worker;
grafana-image-renderer = final.docker-image-grafana-image-renderer;
jellyfin = final.docker-image-jellyfin;
grafana-to-ntfy = final.docker-image-grafana-to-ntfy;
grafana = final.docker-image-grafana;
immich = final.docker-image-immich;
immich-machine-learning = final.docker-image-immich-machine-learning;
jellyseerr = final.docker-image-jellyseerr;
littlelink-server = final.docker-image-littlelink-server;
mariadb = final.docker-image-mariadb;
@@ -33,7 +36,9 @@ final: prev:
ntfy = final.docker-image-ntfy;
oidcwarden = final.docker-image-oidcwarden;
outline = final.docker-image-outline;
plex = final.docker-image-plex;
postgresql = final.docker-image-postgresql;
postgresql-vectorchord = final.docker-image-postgresql-vectorchord;
prometheus = final.docker-image-prometheus;
prometheus-fail2ban-exporter = final.docker-image-prometheus-fail2ban-exporter;
prometheus-node-exporter = final.docker-image-prometheus-node-exporter;
@@ -51,19 +56,6 @@ final: prev:
transmission-protonvpn = final.docker-image-transmission-protonvpn;
whoami = final.docker-image-whoami;
};
jellyfinPlugins = prev.jellyfinPlugins or { } // {
bookshelf = final.jellyfin-plugin-bookshelf-bin;
intro-skipper = final.jellyfin-plugin-intro-skipper-bin;
opensubtitles = final.jellyfin-plugin-opensubtitles-bin;
playbackreporting = final.jellyfin-plugin-playbackreporting-bin;
reports = final.jellyfin-plugin-reports-bin;
sso = final.jellyfin-plugin-sso-bin;
subtitleextract = final.jellyfin-plugin-subtitleextract-bin;
tmdbboxsets = final.jellyfin-plugin-tmdbboxsets-bin;
tvdb = final.jellyfin-plugin-tvdb-bin;
};
obsidianPlugins = prev.obsidianPlugins or { } // {
better-word-count = final.obsidian-plugin-better-word-count;
dataview = final.obsidian-plugin-dataview;

View File

@@ -0,0 +1,13 @@
final: prev:
# FIXME: https://github.com/go-swagger/go-swagger/issues/3220
# FIXME: https://github.com/go-swagger/go-swagger/issues/3229
prev.go-swagger.overrideAttrs (oldAttrs: {
src = final.fetchFromGitHub {
owner = "go-swagger";
repo = "go-swagger";
rev = "717e3cb29becaaf00e56953556c6d80f8a01b286";
hash = "sha256-IuIVc7NwfXSBQ2tojD4LY7I18k5MJaVeDDPsi/OBFL0=";
};
vendorHash = "sha256-x3fTIXmI5NnOKph1D84MHzf1Kod+WLYn1JtnWLr4x+U=";
})

View File

@@ -1,4 +0,0 @@
final: prev:
prev.hyprland.overrideAttrs (oldAttrs: {
patches = oldAttrs.patches or [ ] ++ [ ./fix-maxwidth-resolution-mode.patch ];
})

View File

@@ -1,13 +0,0 @@
diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp
index 635c7977..80093c0d 100644
--- a/src/config/ConfigManager.cpp
+++ b/src/config/ConfigManager.cpp
@@ -2091,6 +2091,8 @@ bool CMonitorRuleParser::parseMode(const std::string& value) {
m_rule.resolution = Vector2D(-1, -1);
else if (value.starts_with("highres"))
m_rule.resolution = Vector2D(-1, -2);
+ else if (value.starts_with("maxwidth"))
+ m_rule.resolution = Vector2D(-1, -3);
else if (parseModeLine(value, m_rule.drmMode)) {
m_rule.resolution = Vector2D(m_rule.drmMode.hdisplay, m_rule.drmMode.vdisplay);
m_rule.refreshRate = float(m_rule.drmMode.vrefresh) / 1000;

View File

@@ -4,7 +4,6 @@
android-tools
attic-client
darktable
hyprland
mpv
spicetify-cli
tea

View File

@@ -1,10 +1,10 @@
final: prev:
prev.tea.overrideAttrs (oldAttrs: {
patches = oldAttrs.patches or [ ] ++ [
# fix: evaluate env login in repo context
(builtins.fetchurl {
url = "https://gitea.com/gitea/tea/pulls/639.patch";
sha256 = "sha256:0c5gpi6aajd3h0wp7lrvj5qk9wsqhgbap7ijvl0x117v0g8mgzvs";
url = "https://gitea.com/gitea/tea/pulls/809.patch";
sha256 = "sha256:1pzp4z49nzdd0rd03d6gmdrkn95vxmamykpz10giampssjn31sn3";
})
./instance-ssh-host-env.patch
];
})

View File

@@ -1,174 +0,0 @@
diff --git a/modules/config/login.go b/modules/config/login.go
index 3b77fb9..94de9cd 100644
--- a/modules/config/login.go
+++ b/modules/config/login.go
@@ -13,6 +13,7 @@ import (
"net/http/cookiejar"
"net/url"
"os"
+ "strconv"
"strings"
"time"
@@ -200,6 +201,63 @@ func UpdateLogin(login *Login) error {
return saveConfig()
}
+// CreateLoginFromEnvVars returns a login based on environment variables, or nil if no login can be created
+func CreateLoginFromEnvVars() (*Login, error) {
+ var token string
+
+ giteaToken := os.Getenv("GITEA_TOKEN")
+ githubToken := os.Getenv("GH_TOKEN")
+ giteaInstanceURL := os.Getenv("GITEA_INSTANCE_URL")
+ instanceInsecure := os.Getenv("GITEA_INSTANCE_INSECURE")
+ giteaInstanceSSHHost := os.Getenv("GITEA_INSTANCE_SSH_HOST")
+ insecure := false
+ if len(instanceInsecure) > 0 {
+ insecure, _ = strconv.ParseBool(instanceInsecure)
+ }
+
+ // if no tokens are set, or no instance url for gitea fail fast
+ if len(giteaInstanceURL) == 0 || (len(giteaToken) == 0 && len(githubToken) == 0) {
+ return nil, nil
+ }
+
+ token = giteaToken
+ if len(giteaToken) == 0 {
+ token = githubToken
+ }
+
+ login := &Login{
+ Name: "GITEA_LOGIN_VIA_ENV",
+ URL: giteaInstanceURL,
+ Token: token,
+ SSHHost: giteaInstanceSSHHost,
+ Insecure: insecure,
+ SSHKey: "",
+ SSHCertPrincipal: "",
+ SSHKeyFingerprint: "",
+ SSHAgent: false,
+ VersionCheck: true,
+ Created: time.Now().Unix(),
+ }
+
+ client := login.Client()
+ u, _, err := client.GetMyUserInfo()
+ if err != nil {
+ return nil, fmt.Errorf("failed to validate token: %s", err)
+ }
+
+ login.User = u.UserName
+
+ if login.SSHHost == "" {
+ parsedURL, err := url.Parse(giteaInstanceURL)
+ if err != nil {
+ return nil, err
+ }
+ login.SSHHost = parsedURL.Host
+ }
+
+ return login, nil
+}
+
// Client returns a client to operate Gitea API. You may provide additional modifiers
// for the client like gitea.SetBasicAuth() for customization
func (l *Login) Client(options ...gitea.ClientOption) *gitea.Client {
diff --git a/modules/context/context.go b/modules/context/context.go
index aec5592..636eeec 100644
--- a/modules/context/context.go
+++ b/modules/context/context.go
@@ -9,9 +9,7 @@ import (
"log"
"os"
"path"
- "strconv"
"strings"
- "time"
"code.gitea.io/tea/modules/config"
"code.gitea.io/tea/modules/git"
@@ -108,16 +106,6 @@ func InitCommand(cmd *cli.Command) *TeaContext {
c.RepoSlug = repoFlag
}
- // override config user with env variable
- envLogin := GetLoginByEnvVar()
- if envLogin != nil {
- _, err := utils.ValidateAuthenticationMethod(envLogin.URL, envLogin.Token, "", "", false, "", "")
- if err != nil {
- log.Fatal(err.Error())
- }
- c.Login = envLogin
- }
-
// override login from flag, or use default login if repo based detection failed
if len(loginFlag) != 0 {
c.Login = config.GetLoginByName(loginFlag)
@@ -196,10 +184,25 @@ func contextFromLocalRepo(repoPath, remoteValue string) (*git.TeaRepo, *config.L
return repo, nil, "", fmt.Errorf("Remote '%s' not found in this Git repository", remoteValue)
}
+ envLogin, err := config.CreateLoginFromEnvVars()
+ if err != nil {
+ log.Fatal(err.Error())
+ }
+
logins, err := config.GetLogins()
if err != nil {
return repo, nil, "", err
}
+
+ if envLogin != nil {
+ _, err := utils.ValidateAuthenticationMethod(envLogin.URL, envLogin.Token, "", "", false, "", "")
+ if err != nil {
+ log.Fatal(err.Error())
+ }
+
+ logins = append([]config.Login{*envLogin}, logins...)
+ }
+
for _, l := range logins {
sshHost := l.GetSSHHost()
for _, u := range remoteConfig.URLs {
@@ -223,40 +226,3 @@ func contextFromLocalRepo(repoPath, remoteValue string) (*git.TeaRepo, *config.L
return repo, nil, "", errNotAGiteaRepo
}
-
-// GetLoginByEnvVar returns a login based on environment variables, or nil if no login can be created
-func GetLoginByEnvVar() *config.Login {
- var token string
-
- giteaToken := os.Getenv("GITEA_TOKEN")
- githubToken := os.Getenv("GH_TOKEN")
- giteaInstanceURL := os.Getenv("GITEA_INSTANCE_URL")
- instanceInsecure := os.Getenv("GITEA_INSTANCE_INSECURE")
- insecure := false
- if len(instanceInsecure) > 0 {
- insecure, _ = strconv.ParseBool(instanceInsecure)
- }
-
- // if no tokens are set, or no instance url for gitea fail fast
- if len(giteaInstanceURL) == 0 || (len(giteaToken) == 0 && len(githubToken) == 0) {
- return nil
- }
-
- token = giteaToken
- if len(giteaToken) == 0 {
- token = githubToken
- }
-
- return &config.Login{
- Name: "GITEA_LOGIN_VIA_ENV",
- URL: giteaInstanceURL,
- Token: token,
- Insecure: insecure,
- SSHKey: "",
- SSHCertPrincipal: "",
- SSHKeyFingerprint: "",
- SSHAgent: false,
- Created: time.Now().Unix(),
- VersionCheck: false,
- }
-}

View File

@@ -1,23 +1,20 @@
{ pkgs, ... }:
# AUTO-UPDATE: nix-update --flake comentario --version=branch=dev --subpackage frontend
pkgs.buildGoModule (finalAttrs: {
pkgs.buildGo125Module (finalAttrs: {
pname = "comentario";
version = "3.14.0-unstable-2025-08-29";
version = "3.14.0-unstable-2025-10-03";
src = pkgs.fetchFromGitLab {
owner = "comentario";
repo = "comentario";
# FIXME: Stable rev once type error is fixed
rev = "90773f976366318389f9d5aa457e6303e6159740";
hash = "sha256-f0Y+OdbsG8eA2kD17b4QWaL0hAuoF476XtYm/aFOmLY=";
rev = "4f493bb2a8cfe6f72dea8aeb3c13671e90c667dc";
hash = "sha256-L1QcDgjWin7DT3XMyTAMl4f8hnC5d7inemzBLFMppi0=";
};
patches = [
# FIXME: https://gitlab.com/comentario/comentario/-/merge_requests/23
./dynamic-config-env-vars.patch
];
patches = [ ./superuser-claim.patch ];
vendorHash = "sha256-IYo1Z7BVTGafsN4gC554S33r7X9Urb1uS4RkOqMYlP0=";
vendorHash = "sha256-tnnSJN3CEDbuj4/B0PBwpYCdm3SOgSbvC7htS9+9pr4=";
nativeBuildInputs = with pkgs; [
go-swagger
@@ -37,7 +34,7 @@ pkgs.buildGoModule (finalAttrs: {
missingHashes = ./missing-hashes.json;
offlineCache = pkgs.yarn-berry.fetchYarnBerryDeps {
inherit (finalFrontendAttrs) src patches missingHashes;
hash = "sha256-bn/PNgk7ZjCzGSj7BQQCB+5RY+ivJGYZa2/GC4eRjPY=";
hash = "sha256-dYk85+e9C0yHZ9jYgsefStZfyQFZZku+Z4Kn7bN4Qjw=";
};
nativeBuildInputs = with pkgs; [
@@ -68,13 +65,10 @@ pkgs.buildGoModule (finalAttrs: {
'';
installPhase = ''
mkdir -p $out/bin $out/lib/${finalAttrs.pname}
mkdir -p $out/bin
cp -r $GOPATH/bin/comentario $out/bin/${finalAttrs.pname}
cp -r db templates $out/lib/${finalAttrs.pname}
wrapProgram $out/bin/${finalAttrs.pname} \
--add-flags "--db-migration-path=$out/lib/${finalAttrs.pname}/db" \
--add-flags "--template-path=$out/lib/${finalAttrs.pname}/templates" \
--add-flags "--static-path=${finalAttrs.frontend}"
'';

View File

@@ -1,312 +0,0 @@
From 2ef5994a21d694bdf50d1d9d4c524c9584368917 Mon Sep 17 00:00:00 2001
From: Nikolaos Karaolidis <nick@karaolidis.com>
Date: Mon, 28 Jul 2025 15:33:55 +0100
Subject: [PATCH] fe: dynamic configuration env vars
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
---
.../dynamic/auth.emailupdate.enabled.en.md | 1 +
.../auth.login.local.maxattempts.en.md | 2 ++
.../auth.signup.confirm.commenter.en.md | 2 ++
.../dynamic/auth.signup.confirm.user.en.md | 2 ++
.../backend/dynamic/auth.signup.enabled.en.md | 2 ++
...in.defaults.comments.deletion.author.en.md | 2 ++
...defaults.comments.deletion.moderator.en.md | 2 ++
...ain.defaults.comments.editing.author.en.md | 2 ++
....defaults.comments.editing.moderator.en.md | 2 ++
...omain.defaults.comments.enablevoting.en.md | 2 ++
...domain.defaults.comments.rss.enabled.en.md | 2 ++
...domain.defaults.comments.showdeleted.en.md | 2 ++
...ain.defaults.comments.text.maxlength.en.md | 2 ++
.../domain.defaults.login.showforunauth.en.md | 2 ++
...ain.defaults.markdown.images.enabled.en.md | 2 ++
...main.defaults.markdown.links.enabled.en.md | 2 ++
...ain.defaults.markdown.tables.enabled.en.md | 4 ++-
...main.defaults.signup.enablefederated.en.md | 2 ++
.../domain.defaults.signup.enablelocal.en.md | 2 ++
.../domain.defaults.signup.enablesso.en.md | 2 ++
.../dynamic/integrations.usegravatar.en.md | 1 +
.../dynamic/operation.newowner.enabled.en.md | 2 ++
internal/data/dyn_config.go | 28 +++++++++++++++++--
23 files changed, 69 insertions(+), 3 deletions(-)
diff --git a/docs/content/configuration/backend/dynamic/auth.emailupdate.enabled.en.md b/docs/content/configuration/backend/dynamic/auth.emailupdate.enabled.en.md
index 5b9c5716..a139c953 100644
--- a/docs/content/configuration/backend/dynamic/auth.emailupdate.enabled.en.md
+++ b/docs/content/configuration/backend/dynamic/auth.emailupdate.enabled.en.md
@@ -20,3 +20,4 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter defines w
* If no operational mailer is configured, the email will be updated right away, without intermediate confirmation.
* If set to `Off`, users cannot change their emails themselves. Email can only be updated by a [superuser](/kb/permissions/superuser) (for example, at a user's request).
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_AUTH_EMAILUPDATE_ENABLED`.
diff --git a/docs/content/configuration/backend/dynamic/auth.login.local.maxattempts.en.md b/docs/content/configuration/backend/dynamic/auth.login.local.maxattempts.en.md
index bada14ec..5a3993a8 100644
--- a/docs/content/configuration/backend/dynamic/auth.login.local.maxattempts.en.md
+++ b/docs/content/configuration/backend/dynamic/auth.login.local.maxattempts.en.md
@@ -20,3 +20,5 @@ Comentario keeps a counter of failed login attempts for each user.
* The failed login attempt counter gets reset to zero as soon as the user has successfully logged in.
This mechanism is mostly intended to safeguard users against brute-force attacks on their passwords. In order to disable it, set the value to `0`.
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_AUTH_LOGIN_LOCAL_MAXATTEMPTS`.
diff --git a/docs/content/configuration/backend/dynamic/auth.signup.confirm.commenter.en.md b/docs/content/configuration/backend/dynamic/auth.signup.confirm.commenter.en.md
index 6ad25e5a..fbb38b3b 100644
--- a/docs/content/configuration/backend/dynamic/auth.signup.confirm.commenter.en.md
+++ b/docs/content/configuration/backend/dynamic/auth.signup.confirm.commenter.en.md
@@ -17,3 +17,5 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter configure
* If set to `Off`, commenters will be logged in immediately upon registration. This is not recommended due to security considerations; it may also render the user account unusable in the future if they misspelled their email address.
This setting applies only to websites embedding Comentario. For the Administration UI there's a [separate configuration item](auth.signup.confirm.user).
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_AUTH_SIGNUP_CONFIRM_COMMENTER`.
diff --git a/docs/content/configuration/backend/dynamic/auth.signup.confirm.user.en.md b/docs/content/configuration/backend/dynamic/auth.signup.confirm.user.en.md
index 6a14a1d3..3aee4ae6 100644
--- a/docs/content/configuration/backend/dynamic/auth.signup.confirm.user.en.md
+++ b/docs/content/configuration/backend/dynamic/auth.signup.confirm.user.en.md
@@ -17,3 +17,5 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter configure
* If set to `Off`, users can log in immediately upon registration. This is not recommended due to security considerations; it may also render the user account unusable in the future if they misspelled their email address.
This setting applies only to the Administration UI. For websites embedding Comentario there's a [separate configuration item](auth.signup.confirm.commenter).
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_AUTH_SIGNUP_CONFIRM_USER`.
diff --git a/docs/content/configuration/backend/dynamic/auth.signup.enabled.en.md b/docs/content/configuration/backend/dynamic/auth.signup.enabled.en.md
index 35acc2ba..d8a38804 100644
--- a/docs/content/configuration/backend/dynamic/auth.signup.enabled.en.md
+++ b/docs/content/configuration/backend/dynamic/auth.signup.enabled.en.md
@@ -20,3 +20,5 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter controls
* If set to `Off`, new user registrations are forbidden.
This setting applies only to the Administration UI. It can be useful for (temporarily) preventing new sign-ups.
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_AUTH_SIGNUP_ENABLED`.
diff --git a/docs/content/configuration/backend/dynamic/domain.defaults.comments.deletion.author.en.md b/docs/content/configuration/backend/dynamic/domain.defaults.comments.deletion.author.en.md
index 9fef72b4..93f280cf 100644
--- a/docs/content/configuration/backend/dynamic/domain.defaults.comments.deletion.author.en.md
+++ b/docs/content/configuration/backend/dynamic/domain.defaults.comments.deletion.author.en.md
@@ -18,3 +18,5 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter can be us
* When set to `On`, comment authors will see a *trashcan* button on their comments, which allows them to delete a written comment.
* If set to `Off`, comments cannot be removed by their authors once submitted (but possibly can [by domain moderators](domain.defaults.comments.deletion.moderator) if enabled).
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_DOMAIN_DEFAULTS_COMMENTS_DELETION_AUTHOR`.
diff --git a/docs/content/configuration/backend/dynamic/domain.defaults.comments.deletion.moderator.en.md b/docs/content/configuration/backend/dynamic/domain.defaults.comments.deletion.moderator.en.md
index 97c3b3e0..f1c002f8 100644
--- a/docs/content/configuration/backend/dynamic/domain.defaults.comments.deletion.moderator.en.md
+++ b/docs/content/configuration/backend/dynamic/domain.defaults.comments.deletion.moderator.en.md
@@ -18,3 +18,5 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter can be us
* When set to `On`, users with the moderator or owner [role](/kb/permissions/roles) and [superusers](/kb/permissions/superuser) will see a *trashcan* button on every comment, allowing them to delete any comment on the domain's pages.
* If set to `Off`, comments cannot be removed by moderators (but possibly can [by their authors](domain.defaults.comments.deletion.author) if enabled).
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_DOMAIN_DEFAULTS_COMMENTS_DELETION_MODERATOR`.
diff --git a/docs/content/configuration/backend/dynamic/domain.defaults.comments.editing.author.en.md b/docs/content/configuration/backend/dynamic/domain.defaults.comments.editing.author.en.md
index 346a93b6..de989f59 100644
--- a/docs/content/configuration/backend/dynamic/domain.defaults.comments.editing.author.en.md
+++ b/docs/content/configuration/backend/dynamic/domain.defaults.comments.editing.author.en.md
@@ -17,3 +17,5 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter can be us
* When set to `On`, comment authors will see a *pencil* button on their comments, which allows them to edit a written comment.
* If set to `Off`, comments cannot be changed by their authors once submitted (but possibly can [by domain moderators](domain.defaults.comments.editing.moderator) if enabled).
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_DOMAIN_DEFAULTS_COMMENTS_EDITING_AUTHOR`.
diff --git a/docs/content/configuration/backend/dynamic/domain.defaults.comments.editing.moderator.en.md b/docs/content/configuration/backend/dynamic/domain.defaults.comments.editing.moderator.en.md
index 0b35d1e3..25bfcbf1 100644
--- a/docs/content/configuration/backend/dynamic/domain.defaults.comments.editing.moderator.en.md
+++ b/docs/content/configuration/backend/dynamic/domain.defaults.comments.editing.moderator.en.md
@@ -17,3 +17,5 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter can be us
* When set to `On`, users with the moderator or owner [role](/kb/permissions/roles) and [superusers](/kb/permissions/superuser) will see a *pencil* button on every comment, allowing them to edit any comment on the domain's pages.
* If set to `Off`, comments cannot be changed by moderators (but possibly can [by their authors](domain.defaults.comments.editing.author) if enabled).
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_DOMAIN_DEFAULTS_COMMENTS_EDITING_MODERATOR`.
diff --git a/docs/content/configuration/backend/dynamic/domain.defaults.comments.enablevoting.en.md b/docs/content/configuration/backend/dynamic/domain.defaults.comments.enablevoting.en.md
index a2e72f5b..17ec769e 100644
--- a/docs/content/configuration/backend/dynamic/domain.defaults.comments.enablevoting.en.md
+++ b/docs/content/configuration/backend/dynamic/domain.defaults.comments.enablevoting.en.md
@@ -13,3 +13,5 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter can be us
* When set to `On`, every comment card will contain a score and a set of upvote/downvote buttons. Also, any commenter will be able to upvote or downvote others' comments (bot not those they authored self).
* If set to `Off`, the score and the voting buttons will be hidden. It will also disable sorting comments by votes.
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_DOMAIN_DEFAULTS_COMMENTS_ENABLEVOTING`.
diff --git a/docs/content/configuration/backend/dynamic/domain.defaults.comments.rss.enabled.en.md b/docs/content/configuration/backend/dynamic/domain.defaults.comments.rss.enabled.en.md
index 3981741e..4108ed90 100644
--- a/docs/content/configuration/backend/dynamic/domain.defaults.comments.rss.enabled.en.md
+++ b/docs/content/configuration/backend/dynamic/domain.defaults.comments.rss.enabled.en.md
@@ -21,3 +21,5 @@ When RSS is enabled, the users will be able to subscribe to comment feeds using:
* `RSS` dropdown button under the [comment editor](/kb/comment-editor) (left of the sorting buttons).
* `Comment RSS feed` links in Domain properties and Domain page properties.
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_DOMAIN_DEFAULTS_COMMENTS_RSS_ENABLED`.
diff --git a/docs/content/configuration/backend/dynamic/domain.defaults.comments.showdeleted.en.md b/docs/content/configuration/backend/dynamic/domain.defaults.comments.showdeleted.en.md
index 89b26859..f2293dba 100644
--- a/docs/content/configuration/backend/dynamic/domain.defaults.comments.showdeleted.en.md
+++ b/docs/content/configuration/backend/dynamic/domain.defaults.comments.showdeleted.en.md
@@ -18,3 +18,5 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter controls
* If set to `Off`, deleted comments will be hidden in the comment tree immediately, as well as all its child comments.
This setting doesn't affect comment display in the Administration UI (it has a separate switch for hiding deleted comments).
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_DOMAIN_DEFAULTS_COMMENTS_SHOWDELETED`.
diff --git a/docs/content/configuration/backend/dynamic/domain.defaults.comments.text.maxlength.en.md b/docs/content/configuration/backend/dynamic/domain.defaults.comments.text.maxlength.en.md
index a87b2fff..9e068c5a 100644
--- a/docs/content/configuration/backend/dynamic/domain.defaults.comments.text.maxlength.en.md
+++ b/docs/content/configuration/backend/dynamic/domain.defaults.comments.text.maxlength.en.md
@@ -21,3 +21,5 @@ It defines how long comment texts can be on a specific domain, representing the
* The lowest possible value for this setting is the "Twitter limit" of `140` characters.
* The top (physical) limit is `1048576` (1 MiB).
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_DOMAIN_DEFAULTS_COMMENTS_TEXT_MAXLENGTH`.
diff --git a/docs/content/configuration/backend/dynamic/domain.defaults.login.showforunauth.en.md b/docs/content/configuration/backend/dynamic/domain.defaults.login.showforunauth.en.md
index 85b264e6..65a29ea4 100644
--- a/docs/content/configuration/backend/dynamic/domain.defaults.login.showforunauth.en.md
+++ b/docs/content/configuration/backend/dynamic/domain.defaults.login.showforunauth.en.md
@@ -19,3 +19,5 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter allows to
* If set to `Off`, the login dialog will be skipped if [commenting without registration](/configuration/frontend/domain/authentication) is enabled for this domain. Otherwise the dialog will be shown and user will be requested to log in or sign up.
When this setting is `Off`, the user can still authenticate explicitly using the `Sign in` button above the comment editor.
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_DOMAIN_DEFAULTS_LOGIN_SHOWFORUNAUTH`.
diff --git a/docs/content/configuration/backend/dynamic/domain.defaults.markdown.images.enabled.en.md b/docs/content/configuration/backend/dynamic/domain.defaults.markdown.images.enabled.en.md
index f229294c..6af7d884 100644
--- a/docs/content/configuration/backend/dynamic/domain.defaults.markdown.images.enabled.en.md
+++ b/docs/content/configuration/backend/dynamic/domain.defaults.markdown.images.enabled.en.md
@@ -18,3 +18,5 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter configure
* If set to `Off`, commenters won't be able to insert images, and the corresponding markup will be removed from the resulting text.
This setting only applies to newly written comments and does not affect images in existing comments.
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_DOMAIN_DEFAULTS_MARKDOWN_IMAGES_ENABLED`.
diff --git a/docs/content/configuration/backend/dynamic/domain.defaults.markdown.links.enabled.en.md b/docs/content/configuration/backend/dynamic/domain.defaults.markdown.links.enabled.en.md
index cfaaddcb..bb171b1e 100644
--- a/docs/content/configuration/backend/dynamic/domain.defaults.markdown.links.enabled.en.md
+++ b/docs/content/configuration/backend/dynamic/domain.defaults.markdown.links.enabled.en.md
@@ -18,3 +18,5 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter configure
* If set to `Off`, commenters won't be able to insert links, and the corresponding string will be rendered as plain, non-clickable text.
This setting only applies to newly written comments and does not affect links in existing comments.
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_DOMAIN_DEFAULTS_MARKDOWN_LINKS_ENABLED`.
diff --git a/docs/content/configuration/backend/dynamic/domain.defaults.markdown.tables.enabled.en.md b/docs/content/configuration/backend/dynamic/domain.defaults.markdown.tables.enabled.en.md
index 243affed..f38c097e 100644
--- a/docs/content/configuration/backend/dynamic/domain.defaults.markdown.tables.enabled.en.md
+++ b/docs/content/configuration/backend/dynamic/domain.defaults.markdown.tables.enabled.en.md
@@ -16,5 +16,7 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter configure
* If set to `On`, commenters can insert [tables](/kb/markdown#tables) in comments.
* If set to `Off`, commenters won't be able to insert tables, and the corresponding markup will be removed from the resulting text.
-
+
This setting only applies to newly written comments and does not affect tables in existing comments.
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_DOMAIN_DEFAULTS_MARKDOWN_TABLES_ENABLED`.
diff --git a/docs/content/configuration/backend/dynamic/domain.defaults.signup.enablefederated.en.md b/docs/content/configuration/backend/dynamic/domain.defaults.signup.enablefederated.en.md
index 7a5d92a9..c6015fdb 100644
--- a/docs/content/configuration/backend/dynamic/domain.defaults.signup.enablefederated.en.md
+++ b/docs/content/configuration/backend/dynamic/domain.defaults.signup.enablefederated.en.md
@@ -21,3 +21,5 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter controls
This setting doesn't apply to the Administration UI, which uses a [separate configuration item](auth.signup.enabled) for that.
It also doesn't affect *existing users*, i.e. those already registered. They will be able to log in even when this setting is disabled. To disable federated login completely, switch off undesirable providers in the domain's [authentication settings](/configuration/frontend/domain/authentication).
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_DOMAIN_DEFAULTS_SIGNUP_ENABLEFEDERATED`.
diff --git a/docs/content/configuration/backend/dynamic/domain.defaults.signup.enablelocal.en.md b/docs/content/configuration/backend/dynamic/domain.defaults.signup.enablelocal.en.md
index 874d6dc4..f2dff20e 100644
--- a/docs/content/configuration/backend/dynamic/domain.defaults.signup.enablelocal.en.md
+++ b/docs/content/configuration/backend/dynamic/domain.defaults.signup.enablelocal.en.md
@@ -23,3 +23,5 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter controls
This setting doesn't apply to the Administration UI, which uses a [separate configuration item](auth.signup.enabled) for that.
It also doesn't affect *existing users*. They will be able to log in even when this setting is disabled. To disable login with email/password completely, switch it off in the domain's [authentication settings](/configuration/frontend/domain/authentication).
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_DOMAIN_DEFAULTS_SIGNUP_ENABLELOCAL`.
diff --git a/docs/content/configuration/backend/dynamic/domain.defaults.signup.enablesso.en.md b/docs/content/configuration/backend/dynamic/domain.defaults.signup.enablesso.en.md
index 6da28064..86bc6826 100644
--- a/docs/content/configuration/backend/dynamic/domain.defaults.signup.enablesso.en.md
+++ b/docs/content/configuration/backend/dynamic/domain.defaults.signup.enablesso.en.md
@@ -22,3 +22,5 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter controls
* If set to `Off`, SSO registration on websites embedding comments will be forbidden.
This setting doesn't affect *SSO users who are already registered*. They will be able to log in even when this setting is disabled. To disable SSO login completely, switch it off in the domain's [authentication settings](/configuration/frontend/domain/authentication).
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_DOMAIN_DEFAULTS_SIGNUP_ENABLESSO`.
diff --git a/docs/content/configuration/backend/dynamic/integrations.usegravatar.en.md b/docs/content/configuration/backend/dynamic/integrations.usegravatar.en.md
index 8509944d..b81d1766 100644
--- a/docs/content/configuration/backend/dynamic/integrations.usegravatar.en.md
+++ b/docs/content/configuration/backend/dynamic/integrations.usegravatar.en.md
@@ -16,3 +16,4 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter configure
It also enables the use of Gravatar during import (e.g. from [Commento](/installation/migration/commento) or [WordPress](/installation/migration/wordpress)).
* If set to `Off`, Gravatar won't be contacted.
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_INTEGRATIONS_USEGRAVATAR`.
diff --git a/docs/content/configuration/backend/dynamic/operation.newowner.enabled.en.md b/docs/content/configuration/backend/dynamic/operation.newowner.enabled.en.md
index a3025d96..d2a27b15 100644
--- a/docs/content/configuration/backend/dynamic/operation.newowner.enabled.en.md
+++ b/docs/content/configuration/backend/dynamic/operation.newowner.enabled.en.md
@@ -15,3 +15,5 @@ This [dynamic configuration](/configuration/backend/dynamic) parameter configure
* If set to `Off`, users without domains (for example, commenters) won't be able to register their own domains in Comentario, and thus become domain owners. Most likely, this is what you want, therefore `Off` is the default.
This setting doesn't affect users who already own at least one domain.
+
+This default value of this setting can be overridden at startup using the environment variable `DYN_DEFAULT_OPERATION_NEWOWNER_ENABLED`.
diff --git a/internal/data/dyn_config.go b/internal/data/dyn_config.go
index 8595ea2a..ad5b9894 100644
--- a/internal/data/dyn_config.go
+++ b/internal/data/dyn_config.go
@@ -2,14 +2,21 @@ package data
import (
"fmt"
+ "os"
+ "strconv"
+ "strings"
+ "time"
+
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/google/uuid"
+ "github.com/op/go-logging"
"gitlab.com/comentario/comentario/internal/api/models"
- "strconv"
- "time"
)
+// logger represents a package-wide logger instance
+var logger = logging.MustGetLogger("data")
+
// DynConfigItemSectionKey is a key of dynamic configuration item section
type DynConfigItemSectionKey string
@@ -196,6 +203,11 @@ const (
// ConfigKeyDomainDefaultsPrefix is a prefix given to domain setting keys that turn them into global domain defaults keys
const ConfigKeyDomainDefaultsPrefix = "domain.defaults."
+func getInitialDynConfigItemEnvVar(key DynConfigItemKey) (string, bool) {
+ name := "DYN_DEFAULT_" + strings.ToUpper(strings.ReplaceAll(string(key), ".", "_"))
+ return os.LookupEnv(name)
+}
+
// DefaultDynInstanceConfig is the default dynamic instance configuration
var DefaultDynInstanceConfig = DynConfigMap{
ConfigKeyAuthEmailUpdateEnabled: {DefaultValue: "false", Datatype: ConfigDatatypeBool, Section: DynConfigItemSectionAuth},
@@ -221,3 +233,15 @@ var DefaultDynInstanceConfig = DynConfigMap{
ConfigKeyDomainDefaultsPrefix + DomainConfigKeyFederatedSignupEnabled: {DefaultValue: "true", Datatype: ConfigDatatypeBool, Section: DynConfigItemSectionAuth},
ConfigKeyDomainDefaultsPrefix + DomainConfigKeySsoSignupEnabled: {DefaultValue: "true", Datatype: ConfigDatatypeBool, Section: DynConfigItemSectionAuth},
}
+
+func init() {
+ for key, item := range DefaultDynInstanceConfig {
+ value, override := getInitialDynConfigItemEnvVar(key)
+ if override {
+ if err := item.ValidateValue(value); err != nil {
+ logger.Fatalf("bad override for %q: %v", key, err)
+ }
+ item.DefaultValue = value
+ }
+ }
+}
--
GitLab

View File

@@ -1,84 +1,86 @@
{
"@esbuild/aix-ppc64@npm:0.25.5": "fb872b34a2843293dc60e809968fedf93e0d8f7174b062decffae6ba861eb56aaea0cd0aba87ba99162ceb2a690f0cde4fc29c000b52c035e40c91ec7861d43e",
"@esbuild/aix-ppc64@npm:0.25.8": "37fc14b17214c1f6bf41175029b62a43664a6a5a5b802614fe1d837bbf7abf5eaf2f6b735b6a446ebcfabb632e038c8ad9cccd87a259c45a1846689f8527874a",
"@esbuild/android-arm64@npm:0.25.5": "c818e799b19b5587466bf68a27b578ccaaf866c1d144573fbde7659e3fd3f555422ec3e67f5bd186a87648957d1b6e74df4f847edea7219c16979c9916f36e91",
"@esbuild/aix-ppc64@npm:0.25.9": "b5c49f119424bb3f7be30b652ef87bf881824ace02cc2327ba6f7e0f86d04d2afe2087086985f4220dfdbb816acb67e6dfcf00da9eb8b028babfac43df3adac8",
"@esbuild/android-arm64@npm:0.25.8": "e367e989238292ccee72013511dde1aef2d2160d8d5d669a12272f693cf9a0970fac9d7835178b3c46ed6936a0c4b29d21d58ed11851a3697bf98b4320be4b74",
"@esbuild/android-arm@npm:0.25.5": "a5384933f9f2ffcadce2be49da6ff43249fe42f32a04071316434e9f633fc20c8d4029072e9a53555620c3531045786297607b852579eee30b6dbc3bc9d98cd9",
"@esbuild/android-arm64@npm:0.25.9": "afe76da072b16e355f546d1d9023b814ead0487d9fa5e393eeb4bb3f6f76e5542e14b82396ca37c38e868ed64deef6b25f1c47e2564141fe5ecfe0eb7543f3f8",
"@esbuild/android-arm@npm:0.25.8": "cbfa2c802d8931e5f4d06582f20573cb34774ab713b4712c37eb15bfab6f90b693878b661de2a3bb9c81eecf45b37e0ddf2e9c79ef4ff932bbc37da588c40183",
"@esbuild/android-x64@npm:0.25.5": "8ce115dc7e1e6735f23b4aadb2dfca29c0abd8577ce34802ea3d017a64e388928949134fe225dfe190babdc5ec01be5fc7794eca84738cdefc12c5e3789ce43b",
"@esbuild/android-arm@npm:0.25.9": "0d4c724b84043db6597736865a4fe86640c88f21dc0ebe93a2298b4a0e0f0a5d1530a821dd1c18a2b39a6ca9abe8ff714b8ed5de496045a67150ea47c86a39d7",
"@esbuild/android-x64@npm:0.25.8": "1d4b900dd2f43790415745d20ae6cadb53e9412911578aaf43462277169c22800eca1f49a9f8ce9c37236e1691279494f91967d28310720707911910ec765013",
"@esbuild/darwin-arm64@npm:0.25.5": "a009eab62f2bd284a6f2001d5e08217059186ffc16907bbe873e1de40fe9b5ed92c0db2f4c4d0dc41545838850a430c8f2f35d7bdb9cd01a1a04293acd97afca",
"@esbuild/android-x64@npm:0.25.9": "9da8b1ede5670c2c9e644e0fedf1dd0747081272b296d3f80b53b44b637744d836efee00dc767b81940eb285524a5e3149d8045787f6281ee2bd53f61a9c8717",
"@esbuild/darwin-arm64@npm:0.25.8": "a8a50e303056e668e99370a88d1744de4a83e62e2f3f7fcf2ff611142346505229568b0ec5edda93ec96e33e842a585880a312790553202750f123d9636fa97d",
"@esbuild/darwin-x64@npm:0.25.5": "cac8021a7a0c549263e076913346b35a5bb81f76ffbc1abfad5e7b67303f013ac0c76f111bf624ea8447b327ec86c18a60c6ff307d743a2269f5d47313f5b2de",
"@esbuild/darwin-arm64@npm:0.25.9": "84a88a8f72fcc66381518d5821e4ed276aeea17c76559bee4fcb472b19456da630ad84d8d74b3e4c297ef7b1ec5bd5037d55ac6ef5c515a77fb94a1bd891dece",
"@esbuild/darwin-x64@npm:0.25.8": "9806fe9d54f3228a01f535e7c51aea26bd1bab3c5d64d5f77f4606de44f361f049222776d32bfd262d45991b7aecca645ed576ea338edbf4f8044b22b3e331ad",
"@esbuild/freebsd-arm64@npm:0.25.5": "d248e7103b7094eb4288db7c9a78b2905a25b4a957f2b945531ca88d3394f45ceca2343a7c84954734534af6159bc741eb3d5c1ed9df990f7395337a1b14192c",
"@esbuild/darwin-x64@npm:0.25.9": "c500bcd0c1a8f66d19cf575299a7da7f29dbdb56beac95bb6ddc570291e3e9da4e7d31db6453fc19ea5ac7f85662f40c9a3965c3e1f49657efeb292a6f601a26",
"@esbuild/freebsd-arm64@npm:0.25.8": "8e6cbdd45819390ecdb62a70a4f119a9269a90895f3e1237788b36a512248a756233ef59f55f9033658af372a196f0edc3567f078f1387e150238d2bd51f733b",
"@esbuild/freebsd-x64@npm:0.25.5": "8a7be0740f07f5dbb3e24bf782ca6ef518a8ce9b53e5d864221722045713586d41774cbd531df97dc868b291b3b303c12e50ca8611c3cb7b5fe09a30b38285eb",
"@esbuild/freebsd-arm64@npm:0.25.9": "fb4951968ad62e5316ffd08c037bb2396e8119a0c2289e968421bb277bdbffe23d89c92dcd2d7eb680f31be2b05e36406211b141b9b89378b424e4b5193c3d7f",
"@esbuild/freebsd-x64@npm:0.25.8": "3f920c686037f825859a2fe82104085f4b254b77821cc71a71db512ef0679dd01481c136c3f7057ba7250daff2458aa3ffd101cc28cb5fff2d55270ba5930ec8",
"@esbuild/linux-arm64@npm:0.25.5": "ce3c8fca47cf0a92148fb288eb35a5c4a4dcf7a700730b3a48fdd63c13e17c719eb6b350378203fba773477eb5be637f47a6d52c5d4ce5bdc0075ee917156006",
"@esbuild/freebsd-x64@npm:0.25.9": "0c71856891d5cea255f9bcb4482c70347a39198da2769a273afbd16bc9db7176bc1d28c41dd569b8b8f98cfd14b10a728c79f54311037d702bb7dffd95f27740",
"@esbuild/linux-arm64@npm:0.25.8": "234edc9f815cdc74d21c6a90a3542c941deeaf3a24b408c74a4651616bd270383ba5a15eaef837ab347a374032c7028fc29e4f1da0becb33f0b8dd8f744934d7",
"@esbuild/linux-arm@npm:0.25.5": "cc81ea76ab86ed2a837c9da329f7c63412d288dc0aa608c8dcdf51705dc93d5b7f966a429be4896babe611074e5898c7e6c8e07ad7f50123a05478975294fbb4",
"@esbuild/linux-arm64@npm:0.25.9": "6c64bf50dfb709d4000b022558293764a20ff8ba0b6638e8abb3bd0806cab25ac971f1910eaf2a4d73f92080afdb296d57175b2199647de019c2f419365f839d",
"@esbuild/linux-arm@npm:0.25.8": "dc6dc225ae278cb3383e11d9829d22f301e1b79f2ed4efde1a01896ae67e45efde98caa61f10cb425a809e9b61e9a4651b60d2b6a3e9ad6174519e8ce74bc02a",
"@esbuild/linux-ia32@npm:0.25.5": "bfed6750923afd56148f658f6ec8995479f5115116dc212ecb9e4c556064422e22eda855177e7c02cbc945494e4db1167101918c5fa932278115db2c7025a3f6",
"@esbuild/linux-arm@npm:0.25.9": "b9a473988dadbe98f1c6ade2597e5967371d929ac83bb9f888d726d4f0e5cc4b8fe5020332adb26d61748619bf3e62c831d9c80b3bf815a6dd90dab76283d533",
"@esbuild/linux-ia32@npm:0.25.8": "1c780012035552e27adea34d11f959a3ddd4a4d576cddd03d320b1db18110e777c1adca2c6d10affd587a4454900d3ffcad9371956855e56739babdc2e4edcd3",
"@esbuild/linux-loong64@npm:0.25.5": "e5c20140bbbdba53f0d86dd72961ed73e6255d2ada2d3a626f390b352170605644822ad7592f695b6e520edcefe0c5f6ba19d10694b5d11d725745d9792bde01",
"@esbuild/linux-ia32@npm:0.25.9": "51f458a7038e2ee014b994f7f216821194b00716ae5abe78fe828d9cefd47575dc0ff703f95c18017e59b7bcb63c13ac6d551e8ab64522cce8af89bc33a689d6",
"@esbuild/linux-loong64@npm:0.25.8": "d3d39691d301d144c7d61f52163a2fe64caaf928f4117d906707dc1456f3d88d1a7a3b16fb988ccfc0b0bc203f4bcd56665a9c7405dc380b3165a26ab195b9ec",
"@esbuild/linux-mips64el@npm:0.25.5": "6b3559517efd0dd1301debc7af7e275b055859c26facdda2e229b1aaab6ebea4c480a1da151c46211ee4035d95bfa7f0cdacf735b57ee99d41b69c77357310b9",
"@esbuild/linux-loong64@npm:0.25.9": "81478bff0f6d54e06fa96ec120c2dc92e47c3a1392397a121cf50a83496156f9abebd46f93e35a496f4f305c8af7cd430e4fe723474a2420cc21d39257852210",
"@esbuild/linux-mips64el@npm:0.25.8": "437e51b2be977cf7774114e04c141e3c0f1ceb7f12b961b7b3ac7f99c4e203afdd74c41e072ecdc4bab3cde4f14feedd78653727d1b2013ed3611bd89117ee8c",
"@esbuild/linux-ppc64@npm:0.25.5": "a1a1af99d758efce928335637924dcd8ddec4201af51014e1f831b012d53a0a673b1e0c31036ec9e8c5a0311439283419ec8abdfc67ecb245fa7f7b653006ed0",
"@esbuild/linux-mips64el@npm:0.25.9": "78709795d663461c54168719517c4c38b2a51861af0f97a91003ea6fe2c2b67dca77f57c2a2c4eb2c6481a8660b5fe477c72e46e90154cb72f1f235e683b2d0f",
"@esbuild/linux-ppc64@npm:0.25.8": "29d2e344b1c8b767518d25b23eb9e98d85deae1f2def2e01c1939536ac7d1fc9e92749a8d29b29277b3340d3613e4b0f96213c6aa2de7e06885a19d3d269870a",
"@esbuild/linux-riscv64@npm:0.25.5": "6cd8dce6723b73e0f89898ab6cd52e0d009afdacdfc0d5529134de7b832c92c2e0421fbb5cbfc0e0c0b2b00a9b1ff2c4cdb9695b2c535ebc174960e986c727a7",
"@esbuild/linux-ppc64@npm:0.25.9": "4a43e167f7f9659a5ca34678cb6fee53fbdba8b14a9a45f323abf33d9e141fd268984b0a18445db84cbada5ed2ac211ef318d5c44af0fbc0c7eea31c0c82321f",
"@esbuild/linux-riscv64@npm:0.25.8": "82b2ef7fd5a00b465da97bd797246269d7460ed710c0533517a1f8ad8e32527f405509b2ce27e29f8f3df1affa04e45cf5d1a71205f69dab5c1a27118cf10fb8",
"@esbuild/linux-s390x@npm:0.25.5": "31b86dbc93d19eb362bad3353e65d6da771118346e723582d06c05f1b6ffad1c3765001b5215ef1e8f0c2bb29130d98815359bbc88e5c08304354d5a92e6ea94",
"@esbuild/linux-riscv64@npm:0.25.9": "e71027660a884b5bbcb6e9e84ed439b3ef19466cd9f6d16c1bdfe2db6dbe9a40e8fc19ad429749102d1c15f015372f7f9f8ab7f82d506efffbd143fe8ab28aa9",
"@esbuild/linux-s390x@npm:0.25.8": "74168a6e8927d12c883dba56006f5277f8888c7b1b5e4d132a3c235b8629c3015b4715968ba128a79ff55c9f08a23df84fe44047e8cda4366b9699c5c45f27a4",
"@esbuild/linux-x64@npm:0.25.5": "f878a3e40edfd8a50de94bf982a9eaf03e636a0332af163a6c905490063aae652384fb392d4765c4338fb6f991034949c92ec768ee65c3b2fceeb494b89fe8b3",
"@esbuild/linux-s390x@npm:0.25.9": "7004c5852a3cbd1bd1d727e13b2f2be0747861ceab840ab8492ddff80b3d34dd41398681016cda3748c697e1b384bf4d4c1ad588538df38eba599ba935ecbd09",
"@esbuild/linux-x64@npm:0.25.8": "d531002ac2ead0bdb293ec1a4eceea687d37815e298196af2471107cdd4c1f76ef7d12417052b51852b80f66111abfb5ad8375c58b97da92306b975e9a8f0649",
"@esbuild/netbsd-arm64@npm:0.25.5": "941c5e28a63a93f19122271b5490e196db12815702c2266c6d66401b6909a4364ab889611ba81c5359624e3ce61f0505a680a1179ed9a555d1415fa1c485d75d",
"@esbuild/linux-x64@npm:0.25.9": "3bab69aa63d5ef65e9eaa229963ee3a3c868a06badf019277af06d4f7c52ea3a30abafad361e5bcce71032abd8b6fb666607977a627fa2def8db6756cf99b2ee",
"@esbuild/netbsd-arm64@npm:0.25.8": "55626924ae946a6225707062648aabb79c70d61e7e094b067338ea1adf72493b502e99e59440fe0d3abfe20eb36c33f78115815d63e72fa99f5e90146c2ee5d9",
"@esbuild/netbsd-x64@npm:0.25.5": "edbefdd88ca24a373497a7c8d1fdab418827ff89c6eee1c574159dbb4d9174552aa87753f35525a894964b77c14b012164ec5582b9f19dd4d6c1f5d45df411c7",
"@esbuild/netbsd-arm64@npm:0.25.9": "02b82c6773ad09082e3b18f01dbf4b9f72519a9e93995b479e4bd4bb28c8a4088972fd0e9304881d09775429dc388a3625249096426349dc3adc2208ee408a4a",
"@esbuild/netbsd-x64@npm:0.25.8": "d03122aaa3e9a8bda686bc4120820805b5d9701099458a2c928ee1a292fabcc47df0cb178c8c428edb78a058e75cf7c0d80fa25b71fb91db43d73fe6e4062c41",
"@esbuild/openbsd-arm64@npm:0.25.5": "d44633a374c109d2fb9c678882016e3ec3d79f0c5f21a6e6fb0114ea709bc539200b037a4e3ec52304eea2f8c5957bf16c6f0a7af5cfde41b652c4bac604bba6",
"@esbuild/netbsd-x64@npm:0.25.9": "51a576cb8ad7f43e43f76b25273735646eefa0d2a5bdaa3bd6387ab89512940bee49e91f7051b3d08d143d5cd6b4501f72a9462f30e242f0dff931c4ebe40363",
"@esbuild/openbsd-arm64@npm:0.25.8": "113ed8722788986b5b703c791bb9c954e80a861b92f453c66c79318d71cc6eac509c1dc79d20671b4af92165eee05a28eb7b3122537d8701447d30f58c428942",
"@esbuild/openbsd-x64@npm:0.25.5": "efc4641ea653dedc9886f0603c2e7cfc6fbe94c34d4cdaee9b060a8b9d8143d1192c45da93b3e802af2c26f72ab1ad3a3fad0e0cb297d06de55814fe83ccd32c",
"@esbuild/openbsd-arm64@npm:0.25.9": "9bf0e7a535304f0d3c700d6e4ef536fe94b17e05bf5f9a4cc308ffc8c3d3cd642ee7debcf7f649a845610b1c706675af903f785ef14f320ee918faa61bf79bb5",
"@esbuild/openbsd-x64@npm:0.25.8": "dfa68d80d68ae825de85aeccc118724ced6b232dcf25da6d862ba03abda2f55e75483dccbf8cb3a7338e7882a05e5425fcf5a902b7dced72c9f1a9c2650912bd",
"@esbuild/openbsd-x64@npm:0.25.9": "bb9bb4c5f095575f267d30c9e65f8e64c3f425d24904f6e22112c305ee5f1503aea750f510252e4b862f366e5efac678b71cc848cda72c587be8af4119d28f80",
"@esbuild/openharmony-arm64@npm:0.25.8": "8dab5710d93ad4a78a34a0016f6ea0bf2e16489845f9895ecaf354c1c3db209bed8f05a31309b95c358bfeaea53829605f4e315e9a53dae4d9fdb58e31ca4688",
"@esbuild/sunos-x64@npm:0.25.5": "29860663381b6098c0fda6f69235407654dfad953e83b3f9f06a270950d5c37da4ca60a4b5915b8e2606d468b560be6179870f64a22d5b046e8a930c31a7b554",
"@esbuild/openharmony-arm64@npm:0.25.9": "130f08d72a018aceef14b8534b9910bd2c0852fc074467ad0adf54d339584277326c7ffc8cd8126be08a530184c51d8de11d06971d1cc44545162bfb8428bcc3",
"@esbuild/sunos-x64@npm:0.25.8": "ccc940bd687d1f6d320d2538ac594b7fe5e291e194380a8b392dd2348d738cf8d322f9f62bcea82b3809f98796a0a004cd02ba9c4d563e5e336665e1ec8e1e1d",
"@esbuild/win32-arm64@npm:0.25.5": "a77d395251c8a62ab0cec07d5230222823fa02fbf3ef008d94b5213a335c9f949872c3f1c2f947abaa28098b669018e429af42f59616e049860a0072f3b006de",
"@esbuild/sunos-x64@npm:0.25.9": "a86fca2baaaffcaed419bdc6569cf4a88b4ae830034f8deb4d83ba781b989e25b03696cfe05a482e24710191abc53479b70491f3f157eff37ed2b486d88c897b",
"@esbuild/win32-arm64@npm:0.25.8": "b0a9a86548d4a62e68b12a89e21aaadda3d6d3e96541a2714b74df370cc344e1a2d91604998a26951da28c2f932bd2ee033adc9346bb232622c3ac419107136a",
"@esbuild/win32-ia32@npm:0.25.5": "ff1b6cbe835082aef5b93c3e2012d51be431d05c6ae5f90a5bc89687c687e8e2340c262dedddd124b27b511616bbc4088b5a4a949d3147f677084dc6ec572629",
"@esbuild/win32-arm64@npm:0.25.9": "cd32c8fe88511e413f6161480ea3b6fc2ed7cb4bd2febb455e7bc45842880c752ba71772f289908ded50d8cfd3207e6730c8fdec2e811af1f65e97d1ee53b4ef",
"@esbuild/win32-ia32@npm:0.25.8": "5880e933c8fb8dc1de1225128c171ea64f4b27fe52fc11ed9cfe6b0ca8ae091c2703d4cb629f08c06731810c46f48cf881516d0d54b3ac408dec34586ea84d27",
"@esbuild/win32-x64@npm:0.25.5": "266e69e8d37bd4deb77443588e49472e4e9791178cb39e1692eabb67cf65d8e85a932ac468e7ebb2072c8a9ee23ad413c8f0f7d954c474f643cedbbf7aad952a",
"@esbuild/win32-ia32@npm:0.25.9": "c374576d857dfa3c8dc5bdeef598ffc0e3adfcb8b7986a2b78ce61d2aa3c3d1f1cd15cb685f52b88411403d974f657c3fd44cf9b4fadb94e8ed69d75e9e3e0ef",
"@esbuild/win32-x64@npm:0.25.8": "9e98fe0e7eef7a0e774ab761c59d520ea1c997a7a6e4c7f9cbc967471a4a7ffb14bc27c60d2aa10796c4e945c3da2613fcc297054566fe3f5191e1250691d622",
"@lmdb/lmdb-darwin-arm64@npm:3.4.1": "2ef08ba9fbefcef1b8623c2892021fa4e7c7a148b7b50c0cdb824e1f3579806bcd73fab50ffad0deaa8fa9e3f7aa98db81a2db84dc22c48ae6cc4e68d48c672d",
"@lmdb/lmdb-darwin-x64@npm:3.4.1": "fd60bdb8198f17914efb8a51b198fe5e44e111a3b6b1082ec1f76aaaf02ac58b70b34950cc3ff5c289db779820908fdad0edb7f76addddc0ef841d0f2c8a084a",
"@lmdb/lmdb-linux-arm64@npm:3.4.1": "45c496e8784e82ff19a87cf54c9e988fec9c7e160558e9b3d4cdf9de9ee3c80def506303cfeb70226f2a614f898f9b062c462bde220209ba559f152cdfff1601",
"@lmdb/lmdb-linux-arm@npm:3.4.1": "6542a3c7a389d5e43955f2a25314b1aea619156b6fd7b6f46e27dcd24949ef68d91fb88cbbbf41de96e83856c44fc392fcf0f98d3c1b43b8ab65f67eb70fc3f8",
"@lmdb/lmdb-linux-x64@npm:3.4.1": "c432e8e30b7895eb5ad05dae1bc0d5ad59c1f220c717d68ac8c34201fbc380500e1cdbee33bcf2d21bb10aa9f32aa42234816205740f4e12e737fae8466c0028",
"@lmdb/lmdb-win32-arm64@npm:3.4.1": "95148c68e73fd25914e65ae129ce822679d8ffb952de771fced47364c42f36909d3c3f3ae66457450a6eaae03428ecf9f689d68674d335c663444f6e9eca07c4",
"@lmdb/lmdb-win32-x64@npm:3.4.1": "6b25b8caaa41e817bb9e278d26947e49b8669ef1c6d60595b9258fd067bc49f47ed7890b82acbcfcca79bf98661d3cc00eaf26f852c5e8f89db9346c69b2f6f6",
"@esbuild/win32-x64@npm:0.25.9": "41f2ba9101f4a9a28e3287026a32a05e8fdffcb4a4e41cfaf9f94b41093c6882f46ef80f12854b67b7ad78e47d1df492f3e8a71d41813a61500ace4a574af851",
"@lmdb/lmdb-darwin-arm64@npm:3.4.2": "318710007b94270701e8fb8321a1629e12f4628b943e017d1acab1896774b3974397f0972d9d603ffd3e129a68dc1ac0031f99538923e314ad6f4c8c480f4972",
"@lmdb/lmdb-darwin-x64@npm:3.4.2": "52b1afeffa796dd0d520c21662f428da39b29f76370d014581805cde274912e3e752bd80444e253bf1aee65c75238a3d2a98588f9d22842aa7ecfaa82ae3e3d9",
"@lmdb/lmdb-linux-arm64@npm:3.4.2": "2d938b6a07710714f25a29d4285230e2f4999b6d5206b74250ce65deae7514dd9dfafd86481f8ac5c5e2baaea56413558279f0b093e4a83e2f98063c8ebe75ed",
"@lmdb/lmdb-linux-arm@npm:3.4.2": "5b21eda51f3b993a9b35ed0e6093d3240c8faf4c1090a500ae01c7437d5a94d0db8f3d58f2db05a9b8236e2629d1d54ce35533c0c9fba473b40ffea4bf19a7c3",
"@lmdb/lmdb-linux-x64@npm:3.4.2": "da1ccac62a108b7db7d719a14d5ba5f81285b4ec1e3f841400477ef98428940f33fe6a175b442df3a97206fec9dc1d611d4b23f03ac5ef0db0161b8de4629d77",
"@lmdb/lmdb-win32-arm64@npm:3.4.2": "cc300aeca06ac55de7eb24c2047b920cbce0a6f646f1aba1ed5ecc8cc2bebc92f9f82975e7ecf80977db30636ab7c869108e79b91c4b5c59b771055b09cf9e12",
"@lmdb/lmdb-win32-x64@npm:3.4.2": "8fae43fa379018653c307f28c6064099e4f8e19439f4cc895d7f29d11dfe1969e99d0da32b167820304154758a23b30b2dc2c86f71b3590e33bf837cf64ae46d",
"@msgpackr-extract/msgpackr-extract-darwin-arm64@npm:3.0.3": "f8d48613de17e5356d273745042bbc7c0d345e1e4e5e88ed1132a3ed9ca65db9f8b908a708383d7a58ede276c105e7c9a2887fad00d5e47892010466e5312b47",
"@msgpackr-extract/msgpackr-extract-darwin-x64@npm:3.0.3": "270f8831d7f6d61ff36e7118e09ec13baa633bb22dd57fa3a9e6af67f7036a3fd1beec4c8aad1c72639522300364cef98bc5ba2cddf67a3832f8a35b7fc45502",
"@msgpackr-extract/msgpackr-extract-linux-arm64@npm:3.0.3": "326c19865ead39017326dd8066cde30adaf87a62b968a22d5e5369691817086f681fc291be80b7c61c5496fe915f6df546f41e14ee18b896c54de5099cf147ac",
"@msgpackr-extract/msgpackr-extract-linux-arm@npm:3.0.3": "115f627bcd7c290ddfa1d16405cc1ce388be0b295a26adb1ee2d774133a77809aa0bae83775d3c11f220544e77d34c01d607037bf9dfc6aaac67329cb5fe2818",
"@msgpackr-extract/msgpackr-extract-linux-x64@npm:3.0.3": "ab74755aa2afd27ad8f5277c7cb7a0c721028ba63c3e3109458689340d76ed4c901aa2c71ae5950f3a2149ae9e835e711a12842db219864107cc0c5c1fbdc0da",
"@msgpackr-extract/msgpackr-extract-win32-x64@npm:3.0.3": "edfea4f5455f71f608c15163467b98245d46fbd6078019347418e193968431afe632a11e8729ec39755a54563d86940899e2fe8f3d124393f91855956bd1d745",
"@napi-rs/nice-android-arm-eabi@npm:1.0.4": "45b448e131164f3a8cbb31a2a5f55bebb7a3d5f5331d29e18f7aafc55b0b0153f19cee4176095a37d3b2bf64073cb1a3e82840d73171fed0282ea22fd167b556",
"@napi-rs/nice-android-arm64@npm:1.0.4": "d32f04cd8028da9fded9dc0f85f39280097b8fe47ad13c80924be4a41a4f85426c97f9928ad3e7532d1dd976ce4ffca46db1d41c4f938ed1e0419963225b2d13",
"@napi-rs/nice-darwin-arm64@npm:1.0.4": "6599fac84bf0f11a1e3cd290d182bc7cf9d44ca437e8db8a5f68cdd127f8003626527ca3a9a4246bef2def8450dc0675b60f919d8d613631b63ead3dcfd1b4e5",
"@napi-rs/nice-darwin-x64@npm:1.0.4": "fa86938095b1b26add66e844d5c890a23a36f52eef3dfa26cfc4f96e085a43e3a9f3cd21a340aee5824f3c0745291474822a4f16fdf4ddfa11bf59e2fc2ad1f9",
"@napi-rs/nice-freebsd-x64@npm:1.0.4": "18ffc677b3e38686954dfe46974bb796203f98e604d8a611ce86e1a73135b46a1ae031cb96df64e07fa3c68772465f15a7cc3146d22399dd0428022ce16131b8",
"@napi-rs/nice-linux-arm-gnueabihf@npm:1.0.4": "08691179d26f4f7f9f12221f40aa16051a945b52e58b6bc8f243f0fe8f17390a1b75ecc5da3b6bf0ccc3cc1b429703852a2398f60227024481602a8090be9eb9",
"@napi-rs/nice-linux-arm64-gnu@npm:1.0.4": "82a0503b880fa72f8921a81ff22a5257a8a7782ad30e097cbbbcccfe5021559d29c01ec06235ba1d549a18ef1b2ace6af94b5ba6d55aeb39afae237ae57b4f21",
"@napi-rs/nice-linux-arm64-musl@npm:1.0.4": "26d52ea8cdedb140f594813c8d8d6c513511759298a1fdaf1cc1adee3c562b0be41fd8f5ae98666f023ed20d80d6d37ce3f71a7ef372bc30e6fbbf4f5805a2e3",
"@napi-rs/nice-linux-ppc64-gnu@npm:1.0.4": "037cd5d190f71c867fd370e76d2721c12ba124392d71f52773a7668dc1f61d642b05c3b9045ce759571af7251c9b33627177b3dcb8f0f49cd71c0e4a64510ee1",
"@napi-rs/nice-linux-riscv64-gnu@npm:1.0.4": "f34a91d10e9799d9b944c1ad33306d93e4f4c0182ccbac945b2326d5e533e987762bbba12a05ad0b2cbb7651ff004cc16187a9b87fc0e0c0745d6045552c4957",
"@napi-rs/nice-linux-s390x-gnu@npm:1.0.4": "1cddeeae3b28bb773cd17b43df08612fa33cd9ef35d9ce77ca59f45912d676da2900585c05c74fce504ce25771fb7828b109910f9b2e621b56aca9c0ff9a69ee",
"@napi-rs/nice-linux-x64-gnu@npm:1.0.4": "658a8e6ac405f9ac56cc39e356561d41dd2681f982aa2f873b30fc8ac375518f8aa428f3ff6634a82183deac9f556ebc333abb34f4612cd7d115e05e1e78bd12",
"@napi-rs/nice-linux-x64-musl@npm:1.0.4": "21e2a6d6bb927fc2dab2aa9bc80c1546c68b7b2f97831467ba3f716f27289d2cd0643697cd616e61ea708b09b424087a55161d18f350c6a3b00435f7fb719e1b",
"@napi-rs/nice-win32-arm64-msvc@npm:1.0.4": "004988d067b404d74ff3cb87de9e3bd6dfeb19c1156ea46dda361753daa91671e634ecd58d97e3137f81a969bf268c0551b86dc4f10708c285de9c8de71b0566",
"@napi-rs/nice-win32-ia32-msvc@npm:1.0.4": "a5e5ef142b4c361d665bb39ce82b4f8fdc685a59dca0696436fed60a2ad65f399451afddf57d82a4b82050a457c1802c04c826407e6e902f4907b5e53745cd05",
"@napi-rs/nice-win32-x64-msvc@npm:1.0.4": "fbd1ccc22daf769b179656cab2cbcffe0612dc859181ae057509d50e4f10787d00c90277ba949084ce7c01caf9251f8cde8a198694bc8b5aba57e83c49ff8cf8",
"@napi-rs/nice-android-arm-eabi@npm:1.1.1": "a1f8caf7bba36f2131303d7bee53e98f46521e499f4b1ba25f5ab770d8acea36abc1792c53e5c6db4235ff1873ee7c94d2ab2f38c8d492ddbdf47fbc6e92c423",
"@napi-rs/nice-android-arm64@npm:1.1.1": "5f623438ee64b9e9df5fa8f7b6533776e9774e8edd2aca6945f85869a4f3232c2f30d3a69ad1ce54e775e4747085e57ab473c8421551bda65d809ab65332a8bd",
"@napi-rs/nice-darwin-arm64@npm:1.1.1": "733a2d0b606efb52d5532c9a00c54e8c664ecb794fba302c14055d0145baa02b5a50459ef07c171416a5f941e2779cef7406181df7e7969f978ee1d1fdcf2b5f",
"@napi-rs/nice-darwin-x64@npm:1.1.1": "f99af3768f04db9fb91d54e460e4e315242d3d8ff3ab0d013c56f4bdf62497d223459cf41554dc0914d11e97f39c2b154c517c3d5996c0ef0297d18702c51b29",
"@napi-rs/nice-freebsd-x64@npm:1.1.1": "0d317f65d4d07ae3c8d5d6a424b6b10c599728eef8d58f89ceef2491dffb849bf5744b0eb59c0feeea461b1fd4959db676dc471cdff10ce1304d06246178ce0e",
"@napi-rs/nice-linux-arm-gnueabihf@npm:1.1.1": "ed67fd70224048f84401cc1167210d2ab306eac252a83eed96357c49499029205b83e1cfdc606427c46edafb113c0c17613a5de7f122947d4ed6bf3cd7663287",
"@napi-rs/nice-linux-arm64-gnu@npm:1.1.1": "9477d397afb1df69647adb449c0d05300a99abf6038a3e2eb63307908375b9a6fa271fe5f7ed211b1b55e87dd9c6ce25f41a6f0f270fa45af13a3a84f546be72",
"@napi-rs/nice-linux-arm64-musl@npm:1.1.1": "a754ae5275a306f70eb621e855f234318c9b4db323e10dac3d180c1e66953cb5505e8a1ef13677492839f160c28cc14583b22b3953a061d5b42a7cb7b0dcf42e",
"@napi-rs/nice-linux-ppc64-gnu@npm:1.1.1": "91c96752d3b0c57156dfe0885b8124e933197a3a84d4f312986721b37abc1f129f9f071b73142c2074d7e848bab8b8a16e6d246e8c355fda9ed6817ea6450330",
"@napi-rs/nice-linux-riscv64-gnu@npm:1.1.1": "e898401c486a5b637426562bb93b12609349119812273b5c19ebc565e516c1246043888ee2c5d24c6178c93288a761284813e495c026c74466f832ef5d63d906",
"@napi-rs/nice-linux-s390x-gnu@npm:1.1.1": "0ef13c6beca52613a76c38f70e60ac4959ad08f9d898f64036be8d45ad8266e1f4fc64cf1ba669e55a5869c64a3c2c9a9ea4d4d3836a5b6941fb147a7ee0abd7",
"@napi-rs/nice-linux-x64-gnu@npm:1.1.1": "d5ce16822498a12d29c59d5aaa5af4346bb9394d45342706392c2e27915dea9cb37692f0c7fc69dab8c65d31556dec851d631a8d557598bc32d57c3788835dc6",
"@napi-rs/nice-linux-x64-musl@npm:1.1.1": "debb8a7067c1d20ce4c29c897e470ef84fa8f66014c0c8e7fe66d6676d6e9199a0936ec3cf3098f89ca1fa1f2628c27d32991f933a7a33da304f1baa45af0eb9",
"@napi-rs/nice-openharmony-arm64@npm:1.1.1": "086899cc30d453380399afea4e827cca1428f9cdfc7e253289b0fd41a84c76850315f0070432a00c3c9f80608ac42d638ad836fcad742f437c1fd9465d8a83c4",
"@napi-rs/nice-win32-arm64-msvc@npm:1.1.1": "653a7b04d9caa311d793f12d2c2323830a0863737fa4e0cf4ceb5adbb85d30b29d02fb59e5d513e5375273054a965777c83732b25592e80a574f15114bdf96c2",
"@napi-rs/nice-win32-ia32-msvc@npm:1.1.1": "f32fcebde27d2e8022e6ea2c8a4fad0676b59c24373c8f7e6fafe3711932a743798909122937278f94c42cba952f5403ed1dc1879544e508299af88ecbbbd68c",
"@napi-rs/nice-win32-x64-msvc@npm:1.1.1": "657bf5caa5b07b16f39b3fca7d7f99a0609a01ffbb620ff167652145c0e5c3ee1bbc19f4ed09b9e4373da8a1f84870b58152bb6c44288a3e9cb46ca8a236cec8",
"@parcel/watcher-android-arm64@npm:2.5.1": "f99d569e4f6cf78a1b0097fb9d4682cb201a74370ae440c531da4e1d5021e46141bfcdf8ef708b51a5b9cb1c30f78eea933ce75216d5eeb7b969a2ad27c68e4a",
"@parcel/watcher-darwin-arm64@npm:2.5.1": "973c7ef3c94608da9cd1b20b18b9a7de2fb46fe44553731fe372b640de524491976150d0845f3d5953b74ed8ea469cb8d18a48651d0e5fb82f549a6b46b54f79",
"@parcel/watcher-darwin-x64@npm:2.5.1": "848c5516aed9c36e14751200dbbf57e83c0bd46cdab0932df33db120e66b9596de18eeb98980e319efde84014f67d9e7924d7555383d8ffcefe35c501166b84b",
@@ -92,45 +94,39 @@
"@parcel/watcher-win32-arm64@npm:2.5.1": "e015314d6b9b727cbe25eedf963ca8b23bf6d4e78d3c28008bd0d2657940ad54a271330486df3a93a5f1a30f2b8d052d14415b85cc7e7b747c6c73b5dc055628",
"@parcel/watcher-win32-ia32@npm:2.5.1": "920b6ad6a2095aeb9c2d329c5118472a3c14669fa93eaa99aa8050c76c5c2d3d76d92677167ed748c2ac5487c568d5df16d5d94f4bc7c354094fccd8e0d6350c",
"@parcel/watcher-win32-x64@npm:2.5.1": "8f1c8e41ec9f86e4dcd0d4db0a077742d5dcc853f15ea888387183e34e2efcff09fd1cc9ec46fc1121b9ad4ddc0e221283f2ffb23cfd7dbcbb8b03060b461963",
"@rollup/rollup-android-arm-eabi@npm:4.44.1": "ec8b655e2930312fe94eded1cfe439901bbd442a891494a512259caa0bd936c29b9d06debb061bece88f5d41f619ccb4d0d77d0fe190f1a5c25a3a1a724d255f",
"@rolldown/binding-android-arm64@npm:1.0.0-beta.32": "f683651ca6732c155432bf11ecf88d45231ff1a81c8b51697efaad11f02b00fa2af14d042099d8caf5053ff9741680f4b476d63e93cbad155af067ab51218239",
"@rolldown/binding-darwin-arm64@npm:1.0.0-beta.32": "4ac2ecafab76c25edbfa0fd70fe46647d0b9385b985265be8208a3dc9d65910ab8c352a328e58b41c2d981f26d9d1b48e08ea087d986707bf37891736d5b5d57",
"@rolldown/binding-darwin-x64@npm:1.0.0-beta.32": "877ad1cd46fb411598a11cefd86c5557dc32037d1d757869dd520c05ada993424364012c0d0b65b221a15b65569fea1bb997140272e5886d1f0332d29de22cdc",
"@rolldown/binding-freebsd-x64@npm:1.0.0-beta.32": "a441aaaed1ca4954d7ec1c23c42d57faf926a93028cbc8e212392f15dd545e86916f8ae814dcc24f667225259bd6b7ea69e3ff1921f30b430f48050a026a8995",
"@rolldown/binding-linux-arm-gnueabihf@npm:1.0.0-beta.32": "60a7a54ed4271e4ae24ce74f1d91f7f75122036374d8d3aca276ffae31de5b06b4c700226143d7463e24a8fb19c0d9a4cc48b14a8b53d1c03cb5555037e27899",
"@rolldown/binding-linux-arm64-gnu@npm:1.0.0-beta.32": "280bed83133d9d055156aec07fd212a5f804583b2f7a50fe6282c3e51f82fd643aa4affa17f8b6f9040bc3f0b5d8de5c3bf0b73f2471cbe3c80e413a32731663",
"@rolldown/binding-linux-arm64-musl@npm:1.0.0-beta.32": "e13ada56d9168b07d9c30d35e52e66d94b882e872fb92c65cf7da25ecb80c2b776e35a47cf0a739d6352fb386952dcb0918f5ff4b823e88aae8728d3b4201df5",
"@rolldown/binding-linux-x64-gnu@npm:1.0.0-beta.32": "452b5d9826aad34b593f83249e8de3a7078f79837de7aae9e08865342b4142ea6acb4c506fbf637b355e4f9c38d92acace1bb7d9389fe9be8a7cd5a40e58953e",
"@rolldown/binding-linux-x64-musl@npm:1.0.0-beta.32": "06fd13ea6ed94bd92a00dac8f4a7856e6d2058b52cbf8f43743f50a245bb3c0aadc571092233a4914e7772f0f8fa538b1eb66395c88e44610f4509676c87706e",
"@rolldown/binding-openharmony-arm64@npm:1.0.0-beta.32": "fc927ae8f8a6209a463d55c09782ff15759960a69af5fbd45273899fbf9792be5266c5a799d8d5561cca29e0c2a9648ad6060cad99c76b89f3773fda63078725",
"@rolldown/binding-wasm32-wasi@npm:1.0.0-beta.32": "f6b61a2b1ce791aa63144f361bca382d94fbcd8b42166960adaaddbe82cbfa255621ddd846c823a388b2eaa9a524f82873754e5ce63f80125b6ac2b601ff4196",
"@rolldown/binding-win32-arm64-msvc@npm:1.0.0-beta.32": "ca0fafbfaaf1104663417ef52459def1f173beb4afaef86254ce69e0686a6038fc41eef9cac0bce62e2b7d4ecb3b16c4a9a4d460e2d256bff359e6959a9a3b69",
"@rolldown/binding-win32-ia32-msvc@npm:1.0.0-beta.32": "4e365b3f5bb753aefe9a5e1841b1239073598dc738d1cdb2700a984fcb11b2eb5c623a3a3bb348cbfe9b354da15144244bb0875503a0d64603565c19213e5697",
"@rolldown/binding-win32-x64-msvc@npm:1.0.0-beta.32": "48bd1895a5a6111d28ecb5492632e2410aec9dc8502de75fa8617b22892e8138a59e9c543da0c393243b9d5f5a585daa59cdbf76f94724c0e9c78f60f1ab677f",
"@rollup/rollup-android-arm-eabi@npm:4.46.2": "d7d021a87cd3504c8d71b00a94199e13c4e07c14fe20ed7300cf1e6436a5f3fe8496c9e5f206e023b15f9b6f8991b2d95a48b47fa41d5c00b44f37fe5f4d5eb8",
"@rollup/rollup-android-arm64@npm:4.44.1": "5eb5c7de31f435afb97feebd06feefb93c2cb272002240b8530c1f274436ea578d8320d67fa49eb1409ddb20a47219efff59a09b22a426584b881d36adac7bea",
"@rollup/rollup-android-arm64@npm:4.46.2": "ca901edbf95bbdd2505c979f777e2a01e2e885a597b6daeed5362dac523ea2a1eb9c0c0d22b9b436f3613c22abdd442bd2764491948890930333a9e40ade35be",
"@rollup/rollup-darwin-arm64@npm:4.44.1": "93cef94c44d1baabc5346148d59ebb5d640948b2895bbc5de6804908999857f331c388eb449f98eba98d2b239492e91211accee7eaf4b579ae3c007705d76246",
"@rollup/rollup-darwin-arm64@npm:4.46.2": "ed2b07c4803915d46ff642abd659e179fae524dcd3cb88c810a5b71290d16b498e0371dcb91fe98f6301b8c6600d579a099be1e9450278326281002df4a80019",
"@rollup/rollup-darwin-x64@npm:4.44.1": "d235e7f40080cc19295d45935b83a0238cb66962e7d6d4af0eb261012d80bc344fe073ec9dcc3d39092138a66e7a69c4e79cbac7c6ee49b8329b80d12d52968f",
"@rollup/rollup-darwin-x64@npm:4.46.2": "c53e31df756cb8d44e179c167db1ec5d321225561a1aff2b320091c226c2dfafd080a98a1466f2dc697ff0173b52c41d89c60cae97f73b41fbd128d4c87fde66",
"@rollup/rollup-freebsd-arm64@npm:4.44.1": "e26f6bf6914e190d72f74da4c8e47c27661fc99f6310f80e664acfcd6262f4981ff043ff53ccc06dc6a232d481f9f0055e32dba3b65442259316b18fc0ea9087",
"@rollup/rollup-freebsd-arm64@npm:4.46.2": "9495d87e670bbea87e43d06df53ae83fcd46e2c82a80927556f3516ac76613b8b7739bbd4b43c3f264bbab57a50a3b6cd2dfa6c1b2741bd3acdeba8af7c47018",
"@rollup/rollup-freebsd-x64@npm:4.44.1": "d06455bfbe8bf71996864879b8b9b4f95617adda959a5a143be0df9a5bf749cd52740a9a3b2548e67be00eef9ccb99ef04368a052afceae2011e2a817baf4156",
"@rollup/rollup-freebsd-x64@npm:4.46.2": "890a965b45f4c4b9beb4696912ee30472180a040dafb24ce32f8811aed4d0d0ee90bf675d234abc6d8e66266d2966a72483fff7e6f1dbd116424b23e18fe38a9",
"@rollup/rollup-linux-arm-gnueabihf@npm:4.44.1": "1bb6818627de1434f889c8a6bfc13a0205f25055bcc9f39483e445411c753111c7d082cfca2affdeeaad46c3711e3ece140fabb7173ef3cd19446d3682863854",
"@rollup/rollup-linux-arm-gnueabihf@npm:4.46.2": "ce9720f61b4f7d9a791ba78e13cbbea67ef5f46c465e054c08f009cd06de8c1e4518df8e8578366a27cc9ae4280d37528dd0762906a19e820ca1a95158b47090",
"@rollup/rollup-linux-arm-musleabihf@npm:4.44.1": "6e9eff8ad332d9923a7cadc0ce1911c4e0a2f8457a8131485f0bce241bb7c223dc811b976ffb9ca08d4930630c647219dbfcdab639c3a8bef075b68285c01960",
"@rollup/rollup-linux-arm-musleabihf@npm:4.46.2": "b884f568a681d8c13ffdfa77ad6183ed6f7f9fe5bc952b1c82dc21e36b4bc8eb7ee292168929a2575ff5ff14582060d7d73c583aef7edf04fd0bddd67140f4b0",
"@rollup/rollup-linux-arm64-gnu@npm:4.44.1": "a5382c59bf531afd774b7de82d1600bfb5915a0708ad6c7099ffd1af657b813a525de18902178120b247c95e21487f0c96a8eecc8d9ae9163da12ecb4a5ed3d3",
"@rollup/rollup-linux-arm64-gnu@npm:4.46.2": "519477372d8358a4d3f1f1245bc2b5b57b65960f9a7d02bc5795ba68aed471fe87b20391a63c334bf0abb94085ad8c89d8d3b2e4d79ca0fed702537e9a0949eb",
"@rollup/rollup-linux-arm64-musl@npm:4.44.1": "44944da3785383b8cdb5f8b4eb627737edc10e206ec66ec6a762f75f2dcc12dff7bfaae864d37941d5d30c50c8fee5f5a2b12c19627b356e0aea3186f80e2d48",
"@rollup/rollup-linux-arm64-musl@npm:4.46.2": "a5dec7799dd832b5374171a73a6b57cffef8be317482dd9ea4e6554db6fc8afb4bdb91ec725502f1b378aa9cb9a1333684056d55c9120262cb7744a33b961a76",
"@rollup/rollup-linux-loongarch64-gnu@npm:4.44.1": "1f7502f1777622abb717a3109bf69907ba46a277a6ea33b99d6cea8e33f74b6d8a979ac5bfa33398f31211687adf7458ef9680b7f2e3523d82088a3d59857c8f",
"@rollup/rollup-linux-loongarch64-gnu@npm:4.46.2": "76ebbf40535f68c6922edf7d866dad00608cd475c8436d199653341ea09124cb4478f67c12d32b5363634f3f811926acb14a086eb146a1fe6b310fdbde01f2c8",
"@rollup/rollup-linux-powerpc64le-gnu@npm:4.44.1": "535c564216655518831098e5481f06b62452fad69209409776f4508e9c73265dfe531983a64b47c9090b244668b4c023c906061eee5a4c3c29c1c9822f1d1209",
"@rollup/rollup-linux-ppc64-gnu@npm:4.46.2": "31b62a51393e0f2608e1133701523e894ff5d04038e3d9af95abf595ab7fbe827167a651b200e9975d0e76904699cde3428aa1afbf46bf939f836f8ef90b1d88",
"@rollup/rollup-linux-riscv64-gnu@npm:4.44.1": "423cb7d6578052d23374fe2ec3b996be9a420785fe324a0d1aa38e9e9d3f4b34ba3673f4ce5f622c7ed99bf82c4e24f55469b6e75deb5f27eb67369c052d8a9b",
"@rollup/rollup-linux-riscv64-gnu@npm:4.46.2": "4f22bc5fe58730026085d1a5372b51d9ed933b314cde2d3dec1d73ad76106406915c313d331f094176ed917863c0041667e63184d9730da7107b11266dad477f",
"@rollup/rollup-linux-riscv64-musl@npm:4.44.1": "03a0530b45b554363296e938493e263aa26c342080aea72c9e2b673fc42d5ed2d9267d283ef136c71da64bc85a03fd360988aedbfc68029bd948567afafe71a6",
"@rollup/rollup-linux-riscv64-musl@npm:4.46.2": "1bc37b77ac38d7e82e7d661b67fc043a1db01272a0566e5432c61e3c7a5e6c11b5ecb4a49547da2de33a8e0a7b0d685f3c7341572fa77c90e9b71e515f753e86",
"@rollup/rollup-linux-s390x-gnu@npm:4.44.1": "93fecf841caf9621a99b8f0938d21a261ccc1ee7dced2ff47de659662a3bbbc0e3cc45ef38d3aca84918ecaeff51188a68a109db337892518ccfa393c305fafc",
"@rollup/rollup-linux-s390x-gnu@npm:4.46.2": "1b1821a848d8bf86fa5e01ae57f60ebe5566cbcb0c605b5d05050821e94b8b45a72515ef302a3021196018b289aaffade5c41a5f89b3f8324d509a25d28dcfc4",
"@rollup/rollup-linux-x64-gnu@npm:4.44.1": "b401ab19f21fb0f8ff50662a9fbe823d4c7904fdf0f8de17c3338107e3974ac89ba1189b7c6f0f2ff8734cfd07325bcb5e5dc6a5d53df076fe34757233063a72",
"@rollup/rollup-linux-x64-gnu@npm:4.46.2": "66231802689c1ac1d6ecec6fd65d14c01c900537e588808e0a2c92ba34e322665bb6df3853500717cf600f40a93de4c490838290e21bb10eed4249f587a08109",
"@rollup/rollup-linux-x64-musl@npm:4.44.1": "87ef16e6ca9ae5d398f5b7e1b58033cf1b5f87efb0ab7c4b16dc7dd213a1b3a9305c2624b11e89539d75d146912eb7a41a139270464fc617fd1c2917e391fc56",
"@rollup/rollup-linux-x64-musl@npm:4.46.2": "064ed54e9ddb05eec1b5da5ac8366f3290b7b65e63959a76a26d6940ce44d741c30488e39ce94c51a2679b2c56217c2e0aaf74e123a1c0928503712c115d6047",
"@rollup/rollup-win32-arm64-msvc@npm:4.44.1": "5f4fe05f1c778d839ce22c2b1a1419d8c511fd10793326dab5a033978f259733b6d042519a26ad7df3f5b2cce75bfc2d2600d9c43ec9cf16d6acf105bedf00e7",
"@rollup/rollup-win32-arm64-msvc@npm:4.46.2": "b59089cddf652e3da278744f6b8b2105360d1219833e54791380322913d40073ed4197ccd06d6091e83e1e12a5290d7a2e4aeae7947ff20c45943d07d1f0af0c",
"@rollup/rollup-win32-ia32-msvc@npm:4.44.1": "5639edf6114f38055489c35f03f5d039b4719ae50309db235dc176c026fb8eb5bb028e0db2769ed4faec34722200f6a31df62c23a0f8c90a13568685a6f00140",
"@rollup/rollup-win32-ia32-msvc@npm:4.46.2": "d0aae1f80a64d9148426a7ff25b9df7f3abf7aca912c358a952f4b3bc541e030b5959f52e0b67abe01b9c8c8fb6567d1bbd30e31daabb7e2c4dc0488faf875f7",
"@rollup/rollup-win32-x64-msvc@npm:4.44.1": "d23d4b88a0a96965ba20e75bb9f5bea6e22cab53b9658a54317634363a64aa43dd1ff66f569d0e4fecc34d60d554a64ae905fd85cbc436f78544d6f9d550ebe2",
"@rollup/rollup-win32-x64-msvc@npm:4.46.2": "740ca3c1d07f5af76fc9c2db917edbf6d0c1cf3eeee8330a0c571db4990ec44f0b272696a215ab118e8a32d7529f84bd47225e85dfab458a989b4b18d0bbea49",
"@unrs/resolver-binding-android-arm-eabi@npm:1.11.1": "04dd38b694c1680bfec192b499e188700398a414886a08a8a7c72815db56ac147df03d88c73ff6fff7ac3e0a01dc41978054b3622b49463e0d684c5168557fcc",
"@unrs/resolver-binding-android-arm64@npm:1.11.1": "763626adc34dd2b4af677b5ced6493e7b2b1935351a5c9137f1c9561d11faf97b94015e6876e57e85c33ff563564314c92c0882a4780a57f2225cbbd779a695d",

Some files were not shown because too many files have changed in this diff Show More