diff options
130 files changed, 3866 insertions, 1897 deletions
@@ -8,6 +8,12 @@ creation_rules: | |||
8 | - path_regex: ^hosts/surtr/email/ca | 8 | - path_regex: ^hosts/surtr/email/ca |
9 | key_groups: | 9 | key_groups: |
10 | - age: [ *admin_gkleen ] | 10 | - age: [ *admin_gkleen ] |
11 | - path_regex: ^home-modules/lmu-hausschrift/ | ||
12 | key_groups: | ||
13 | - age: [ *admin_gkleen ] | ||
14 | - path_regex: ^accounts/gkleen@sif/ | ||
15 | key_groups: | ||
16 | - age: [ *admin_gkleen ] | ||
11 | - path_regex: surtr\/?[^\/]*$ | 17 | - path_regex: surtr\/?[^\/]*$ |
12 | key_groups: | 18 | key_groups: |
13 | - age: [ *admin_gkleen, *machine_surtr ] | 19 | - age: [ *admin_gkleen, *machine_surtr ] |
diff --git a/_sources/generated.json b/_sources/generated.json index d86f7005..d98f141f 100644 --- a/_sources/generated.json +++ b/_sources/generated.json | |||
@@ -261,6 +261,36 @@ | |||
261 | }, | 261 | }, |
262 | "version": "1f8c31457459ffc28cd1c3f3c2235a53efad7148" | 262 | "version": "1f8c31457459ffc28cd1c3f3c2235a53efad7148" |
263 | }, | 263 | }, |
264 | "netbootxyz-efi": { | ||
265 | "cargoLocks": null, | ||
266 | "date": null, | ||
267 | "extract": null, | ||
268 | "name": "netbootxyz-efi", | ||
269 | "passthru": null, | ||
270 | "pinned": false, | ||
271 | "src": { | ||
272 | "name": null, | ||
273 | "sha256": "sha256-8kd17ChqLuVH5/OdPc2rVDKEDWHl9ZWLh8k+EBrCGH8=", | ||
274 | "type": "url", | ||
275 | "url": "https://github.com/netbootxyz/netboot.xyz/releases/download/2.0.87/netboot.xyz.efi" | ||
276 | }, | ||
277 | "version": "2.0.87" | ||
278 | }, | ||
279 | "netbootxyz-lkrn": { | ||
280 | "cargoLocks": null, | ||
281 | "date": null, | ||
282 | "extract": null, | ||
283 | "name": "netbootxyz-lkrn", | ||
284 | "passthru": null, | ||
285 | "pinned": false, | ||
286 | "src": { | ||
287 | "name": null, | ||
288 | "sha256": "sha256-/qY3NdRC0SghQ4kamrkm9EFumrKlirqDCJ+XY+jHWLA=", | ||
289 | "type": "url", | ||
290 | "url": "https://github.com/netbootxyz/netboot.xyz/releases/download/2.0.87/netboot.xyz.lkrn" | ||
291 | }, | ||
292 | "version": "2.0.87" | ||
293 | }, | ||
264 | "postfix-mta-sts-resolver": { | 294 | "postfix-mta-sts-resolver": { |
265 | "cargoLocks": null, | 295 | "cargoLocks": null, |
266 | "date": null, | 296 | "date": null, |
@@ -407,7 +437,7 @@ | |||
407 | }, | 437 | }, |
408 | "v4l2loopback": { | 438 | "v4l2loopback": { |
409 | "cargoLocks": null, | 439 | "cargoLocks": null, |
410 | "date": "2025-04-16", | 440 | "date": "2025-04-29", |
411 | "extract": null, | 441 | "extract": null, |
412 | "name": "v4l2loopback", | 442 | "name": "v4l2loopback", |
413 | "passthru": null, | 443 | "passthru": null, |
@@ -419,12 +449,12 @@ | |||
419 | "name": null, | 449 | "name": null, |
420 | "owner": "umlaeute", | 450 | "owner": "umlaeute", |
421 | "repo": "v4l2loopback", | 451 | "repo": "v4l2loopback", |
422 | "rev": "119543510c0455f4e6517ae82d81d65354225a31", | 452 | "rev": "8d806ad688961d8840081a609c39d1a82d296b24", |
423 | "sha256": "sha256-1SAYXV0KEQEQEiQbBom2YHoeDzuDWkmCmcetlU85h/M=", | 453 | "sha256": "sha256-zuE/qFI8QCWCePmHWjTIPTh2KzmDkwQ2uj5C1dAwo1c=", |
424 | "sparseCheckout": [], | 454 | "sparseCheckout": [], |
425 | "type": "github" | 455 | "type": "github" |
426 | }, | 456 | }, |
427 | "version": "119543510c0455f4e6517ae82d81d65354225a31" | 457 | "version": "8d806ad688961d8840081a609c39d1a82d296b24" |
428 | }, | 458 | }, |
429 | "xcompose": { | 459 | "xcompose": { |
430 | "cargoLocks": null, | 460 | "cargoLocks": null, |
@@ -456,10 +486,10 @@ | |||
456 | "pinned": false, | 486 | "pinned": false, |
457 | "src": { | 487 | "src": { |
458 | "name": null, | 488 | "name": null, |
459 | "sha256": "sha256-G/4OZg0acKCeJ7LVj5LjCx4uNizEh4KfL4JDRq5J+5E=", | 489 | "sha256": "sha256-6nOFTF2rwSTymjWo+um8XUIu8yMb6+6ivfqCrBkanCk=", |
460 | "type": "url", | 490 | "type": "url", |
461 | "url": "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2025.3.31.tar.gz" | 491 | "url": "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2025.5.22.tar.gz" |
462 | }, | 492 | }, |
463 | "version": "2025.3.31" | 493 | "version": "2025.5.22" |
464 | } | 494 | } |
465 | } \ No newline at end of file | 495 | } \ No newline at end of file |
diff --git a/_sources/generated.nix b/_sources/generated.nix index a7ebef8a..3bf73fed 100644 --- a/_sources/generated.nix +++ b/_sources/generated.nix | |||
@@ -162,6 +162,22 @@ | |||
162 | }; | 162 | }; |
163 | date = "2020-02-10"; | 163 | date = "2020-02-10"; |
164 | }; | 164 | }; |
165 | netbootxyz-efi = { | ||
166 | pname = "netbootxyz-efi"; | ||
167 | version = "2.0.87"; | ||
168 | src = fetchurl { | ||
169 | url = "https://github.com/netbootxyz/netboot.xyz/releases/download/2.0.87/netboot.xyz.efi"; | ||
170 | sha256 = "sha256-8kd17ChqLuVH5/OdPc2rVDKEDWHl9ZWLh8k+EBrCGH8="; | ||
171 | }; | ||
172 | }; | ||
173 | netbootxyz-lkrn = { | ||
174 | pname = "netbootxyz-lkrn"; | ||
175 | version = "2.0.87"; | ||
176 | src = fetchurl { | ||
177 | url = "https://github.com/netbootxyz/netboot.xyz/releases/download/2.0.87/netboot.xyz.lkrn"; | ||
178 | sha256 = "sha256-/qY3NdRC0SghQ4kamrkm9EFumrKlirqDCJ+XY+jHWLA="; | ||
179 | }; | ||
180 | }; | ||
165 | postfix-mta-sts-resolver = { | 181 | postfix-mta-sts-resolver = { |
166 | pname = "postfix-mta-sts-resolver"; | 182 | pname = "postfix-mta-sts-resolver"; |
167 | version = "1.5.0"; | 183 | version = "1.5.0"; |
@@ -254,15 +270,15 @@ | |||
254 | }; | 270 | }; |
255 | v4l2loopback = { | 271 | v4l2loopback = { |
256 | pname = "v4l2loopback"; | 272 | pname = "v4l2loopback"; |
257 | version = "119543510c0455f4e6517ae82d81d65354225a31"; | 273 | version = "8d806ad688961d8840081a609c39d1a82d296b24"; |
258 | src = fetchFromGitHub { | 274 | src = fetchFromGitHub { |
259 | owner = "umlaeute"; | 275 | owner = "umlaeute"; |
260 | repo = "v4l2loopback"; | 276 | repo = "v4l2loopback"; |
261 | rev = "119543510c0455f4e6517ae82d81d65354225a31"; | 277 | rev = "8d806ad688961d8840081a609c39d1a82d296b24"; |
262 | fetchSubmodules = true; | 278 | fetchSubmodules = true; |
263 | sha256 = "sha256-1SAYXV0KEQEQEiQbBom2YHoeDzuDWkmCmcetlU85h/M="; | 279 | sha256 = "sha256-zuE/qFI8QCWCePmHWjTIPTh2KzmDkwQ2uj5C1dAwo1c="; |
264 | }; | 280 | }; |
265 | date = "2025-04-16"; | 281 | date = "2025-04-29"; |
266 | }; | 282 | }; |
267 | xcompose = { | 283 | xcompose = { |
268 | pname = "xcompose"; | 284 | pname = "xcompose"; |
@@ -278,10 +294,10 @@ | |||
278 | }; | 294 | }; |
279 | yt-dlp = { | 295 | yt-dlp = { |
280 | pname = "yt-dlp"; | 296 | pname = "yt-dlp"; |
281 | version = "2025.3.31"; | 297 | version = "2025.5.22"; |
282 | src = fetchurl { | 298 | src = fetchurl { |
283 | url = "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2025.3.31.tar.gz"; | 299 | url = "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2025.5.22.tar.gz"; |
284 | sha256 = "sha256-G/4OZg0acKCeJ7LVj5LjCx4uNizEh4KfL4JDRq5J+5E="; | 300 | sha256 = "sha256-6nOFTF2rwSTymjWo+um8XUIu8yMb6+6ivfqCrBkanCk="; |
285 | }; | 301 | }; |
286 | }; | 302 | }; |
287 | } | 303 | } |
diff --git a/accounts/gkleen@eostre.nix b/accounts/gkleen@eostre.nix index 72818d44..28daf3fd 100644 --- a/accounts/gkleen@eostre.nix +++ b/accounts/gkleen@eostre.nix | |||
@@ -1,16 +1,16 @@ | |||
1 | { flake, userName, pkgs, ... }: | 1 | { flake, userName, pkgs, ... }: |
2 | { | 2 | { |
3 | imports = with flake.nixosModules.userProfiles.${userName}; [ | 3 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
4 | zsh utils tmux | 4 | utils |
5 | ]; | 5 | ]; |
6 | 6 | ||
7 | config = { | 7 | config = { |
8 | home-manager.users.${userName} = { | 8 | home-manager.users.${userName} = { |
9 | home.stateVersion = "20.09"; | 9 | home.stateVersion = "20.09"; |
10 | 10 | ||
11 | nixpkgs.config = { | 11 | # nixpkgs.config = { |
12 | allowUnfree = true; | 12 | # allowUnfree = true; |
13 | }; | 13 | # }; |
14 | 14 | ||
15 | home.packages = with pkgs; [ | 15 | home.packages = with pkgs; [ |
16 | thunderbird libreoffice element-desktop keepassxc vlc | 16 | thunderbird libreoffice element-desktop keepassxc vlc |
diff --git a/accounts/gkleen@installer.nix b/accounts/gkleen@installer.nix index c7a418f8..5fe1db38 100644 --- a/accounts/gkleen@installer.nix +++ b/accounts/gkleen@installer.nix | |||
@@ -1,7 +1,11 @@ | |||
1 | { userName, ... }: | 1 | { flake, userName, ... }: |
2 | 2 | ||
3 | { | 3 | { |
4 | home-manager.users.${userName} = { config, ... } : { | 4 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
5 | zsh tmux | ||
6 | ]; | ||
7 | |||
8 | config.home-manager.users.${userName} = { config, ... } : { | ||
5 | home.stateVersion = config.home.version.release; | 9 | home.stateVersion = config.home.version.release; |
6 | }; | 10 | }; |
7 | } | 11 | } |
diff --git a/accounts/gkleen@sif/default.nix b/accounts/gkleen@sif/default.nix index 6574af9d..706eb241 100644 --- a/accounts/gkleen@sif/default.nix +++ b/accounts/gkleen@sif/default.nix | |||
@@ -50,9 +50,20 @@ let | |||
50 | }; | 50 | }; |
51 | 51 | ||
52 | lockCommand = "${lib.getExe' config.systemd.package "systemctl"} --user start gtklock.service"; | 52 | lockCommand = "${lib.getExe' config.systemd.package "systemctl"} --user start gtklock.service"; |
53 | |||
54 | editor = pkgs.symlinkJoin { | ||
55 | inherit (cfg.services.emacs.package) name; | ||
56 | buildInputs = with pkgs; [ makeWrapper ]; | ||
57 | paths = [ cfg.services.emacs.package ]; | ||
58 | postBuild = '' | ||
59 | wrapProgram $out/bin/emacsclient \ | ||
60 | --inherit-argv0 \ | ||
61 | --add-flags ${lib.escapeShellArg (lib.escapeShellArgs cfg.services.emacs.client.arguments)} | ||
62 | ''; | ||
63 | }; | ||
53 | in { | 64 | in { |
54 | imports = with flake.nixosModules.userProfiles.${userName}; [ | 65 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
55 | mpv yt-dlp (args: import ./xcompose.nix (inputs // args)) | 66 | zsh tmux mpv yt-dlp (args: import ./xcompose.nix (inputs // args)) |
56 | ]; | 67 | ]; |
57 | 68 | ||
58 | config = { | 69 | config = { |
@@ -60,16 +71,17 @@ in { | |||
60 | imports = [ | 71 | imports = [ |
61 | ./libvirt | 72 | ./libvirt |
62 | ./niri | 73 | ./niri |
74 | ./synadm | ||
63 | flakeInputs.nix-index-database.hmModules.nix-index | 75 | flakeInputs.nix-index-database.hmModules.nix-index |
64 | flakeInputs.impermanence.nixosModules.home-manager.impermanence | 76 | flakeInputs.impermanence.nixosModules.home-manager.impermanence |
65 | ]; | 77 | ]; |
66 | 78 | ||
67 | home.stateVersion = "20.09"; | 79 | home.stateVersion = "20.09"; |
68 | 80 | ||
69 | nixpkgs.config = { | 81 | # nixpkgs.config = { |
70 | allowUnfree = true; | 82 | # allowUnfree = true; |
71 | zathura.useMupdf = false; | 83 | # zathura.useMupdf = false; |
72 | }; | 84 | # }; |
73 | 85 | ||
74 | nix.registry = { | 86 | nix.registry = { |
75 | "flk" = { | 87 | "flk" = { |
@@ -175,13 +187,91 @@ in { | |||
175 | gpu-api = "vulkan"; | 187 | gpu-api = "vulkan"; |
176 | }; | 188 | }; |
177 | 189 | ||
178 | zsh.initExtra = '' | 190 | zsh.initContent = let |
179 | source ${./zshrc} | 191 | zshrc = pkgs.resholve.mkDerivation { |
192 | pname = "zshrc"; | ||
193 | version = "0.0.0"; | ||
194 | |||
195 | src = ./zshrc; | ||
196 | |||
197 | dontUnpack = true; | ||
198 | dontConfigure = true; | ||
199 | dontBuild = true; | ||
200 | |||
201 | installPhase = '' | ||
202 | mkdir -p $out/share | ||
203 | install "$src" $out/share/zshrc | ||
204 | ''; | ||
205 | |||
206 | solutions = { | ||
207 | default = { | ||
208 | scripts = [ "share/zshrc" ]; | ||
209 | interpreter = "none"; | ||
210 | inputs = with pkgs; [ | ||
211 | coreutils | ||
212 | rpm | ||
213 | binutils | ||
214 | squashfsTools | ||
215 | unzip | ||
216 | cfg.programs.git.package | ||
217 | magickWrapped | ||
218 | curl | ||
219 | file | ||
220 | gnutar | ||
221 | cpio | ||
222 | magic-wormhole | ||
223 | cfg.programs.zsh.package | ||
224 | fuse | ||
225 | util-linux | ||
226 | findutils | ||
227 | qrencode | ||
228 | tty-clock | ||
229 | cfg.programs.jq.package | ||
230 | eza | ||
231 | less | ||
232 | config.systemd.package | ||
233 | config.programs.ssh.package | ||
234 | gnused | ||
235 | miniserve | ||
236 | ]; | ||
237 | execer = with pkgs; [ | ||
238 | "cannot:${lib.getExe' rpm "rpm2cpio"}" | ||
239 | "cannot:${lib.getExe' squashfsTools "unsquashfs"}" | ||
240 | "cannot:${lib.getExe' unzip "unzip"}" | ||
241 | "cannot:${lib.getExe cfg.programs.git.package}" | ||
242 | "cannot:${lib.getExe cpio}" | ||
243 | "cannot:${lib.getExe' magic-wormhole "wormhole"}" | ||
244 | "cannot:${lib.getExe' fuse "fusermount"}" | ||
245 | "cannot:${lib.getExe less}" | ||
246 | "cannot:${lib.getExe' config.systemd.package "systemctl"}" | ||
247 | "cannot:${lib.getExe config.programs.ssh.package}" | ||
248 | ]; | ||
249 | wrapper = with pkgs; [ | ||
250 | "${lib.getExe' magickWrapped "magick"}:${lib.getExe' imagemagick "magick"}" | ||
251 | ]; | ||
252 | fake = { | ||
253 | builtin = ["print"]; | ||
254 | external = ["sudo" "umount"]; | ||
255 | }; | ||
256 | }; | ||
257 | }; | ||
258 | }; | ||
259 | magickWrapped = pkgs.symlinkJoin { | ||
260 | inherit (pkgs.imagemagick) name; | ||
261 | paths = [ pkgs.imagemagick ]; | ||
262 | |||
263 | buildInputs = with pkgs; [ makeWrapper ]; | ||
264 | postBuild = '' | ||
265 | wrapProgram $out/bin/magick \ | ||
266 | --prefix PATH : ${lib.makeBinPath (with pkgs; [ ghostscript ])} | ||
267 | ''; | ||
268 | }; | ||
269 | in '' | ||
270 | source ${zshrc}/share/zshrc | ||
180 | ''; | 271 | ''; |
181 | zsh.dirHashes = let | 272 | zsh.dirHashes = let |
182 | flakeHashes = mapAttrs' (n: v: nameValuePair (inputNames.${n} or n) (toString v)) flakeInputs; | 273 | flakeHashes = mapAttrs' (n: v: nameValuePair (inputNames.${n} or n) (toString v)) flakeInputs; |
183 | inputNames = { | 274 | inputNames = { |
184 | "nixpkgs" = "nixos"; | ||
185 | }; | 275 | }; |
186 | in flakeHashes // { | 276 | in flakeHashes // { |
187 | u2w = "$HOME/projects/uni2work"; | 277 | u2w = "$HOME/projects/uni2work"; |
@@ -193,6 +283,16 @@ in { | |||
193 | pro = "$HOME/projects/pro"; | 283 | pro = "$HOME/projects/pro"; |
194 | media = "$HOME/media"; | 284 | media = "$HOME/media"; |
195 | }; | 285 | }; |
286 | jq.colors = { | ||
287 | arrays = "1;37"; | ||
288 | "false" = "0;37"; | ||
289 | "null" = "2;37"; | ||
290 | numbers = "0;37"; | ||
291 | objectKeys = "1;34"; | ||
292 | objects = "1;37"; | ||
293 | strings = "0;32"; | ||
294 | "true" = "0;37"; | ||
295 | }; | ||
196 | 296 | ||
197 | obs-studio = { | 297 | obs-studio = { |
198 | enable = true; | 298 | enable = true; |
@@ -202,7 +302,7 @@ in { | |||
202 | gh = { | 302 | gh = { |
203 | enable = true; | 303 | enable = true; |
204 | settings = { | 304 | settings = { |
205 | editor = lib.getExe' config.home-manager.users.${userName}.programs.emacs.package "emacsclient"; | 305 | editor = lib.getExe' editor "emacsclient"; |
206 | gitProtocol = "ssh"; | 306 | gitProtocol = "ssh"; |
207 | }; | 307 | }; |
208 | }; | 308 | }; |
@@ -228,16 +328,10 @@ in { | |||
228 | # notify_on_cmd_finish = "invisible 120"; | 328 | # notify_on_cmd_finish = "invisible 120"; |
229 | }; | 329 | }; |
230 | keybindings = { | 330 | keybindings = { |
231 | "kitty_mod+n" = "detach_window"; | 331 | "kitty_mod+n" = "new_os_window_with_cwd"; |
232 | "kitty_mod+m" = "detach_window ask"; | 332 | "kitty_mod+m" = "detach_window ask"; |
233 | }; | 333 | "kitty_mod+enter" = "new_window_with_cwd"; |
234 | }; | 334 | "kitty_mod+t" = "new_tab_with_cwd"; |
235 | wpaperd = { | ||
236 | enable = true; | ||
237 | settings.default = { | ||
238 | path = "~/.wallpapers"; | ||
239 | duration = "15m"; | ||
240 | mode = "center"; | ||
241 | }; | 335 | }; |
242 | }; | 336 | }; |
243 | fuzzel = { | 337 | fuzzel = { |
@@ -250,7 +344,7 @@ in { | |||
250 | font = "Fira Sans"; | 344 | font = "Fira Sans"; |
251 | }; | 345 | }; |
252 | colors = { | 346 | colors = { |
253 | background = "000000aa"; | 347 | background = "000000cc"; |
254 | text = "cdd6f4ff"; | 348 | text = "cdd6f4ff"; |
255 | match = "94e2d5ff"; | 349 | match = "94e2d5ff"; |
256 | selection = "585b70ff"; | 350 | selection = "585b70ff"; |
@@ -267,15 +361,28 @@ in { | |||
267 | enable = true; | 361 | enable = true; |
268 | extraAbbreviations = ["i.A." "d.h." "D.h." "gdw."]; | 362 | extraAbbreviations = ["i.A." "d.h." "D.h." "gdw."]; |
269 | }; | 363 | }; |
364 | nushell = { | ||
365 | enable = true; | ||
366 | settings.show_banner = false; | ||
367 | }; | ||
368 | fd.enable = true; | ||
270 | }; | 369 | }; |
271 | 370 | ||
272 | services = { | 371 | services = { |
372 | wpaperd = { | ||
373 | enable = true; | ||
374 | settings.default = { | ||
375 | path = "~/.wallpapers"; | ||
376 | duration = "15m"; | ||
377 | mode = "center"; | ||
378 | }; | ||
379 | }; | ||
273 | emacs = { | 380 | emacs = { |
274 | enable = true; | 381 | enable = true; |
275 | socketActivation.enable = true; | 382 | socketActivation.enable = true; |
276 | client = { | 383 | client = { |
277 | enable = true; | 384 | enable = true; |
278 | arguments = mkForce ["--reuse-frame" "--alternate-editor" "\"\""]; | 385 | arguments = mkForce ["--create-frame" "--alternate-editor" (lib.getExe cfg.services.emacs.package)]; |
279 | }; | 386 | }; |
280 | }; | 387 | }; |
281 | gpg-agent = { | 388 | gpg-agent = { |
@@ -384,6 +491,13 @@ in { | |||
384 | }; | 491 | }; |
385 | }; | 492 | }; |
386 | 493 | ||
494 | qt.kde.settings = { | ||
495 | kwalletrc = { | ||
496 | KSecretD.Enabled = false; | ||
497 | Wallet."Default Wallet" = "store"; | ||
498 | }; | ||
499 | }; | ||
500 | |||
387 | xsession.preferStatusNotifierItems = true; | 501 | xsession.preferStatusNotifierItems = true; |
388 | 502 | ||
389 | xresources.properties = import ./xresources.nix; | 503 | xresources.properties = import ./xresources.nix; |
@@ -403,7 +517,7 @@ in { | |||
403 | libguestfs-with-appliance nerd-fonts.fira-mono | 517 | libguestfs-with-appliance nerd-fonts.fira-mono |
404 | nerd-fonts.symbols-only nerd-fonts.fira-code powerline-fonts | 518 | nerd-fonts.symbols-only nerd-fonts.fira-code powerline-fonts |
405 | swtpm (hunspellWithDicts (with hunspellDicts; [en_GB-large de_DE])) | 519 | swtpm (hunspellWithDicts (with hunspellDicts; [en_GB-large de_DE])) |
406 | # synadm | 520 | libation libqalculate |
407 | ] ++ mapAttrsToList (_name: pkg: pkgs.callPackage pkg {}) (customUtils.nixImport { dir = ./utils; }); | 521 | ] ++ mapAttrsToList (_name: pkg: pkgs.callPackage pkg {}) (customUtils.nixImport { dir = ./utils; }); |
408 | 522 | ||
409 | file = { | 523 | file = { |
@@ -424,12 +538,9 @@ in { | |||
424 | QT_QPA_PLATFORMTHEME = "qt5ct"; | 538 | QT_QPA_PLATFORMTHEME = "qt5ct"; |
425 | LIBVIRT_DEFAULT_URI = "qemu:///system"; | 539 | LIBVIRT_DEFAULT_URI = "qemu:///system"; |
426 | STACK_XDG = 1; | 540 | STACK_XDG = 1; |
427 | EDITOR = pkgs.writeShellScript "editor" '' | 541 | EDITOR = lib.getExe' editor "emacsclient"; |
428 | args=("--reuse-frame" "--alternate-editor" "") | ||
429 | args+=("$@") | ||
430 | exec -a emacsclient ${lib.getExe' cfg.services.emacs.package "emacsclient"} "''${args[@]}" | ||
431 | ''; | ||
432 | RCLONE_PASSWORD_COMMAND = "${lib.getExe' pkgs.libsecret "secret-tool"} lookup service rclone"; | 542 | RCLONE_PASSWORD_COMMAND = "${lib.getExe' pkgs.libsecret "secret-tool"} lookup service rclone"; |
543 | SYSTEMD_TINT_BACKGROUND = "false"; | ||
433 | }; | 544 | }; |
434 | 545 | ||
435 | extraProfileCommands = '' | 546 | extraProfileCommands = '' |
@@ -466,9 +577,17 @@ in { | |||
466 | General = { | 577 | General = { |
467 | dot_as_separator = 0; | 578 | dot_as_separator = 0; |
468 | }; | 579 | }; |
580 | Mode = { | ||
581 | calculate_as_you_type = 1; | ||
582 | }; | ||
469 | }; | 583 | }; |
470 | }; | 584 | }; |
471 | "emacs/init.el".source = ./emacs.el; | 585 | "emacs/init.el".source = pkgs.substitute { |
586 | src = ./emacs.el; | ||
587 | substitutions = [ | ||
588 | "--subst-var-by" "ksshaskpass" (lib.getExe pkgs.kdePackages.ksshaskpass) | ||
589 | ]; | ||
590 | }; | ||
472 | "systemd/user/xdg-desktop-portal.service.d/after-graphical-session.conf".text = '' | 591 | "systemd/user/xdg-desktop-portal.service.d/after-graphical-session.conf".text = '' |
473 | [Unit] | 592 | [Unit] |
474 | After=graphical-session.target | 593 | After=graphical-session.target |
@@ -486,6 +605,8 @@ in { | |||
486 | xdg.dataFile = { | 605 | xdg.dataFile = { |
487 | "dbus-1/services/org.keepassxc.KeePassXC.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.keepassxc.KeePassXC.service"; | 606 | "dbus-1/services/org.keepassxc.KeePassXC.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.keepassxc.KeePassXC.service"; |
488 | "dbus-1/services/org.freedesktop.secrets.service.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.freedesktop.secrets.service.service"; | 607 | "dbus-1/services/org.freedesktop.secrets.service.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.freedesktop.secrets.service.service"; |
608 | "dbus-1/services/org.kde.kwalletd6.service".source = "${pkgs.kdePackages.kwallet}/share/dbus-1/services/org.kde.kwalletd6.service"; | ||
609 | "dbus-1/services/org.kde.kwalletd5.service".source = "${pkgs.kdePackages.kwallet}/share/dbus-1/services/org.kde.kwalletd5.service"; | ||
489 | "emoji-data/list.txt".source = pkgs.stdenv.mkDerivation { | 610 | "emoji-data/list.txt".source = pkgs.stdenv.mkDerivation { |
490 | inherit (sources.emoji-data) pname src; | 611 | inherit (sources.emoji-data) pname src; |
491 | version = lib.removePrefix "v" sources.emoji-data.version; | 612 | version = lib.removePrefix "v" sources.emoji-data.version; |
@@ -573,10 +694,10 @@ in { | |||
573 | exec -- \ | 694 | exec -- \ |
574 | ${lib.getExe' config.systemd.package "systemd-run"} --wait --user --slice-inherit \ | 695 | ${lib.getExe' config.systemd.package "systemd-run"} --wait --user --slice-inherit \ |
575 | --property 'CPUAccounting=yes' --property 'CPUQuotaPeriodSec=50ms' \ | 696 | --property 'CPUAccounting=yes' --property 'CPUQuotaPeriodSec=50ms' \ |
576 | --property 'Environment=DSCP=46' \ | 697 | -E DSCP=46 -E NIXOS_OZONE_WL \ |
577 | -- ${lib.getExe pkgs.dscp} ${lib.getExe' pkgs.google-chrome "google-chrome-stable"} \ | 698 | -- ${lib.getExe pkgs.dscp} ${lib.getExe' pkgs.google-chrome "google-chrome-stable"} \ |
578 | --class=Rainbow \ | 699 | --class=Rainbow \ |
579 | --kiosk "https://web.openrainbow.com" \ | 700 | --app="https://web.openrainbow.com" \ |
580 | --user-data-dir=''${HOME}/.config/google-chrome-rainbow | 701 | --user-data-dir=''${HOME}/.config/google-chrome-rainbow |
581 | ''); | 702 | ''); |
582 | icon = pkgs.fetchurl { | 703 | icon = pkgs.fetchurl { |
@@ -587,6 +708,53 @@ in { | |||
587 | StartupWMClass = "Rainbow"; | 708 | StartupWMClass = "Rainbow"; |
588 | }; | 709 | }; |
589 | }; | 710 | }; |
711 | kimai = { | ||
712 | name = "Kimai"; | ||
713 | exec = toString (pkgs.writeShellScript "kimai" '' | ||
714 | exec -- \ | ||
715 | ${lib.getExe' pkgs.google-chrome "google-chrome-stable"} \ | ||
716 | --class=Kimai \ | ||
717 | --app="https://kimai.yggdrasil.li" \ | ||
718 | --user-data-dir=''${HOME}/.config/google-chrome-kimai | ||
719 | ''); | ||
720 | icon = pkgs.fetchurl { | ||
721 | url = "https://www.kimai.org/images/kimai_logo.png"; | ||
722 | hash = "sha256-lnlOttzR2SwXA70R+egJUkeKr4U5V0avqTk8uX4bqfs="; | ||
723 | }; | ||
724 | settings = { | ||
725 | StartupWMClass = "Kimai"; | ||
726 | StartupNotify = "true"; | ||
727 | }; | ||
728 | }; | ||
729 | audiobookshelf = { | ||
730 | name = "Audiobookshelf"; | ||
731 | exec = toString (pkgs.writeShellScript "audiobookshelf" '' | ||
732 | exec -- \ | ||
733 | ${lib.getExe' pkgs.google-chrome "google-chrome-stable"} \ | ||
734 | --class=Audiobookshelf \ | ||
735 | --app="https://audiobookshelf.yggdrasil.li" \ | ||
736 | --user-data-dir=''${HOME}/.config/google-chrome-audiobookshelf | ||
737 | ''); | ||
738 | icon = pkgs.fetchurl { | ||
739 | url = "https://www.audiobookshelf.org/Logo.png"; | ||
740 | hash = "sha256-JGPk+WNT1C4DC4lSMb0K0YmAMT5LvmSOeO0QRzkc7Lk="; | ||
741 | }; | ||
742 | settings = { | ||
743 | StartupWMClass = "Audiobookshelf"; | ||
744 | StartupNotify = "true"; | ||
745 | }; | ||
746 | }; | ||
747 | thunderbird-lmu = { | ||
748 | name = "Thunderbird (LMU)"; | ||
749 | exec = "thunderbird --name thunderbird -P lmu %U"; | ||
750 | icon = "thunderbird"; | ||
751 | genericName = "Email Client"; | ||
752 | categories = [ "Network" "Chat" "Email" "Feed" "GTK" "News" ]; | ||
753 | settings = { | ||
754 | StartupWMClass = "thunderbird"; | ||
755 | StartupNotify = "true"; | ||
756 | }; | ||
757 | }; | ||
590 | }; | 758 | }; |
591 | 759 | ||
592 | fonts = { | 760 | fonts = { |
diff --git a/accounts/gkleen@sif/emacs.el b/accounts/gkleen@sif/emacs.el index 563c5d0b..3beefba6 100644 --- a/accounts/gkleen@sif/emacs.el +++ b/accounts/gkleen@sif/emacs.el | |||
@@ -254,3 +254,5 @@ necessarily running." | |||
254 | (bind-key "C-x C-m" #'move-file) | 254 | (bind-key "C-x C-m" #'move-file) |
255 | 255 | ||
256 | (let ((ssh_auth_sock (string-chop-newline (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket")))) (setenv "SSH_AUTH_SOCK" ssh_auth_sock)) | 256 | (let ((ssh_auth_sock (string-chop-newline (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket")))) (setenv "SSH_AUTH_SOCK" ssh_auth_sock)) |
257 | (setenv "SSH_ASKPASS_REQUIRE" "prefer") | ||
258 | (setenv "SSH_ASKPASS" "@ksshaskpass@") | ||
diff --git a/accounts/gkleen@sif/niri/default.nix b/accounts/gkleen@sif/niri/default.nix index dc993d66..8752f3e3 100644 --- a/accounts/gkleen@sif/niri/default.nix +++ b/accounts/gkleen@sif/niri/default.nix | |||
@@ -35,7 +35,11 @@ let | |||
35 | if jq -e '.is_focused' <<<"$window_json" >/dev/null; then | 35 | if jq -e '.is_focused' <<<"$window_json" >/dev/null; then |
36 | niri msg action focus-workspace-previous | 36 | niri msg action focus-workspace-previous |
37 | else | 37 | else |
38 | niri msg action focus-window --id "$(jq -r '.id' <<<"$window_json")" | 38 | if [[ $(jq -r --arg workspace_name "$workspace_name" 'map(select(.name == $workspace_name)) | .[0].is_focused' <<<"$workspaces_json") != "true" ]] && [[ $(jq -r --arg workspace_name "$workspace_name" 'map(select(.name == $workspace_name)) | .[0].id' <<<"$workspaces_json") = $(jq -r '.workspace_id' <<<"$window_json") ]]; then |
39 | niri msg action focus-workspace "$workspace_name" | ||
40 | else | ||
41 | niri msg action focus-window --id "$(jq -r '.id' <<<"$window_json")" | ||
42 | fi | ||
39 | fi | 43 | fi |
40 | exit 0 | 44 | exit 0 |
41 | fi | 45 | fi |
@@ -45,7 +49,6 @@ let | |||
45 | ''; | 49 | ''; |
46 | }; | 50 | }; |
47 | focus-or-spawn-action = config.lib.niri.actions.spawn (lib.getExe focus_or_spawn); | 51 | focus-or-spawn-action = config.lib.niri.actions.spawn (lib.getExe focus_or_spawn); |
48 | focus-or-spawn-action-app_id = app_id: focus-or-spawn-action ''select(.app_id == "${app_id}")''; | ||
49 | 52 | ||
50 | with_adjacent_workspace = pkgs.writeShellApplication { | 53 | with_adjacent_workspace = pkgs.writeShellApplication { |
51 | name = "with-adjacent-workspace"; | 54 | name = "with-adjacent-workspace"; |
@@ -84,7 +87,7 @@ let | |||
84 | }; | 87 | }; |
85 | with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^${lib.concatMapStringsSep "|" ({ name, ...}: name) cfg.scratchspaces}$"; | 88 | with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^${lib.concatMapStringsSep "|" ({ name, ...}: name) cfg.scratchspaces}$"; |
86 | focus-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; | 89 | focus-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; |
87 | move-column-to-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}}}}''; | 90 | move-column-to-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}, "focus": true}}}''; |
88 | 91 | ||
89 | with_unnamed_workspace = pkgs.writeShellApplication { | 92 | with_unnamed_workspace = pkgs.writeShellApplication { |
90 | name = "with-unnamed-workspace"; | 93 | name = "with-unnamed-workspace"; |
@@ -131,7 +134,7 @@ let | |||
131 | 134 | ||
132 | windows_json="$(niri msg -j windows)" | 135 | windows_json="$(niri msg -j windows)" |
133 | active_workspace="$(jq -r '.[] | select(.is_focused) | .workspace_id' <<<"$windows_json")" | 136 | active_workspace="$(jq -r '.[] | select(.is_focused) | .workspace_id' <<<"$windows_json")" |
134 | window_ix="$(gojq -r --arg active_workspace "$active_workspace" '.[] | select('"$window_select"') | "\(.title)\u0000icon\u001f\(.app_id)"' <<<"$windows_json" | fuzzel --log-level=warning --dmenu --index)" | 137 | window_ix="$(gojq -r --arg active_workspace "$active_workspace" '.[] | select('"$window_select"') | "\(.title)\u0000icon\u001f\(.app_id)"' <<<"$windows_json" | fuzzel --width=60 --log-level=warning --dmenu --index)" |
135 | # shellcheck disable=SC2016 | 138 | # shellcheck disable=SC2016 |
136 | window_json="$(gojq -rc --arg active_workspace "$active_workspace" --arg window_ix "$window_ix" 'map(select('"$window_select"')) | .[($window_ix | tonumber)]' <<<"$windows_json")" | 139 | window_json="$(gojq -rc --arg active_workspace "$active_workspace" --arg window_ix "$window_ix" 'map(select('"$window_select"')) | .[($window_ix | tonumber)]' <<<"$windows_json")" |
137 | 140 | ||
@@ -141,6 +144,25 @@ let | |||
141 | ''; | 144 | ''; |
142 | }; | 145 | }; |
143 | with-select-window-action = config.lib.niri.actions.spawn (lib.getExe with_select_window); | 146 | with-select-window-action = config.lib.niri.actions.spawn (lib.getExe with_select_window); |
147 | |||
148 | with_predicate_window = pred: pkgs.writeShellApplication { | ||
149 | name = "with-predicate-window"; | ||
150 | runtimeInputs = [ niri pkgs.gojq pkgs.socat ]; | ||
151 | text = '' | ||
152 | action="$1" | ||
153 | shift | ||
154 | |||
155 | windows_json="$(niri msg -j windows)" | ||
156 | window_json="$(gojq -rc 'map(select(${pred})) | .[0]' <<<"$windows_json")" | ||
157 | |||
158 | [[ -z "$window_json" || $window_json = "null" ]] && exit 1 | ||
159 | |||
160 | jq -c "$action" <<<"$window_json" | socat STDIO "$NIRI_SOCKET" | ||
161 | ''; | ||
162 | }; | ||
163 | |||
164 | with-urgent-window-action = config.lib.niri.actions.spawn (lib.getExe (with_predicate_window ".is_urgent")); | ||
165 | with-focused-window-action = config.lib.niri.actions.spawn (lib.getExe (with_predicate_window ".is_focused")); | ||
144 | in { | 166 | in { |
145 | imports = [ | 167 | imports = [ |
146 | ./waybar.nix | 168 | ./waybar.nix |
@@ -171,6 +193,17 @@ in { | |||
171 | type = lib.types.nullOr lib.types.str; | 193 | type = lib.types.nullOr lib.types.str; |
172 | default = null; | 194 | default = null; |
173 | }; | 195 | }; |
196 | moveKey = lib.mkOption { | ||
197 | type = lib.types.nullOr lib.types.str; | ||
198 | default = let | ||
199 | keys = lib.splitString "+" config.key; | ||
200 | defMoveKey = lib.concatStringsSep "+" (lib.flatten [ | ||
201 | (lib.take (lib.length keys - 1) keys) | ||
202 | ["Shift"] | ||
203 | (lib.takeEnd 1 keys) | ||
204 | ]); | ||
205 | in if config.key == null then null else defMoveKey; | ||
206 | }; | ||
174 | spawn = lib.mkOption { | 207 | spawn = lib.mkOption { |
175 | type = lib.types.nullOr (lib.types.listOf lib.types.str); | 208 | type = lib.types.nullOr (lib.types.listOf lib.types.str); |
176 | default = null; | 209 | default = null; |
@@ -245,11 +278,11 @@ in { | |||
245 | Service = { | 278 | Service = { |
246 | Type = "simple"; | 279 | Type = "simple"; |
247 | Sockets = [ "niri-workspace-history.socket" ]; | 280 | Sockets = [ "niri-workspace-history.socket" ]; |
248 | ExecStart = pkgs.writers.writePython3 "niri-workspace-history" {} '' | 281 | ExecStart = pkgs.writers.writePython3 "niri-workspace-history" { flakeIgnore = ["E501"]; } '' |
249 | import os | 282 | import os |
250 | import socket | 283 | import socket |
251 | import json | 284 | import json |
252 | import sys | 285 | # import sys |
253 | from collections import defaultdict | 286 | from collections import defaultdict |
254 | from threading import Thread, Lock | 287 | from threading import Thread, Lock |
255 | from socketserver import StreamRequestHandler, ThreadingTCPServer | 288 | from socketserver import StreamRequestHandler, ThreadingTCPServer |
@@ -274,8 +307,8 @@ in { | |||
274 | 307 | ||
275 | def focus_workspace(output, workspace): | 308 | def focus_workspace(output, workspace): |
276 | with history_lock: | 309 | with history_lock: |
277 | workspace_history[output] = [workspace] + [ws for ws in workspace_history[output] if ws != workspace] # noqa: E501 | 310 | workspace_history[output] = [workspace] + [ws for ws in workspace_history[output] if ws != workspace] |
278 | print(json.dumps(workspace_history), file=sys.stderr) | 311 | # print(json.dumps(workspace_history), file=sys.stderr) |
279 | 312 | ||
280 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | 313 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
281 | sock.connect(os.environ["NIRI_SOCKET"]) | 314 | sock.connect(os.environ["NIRI_SOCKET"]) |
@@ -297,14 +330,14 @@ in { | |||
297 | 330 | ||
298 | class RequestHandler(StreamRequestHandler): | 331 | class RequestHandler(StreamRequestHandler): |
299 | def handle(self): | 332 | def handle(self): |
300 | with detaching(TextIOWrapper(self.wfile, encoding='utf-8', write_through=True)) as out: # noqa: E501 | 333 | with detaching(TextIOWrapper(self.wfile, encoding='utf-8', write_through=True)) as out: |
301 | with history_lock: | 334 | with history_lock: |
302 | json.dump(workspace_history, out) | 335 | json.dump(workspace_history, out) |
303 | 336 | ||
304 | 337 | ||
305 | class Server(ThreadingTCPServer): | 338 | class Server(ThreadingTCPServer): |
306 | def __init__(self): | 339 | def __init__(self): |
307 | ThreadingTCPServer.__init__(self, ("", 8000), RequestHandler, bind_and_activate=False) # noqa: E501 | 340 | ThreadingTCPServer.__init__(self, ("", 8000), RequestHandler, bind_and_activate=False) |
308 | self.socket = socket.fromfd(3, self.address_family, self.socket_type) | 341 | self.socket = socket.fromfd(3, self.address_family, self.socket_type) |
309 | 342 | ||
310 | 343 | ||
@@ -330,6 +363,79 @@ in { | |||
330 | ''; | 363 | ''; |
331 | }; | 364 | }; |
332 | }; | 365 | }; |
366 | systemd.user.services.niri-workspace-sort = { | ||
367 | Unit = { | ||
368 | BindsTo = [ "niri.service" ]; | ||
369 | After = [ "niri.service" ]; | ||
370 | }; | ||
371 | Install = { | ||
372 | WantedBy = [ "niri.service" ]; | ||
373 | }; | ||
374 | Service = { | ||
375 | Type = "simple"; | ||
376 | ExecStart = pkgs.writers.writePython3 "niri-workspace-sort" { flakeIgnore = ["E501"]; } '' | ||
377 | import os | ||
378 | import sys | ||
379 | import socket | ||
380 | import json | ||
381 | |||
382 | outputs = None | ||
383 | only = {'HDMI-A-1': {'bmr'}, 'eDP-1': {'vid'}} | ||
384 | |||
385 | |||
386 | class Niri(socket.socket): | ||
387 | def __init__(self): | ||
388 | super().__init__(socket.AF_UNIX, socket.SOCK_STREAM) | ||
389 | super().connect(os.environ["NIRI_SOCKET"]) | ||
390 | self.fh = super().makefile(mode='rw', buffering=1, encoding='utf-8') | ||
391 | |||
392 | def cmd(self, obj): | ||
393 | print(json.dumps(obj, separators=(',', ':')), flush=True, file=self.fh) | ||
394 | |||
395 | def event_stream(self): | ||
396 | self.cmd("EventStream") | ||
397 | return self.fh | ||
398 | |||
399 | |||
400 | with Niri() as niri, Niri().event_stream() as niri_stream: | ||
401 | for line in niri_stream: | ||
402 | workspaces = None | ||
403 | if line_json := json.loads(line): | ||
404 | if "WorkspacesChanged" in line_json: | ||
405 | workspaces = line_json["WorkspacesChanged"]["workspaces"] | ||
406 | |||
407 | if workspaces is None: | ||
408 | continue | ||
409 | |||
410 | old_outputs = outputs | ||
411 | outputs = {ws["output"] for ws in workspaces} | ||
412 | if old_outputs is None: | ||
413 | print("Initial outputs: {}".format(outputs), file=sys.stderr) | ||
414 | continue | ||
415 | |||
416 | new_outputs = outputs - old_outputs | ||
417 | if not new_outputs: | ||
418 | continue | ||
419 | print("New outputs: {}".format(new_outputs), file=sys.stderr) | ||
420 | |||
421 | relevant_workspaces = list(filter(lambda ws: (ws["name"] is not None) or (ws["active_window_id"] is not None), workspaces)) | ||
422 | target_output = next(iter(outputs - set(only.keys()))) | ||
423 | if not target_output: | ||
424 | continue | ||
425 | for ws in relevant_workspaces: | ||
426 | ws_ident = ws["name"] if ws["name"] is not None else (ws["output"], ws["idx"]) | ||
427 | if ws["output"] not in set(only.keys()): | ||
428 | continue | ||
429 | if ws_ident in only[ws["output"]]: | ||
430 | continue | ||
431 | |||
432 | print("{} -> {}".format(ws_ident, target_output), file=sys.stderr) | ||
433 | niri.cmd({"Action": {"MoveWorkspaceToMonitor": {"reference": {"Id": ws["id"]}, "output": target_output}}}) | ||
434 | ''; | ||
435 | Restart = "on-failure"; | ||
436 | RestartSec = 10; | ||
437 | }; | ||
438 | }; | ||
333 | 439 | ||
334 | programs.niri.scratchspaces = [ | 440 | programs.niri.scratchspaces = [ |
335 | { name = "pwctl"; | 441 | { name = "pwctl"; |
@@ -343,7 +449,7 @@ in { | |||
343 | { title = "^Access Request.*"; } | 449 | { title = "^Access Request.*"; } |
344 | { title = ".*Passkey credentials$"; } | 450 | { title = ".*Passkey credentials$"; } |
345 | ]; | 451 | ]; |
346 | windowRuleExtra = [ | 452 | windowRuleExtra = with kdl; [ |
347 | (kdl.leaf "open-focused" false) | 453 | (kdl.leaf "open-focused" false) |
348 | ]; | 454 | ]; |
349 | key = "Mod+Control+P"; | 455 | key = "Mod+Control+P"; |
@@ -371,6 +477,20 @@ in { | |||
371 | app-id = "com.github.wwmm.easyeffects"; | 477 | app-id = "com.github.wwmm.easyeffects"; |
372 | spawn = [ "easyeffects" ]; | 478 | spawn = [ "easyeffects" ]; |
373 | } | 479 | } |
480 | { name = "time"; | ||
481 | key = "Mod+Control+K"; | ||
482 | app-id = "chrome-kimai.yggdrasil.li__-Default"; | ||
483 | spawn = [ (toString (pkgs.resholve.writeScript "kimai" { | ||
484 | interpreter = pkgs.runtimeShell; | ||
485 | inputs = [ pkgs.dex ]; | ||
486 | execer = [ "cannot:${lib.getExe pkgs.dex}" ]; | ||
487 | } '' | ||
488 | exec dex $HOME/.local/state/nix/profile/share/applications/kimai.desktop | ||
489 | '')) ]; | ||
490 | windowRuleExtra = with kdl; [ | ||
491 | (leaf "block-out-from" "screencast") | ||
492 | ]; | ||
493 | } | ||
374 | ]; | 494 | ]; |
375 | programs.niri.config = | 495 | programs.niri.config = |
376 | let | 496 | let |
@@ -415,6 +535,10 @@ in { | |||
415 | ]) | 535 | ]) |
416 | ]) | 536 | ]) |
417 | 537 | ||
538 | (plain "gestures" [ | ||
539 | (plain "hot-corners" [(flag "off")]) | ||
540 | ]) | ||
541 | |||
418 | (plain "environment" (lib.mapAttrsToList leaf { | 542 | (plain "environment" (lib.mapAttrsToList leaf { |
419 | NIXOS_OZONE_WL = "1"; | 543 | NIXOS_OZONE_WL = "1"; |
420 | QT_QPA_PLATFORM = "wayland"; | 544 | QT_QPA_PLATFORM = "wayland"; |
@@ -422,6 +546,10 @@ in { | |||
422 | GDK_BACKEND = "wayland"; | 546 | GDK_BACKEND = "wayland"; |
423 | SDL_VIDEODRIVER = "wayland"; | 547 | SDL_VIDEODRIVER = "wayland"; |
424 | DISPLAY = ":0"; | 548 | DISPLAY = ":0"; |
549 | ELECTRON_OZONE_PLATFORM_HINT = "auto"; | ||
550 | SSH_ASKPASS_REQUIRE = "prefer"; | ||
551 | SSH_ASKPASS = lib.getExe pkgs.kdePackages.ksshaskpass; | ||
552 | SUDO_ASKPASS = lib.getExe pkgs.kdePackages.ksshaskpass; | ||
425 | })) | 553 | })) |
426 | 554 | ||
427 | (node "output" "eDP-1" [ | 555 | (node "output" "eDP-1" [ |
@@ -536,7 +664,7 @@ in { | |||
536 | (plain "window-rule" [ | 664 | (plain "window-rule" [ |
537 | (map (title: | 665 | (map (title: |
538 | (leaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; inherit title; }) | 666 | (leaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; inherit title; }) |
539 | ) ["^Unlock Database.*" "^Access Request.*" ".*Passkey credentials$"]) | 667 | ) ["^Unlock Database.*" "^Access Request.*" ".*Passkey credentials$" "Browser Access Request$"]) |
540 | (leaf "open-focused" true) | 668 | (leaf "open-focused" true) |
541 | (leaf "open-floating" true) | 669 | (leaf "open-floating" true) |
542 | ]) | 670 | ]) |
@@ -565,7 +693,7 @@ in { | |||
565 | (plain "window-rule" [ | 693 | (plain "window-rule" [ |
566 | (leaf "match" { app-id = "^thunderbird$"; }) | 694 | (leaf "match" { app-id = "^thunderbird$"; }) |
567 | (leaf "match" { app-id = "^Element$"; }) | 695 | (leaf "match" { app-id = "^Element$"; }) |
568 | (leaf "match" { app-id = "^Rainbow$"; }) | 696 | (leaf "match" { app-id = "^chrome-web\.openrainbow\.com__-Default$"; }) |
569 | (leaf "open-on-workspace" "comm") | 697 | (leaf "open-on-workspace" "comm") |
570 | ]) | 698 | ]) |
571 | (plain "window-rule" [ | 699 | (plain "window-rule" [ |
@@ -584,6 +712,11 @@ in { | |||
584 | (leaf "open-focused" false) | 712 | (leaf "open-focused" false) |
585 | ]) | 713 | ]) |
586 | (plain "window-rule" [ | 714 | (plain "window-rule" [ |
715 | (leaf "match" { app-id = "^chrome-audiobookshelf\.yggdrasil\.li__-Default$"; }) | ||
716 | (leaf "match" { app-id = "^YouTube Music Desktop App$"; }) | ||
717 | (leaf "open-on-workspace" "vid") | ||
718 | ]) | ||
719 | (plain "window-rule" [ | ||
587 | (leaf "match" { app-id = "^pdfpc$"; }) | 720 | (leaf "match" { app-id = "^pdfpc$"; }) |
588 | (plain "default-column-width" [(leaf "proportion" 1.)]) | 721 | (plain "default-column-width" [(leaf "proportion" 1.)]) |
589 | ]) | 722 | ]) |
@@ -633,6 +766,21 @@ in { | |||
633 | "Mod+Slash".action = show-hotkey-overlay; | 766 | "Mod+Slash".action = show-hotkey-overlay; |
634 | 767 | ||
635 | "Mod+Return".action = spawn terminal; | 768 | "Mod+Return".action = spawn terminal; |
769 | "Mod+Shift+Return".action = | ||
770 | let | ||
771 | nushellKitty = pkgs.symlinkJoin { | ||
772 | name = "nushell-kitty"; | ||
773 | paths = [ config.programs.kitty.package ]; | ||
774 | buildInputs = [ pkgs.makeWrapper ]; | ||
775 | postBuild = '' | ||
776 | wrapProgram $out/bin/kitty \ | ||
777 | --add-flags "--config ${pkgs.writeText "kitty.conf" '' | ||
778 | include $HOME/${config.xdg.configFile."kitty/kitty.conf".target} | ||
779 | shell ${lib.getExe config.programs.nushell.package} | ||
780 | ''}" | ||
781 | ''; | ||
782 | }; | ||
783 | in spawn (lib.getExe' nushellKitty "kitty"); | ||
636 | "Mod+Q".action = close-window; | 784 | "Mod+Q".action = close-window; |
637 | "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package); | 785 | "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package); |
638 | "Mod+Shift+O".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path"; | 786 | "Mod+Shift+O".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path"; |
@@ -668,12 +816,12 @@ in { | |||
668 | done < <(export LC_ALL=C.UTF-8; echo; find "$RESULTS_DIR" -type f -printf $'%T@ %p\n' | sort -n | cut -d' ' -f2- | xargs -r cat) | 816 | done < <(export LC_ALL=C.UTF-8; echo; find "$RESULTS_DIR" -type f -printf $'%T@ %p\n' | sort -n | cut -d' ' -f2- | xargs -r cat) |
669 | $FOUND || echo | 817 | $FOUND || echo |
670 | } | 818 | } |
671 | FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> ") || exit $? | 819 | FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> " --width=60) || exit $? |
672 | if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then | 820 | if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then |
673 | QALC_RES="$FUZZEL_RES" | 821 | QALC_RES="$FUZZEL_RES" |
674 | QALC_RET=0 | 822 | QALC_RET=0 |
675 | else | 823 | else |
676 | QALC_RES=$(qalc "$FUZZEL_RES" 2>&1) | 824 | QALC_RES=$(qalc -set "autocalc off" "$FUZZEL_RES" 2>&1) |
677 | QALC_RET=$? | 825 | QALC_RET=$? |
678 | fi | 826 | fi |
679 | [[ -n "$QALC_RES" ]] || exit 1 | 827 | [[ -n "$QALC_RES" ]] || exit 1 |
@@ -693,18 +841,33 @@ in { | |||
693 | notify-send "$QALC_RES" | 841 | notify-send "$QALC_RES" |
694 | ''; | 842 | ''; |
695 | })); | 843 | })); |
844 | "Mod+Shift+U".action = | ||
845 | let | ||
846 | qalcKitty = pkgs.symlinkJoin { | ||
847 | name = "qalc-kitty"; | ||
848 | paths = [ config.programs.kitty.package ]; | ||
849 | buildInputs = [ pkgs.makeWrapper ]; | ||
850 | postBuild = '' | ||
851 | wrapProgram $out/bin/kitty \ | ||
852 | --add-flags "--config ${pkgs.writeText "kitty.conf" '' | ||
853 | include $HOME/${config.xdg.configFile."kitty/kitty.conf".target} | ||
854 | shell ${lib.getExe pkgs.libqalculate} | ||
855 | ''}" | ||
856 | ''; | ||
857 | }; | ||
858 | in spawn (lib.getExe' qalcKitty "kitty"); | ||
696 | "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication { | 859 | "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication { |
697 | name = "emoji-fuzzel"; | 860 | name = "emoji-fuzzel"; |
698 | runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ]; | 861 | runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ]; |
699 | text = '' | 862 | text = '' |
700 | FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " <"$HOME"/.local/share/emoji-data/list.txt) || exit $? | 863 | FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " --cache "$HOME"/.cache/fuzzel-emoji --width=60 <"$HOME"/.local/share/emoji-data/list.txt) || exit $? |
701 | [[ -n "$FUZZEL_RES" ]] || exit 1 | 864 | [[ -n "$FUZZEL_RES" ]] || exit 1 |
702 | wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste | 865 | wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste |
703 | ''; | 866 | ''; |
704 | })); | 867 | })); |
705 | "Print".action = screenshot; | 868 | "Print".action = screenshot; |
706 | "Control+Print".action = screenshot-window; | 869 | "Control+Print".action = screenshot-window; |
707 | # "Shift+Print".action = screenshot-screen; | 870 | "Shift+Print".action = kdl.magic-leaf "screenshot-screen"; |
708 | "Mod+B".action = with-select-window-action ".workspace_id == ($active_workspace | tonumber)" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; | 871 | "Mod+B".action = with-select-window-action ".workspace_id == ($active_workspace | tonumber)" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; |
709 | "Mod+Shift+B".action = with-select-window-action "true" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; | 872 | "Mod+Shift+B".action = with-select-window-action "true" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; |
710 | 873 | ||
@@ -743,22 +906,22 @@ in { | |||
743 | "Mod+Shift+Control+C".action = move-workspace-up; | 906 | "Mod+Shift+Control+C".action = move-workspace-up; |
744 | 907 | ||
745 | "Mod+ParenLeft".action = focus-workspace "comm"; | 908 | "Mod+ParenLeft".action = focus-workspace "comm"; |
746 | "Mod+Shift+ParenLeft".action = move-column-to-workspace "comm"; | 909 | "Mod+Shift+ParenLeft".action = kdl.magic-leaf "move-column-to-workspace" "comm"; |
747 | 910 | ||
748 | "Mod+ParenRight".action = focus-workspace "web"; | 911 | "Mod+ParenRight".action = focus-workspace "web"; |
749 | "Mod+Shift+ParenRight".action = move-column-to-workspace "web"; | 912 | "Mod+Shift+ParenRight".action = kdl.magic-leaf "move-column-to-workspace" "web"; |
750 | 913 | ||
751 | "Mod+BraceRight".action = focus-workspace "read"; | 914 | "Mod+BraceRight".action = focus-workspace "read"; |
752 | "Mod+Shift+BraceRight".action = move-column-to-workspace "read"; | 915 | "Mod+Shift+BraceRight".action = kdl.magic-leaf "move-column-to-workspace" "read"; |
753 | 916 | ||
754 | "Mod+BraceLeft".action = focus-workspace "mon"; | 917 | "Mod+BraceLeft".action = focus-workspace "mon"; |
755 | "Mod+Shift+BraceLeft".action = move-column-to-workspace "mon"; | 918 | "Mod+Shift+BraceLeft".action = kdl.magic-leaf "move-column-to-workspace" "mon"; |
756 | 919 | ||
757 | "Mod+Asterisk".action = focus-workspace "vid"; | 920 | "Mod+Asterisk".action = focus-workspace "vid"; |
758 | "Mod+Shift+Asterisk".action = move-column-to-workspace "vid"; | 921 | "Mod+Shift+Asterisk".action = kdl.magic-leaf "move-column-to-workspace" "vid"; |
759 | 922 | ||
760 | "Mod+Plus".action = with-unnamed-workspace-action ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; | 923 | "Mod+Plus".action = with-unnamed-workspace-action ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; |
761 | "Mod+Shift+Plus".action = with-unnamed-workspace-action ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}}}}''; | 924 | "Mod+Shift+Plus".action = with-unnamed-workspace-action ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}, "focus": true}}}''; |
762 | 925 | ||
763 | "Mod+M".action = consume-or-expel-window-left; | 926 | "Mod+M".action = consume-or-expel-window-left; |
764 | "Mod+W".action = consume-or-expel-window-right; | 927 | "Mod+W".action = consume-or-expel-window-right; |
@@ -766,10 +929,11 @@ in { | |||
766 | "Mod+Shift+M".action = toggle-column-tabbed-display; | 929 | "Mod+Shift+M".action = toggle-column-tabbed-display; |
767 | 930 | ||
768 | "Mod+R".action = switch-preset-column-width; | 931 | "Mod+R".action = switch-preset-column-width; |
769 | "Mod+Shift+R".action = switch-preset-window-height; | 932 | "Mod+Shift+R".action = maximize-column; |
933 | "Mod+Shift+Ctrl+R".action = switch-preset-window-height; | ||
770 | "Mod+F".action = center-column; | 934 | "Mod+F".action = center-column; |
771 | "Mod+Shift+F".action = maximize-column; | 935 | "Mod+Shift+F".action = toggle-windowed-fullscreen; |
772 | "Mod+Shift+Ctrl+F".action = fullscreen-window; | 936 | "Mod+Ctrl+Shift+F".action = fullscreen-window; |
773 | 937 | ||
774 | "Mod+V".action = switch-focus-between-floating-and-tiling; | 938 | "Mod+V".action = switch-focus-between-floating-and-tiling; |
775 | "Mod+Shift+V".action = toggle-window-floating; | 939 | "Mod+Shift+V".action = toggle-window-floating; |
@@ -825,13 +989,24 @@ in { | |||
825 | 989 | ||
826 | "Mod+Semicolon".action = spawn makoctl "dismiss" "--group"; | 990 | "Mod+Semicolon".action = spawn makoctl "dismiss" "--group"; |
827 | "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all"; | 991 | "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all"; |
828 | "Mod+Period".action = spawn makoctl "menu" (lib.getExe config.programs.fuzzel.package) "--dmenu"; | 992 | "Mod+Period".action = spawn makoctl "menu" "--" (lib.getExe config.programs.fuzzel.package) "--dmenu"; |
829 | "Mod+Comma".action = spawn makoctl "restore"; | 993 | "Mod+Comma".action = spawn makoctl "restore"; |
830 | 994 | ||
831 | "Mod+Control+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"FocusWorkspace\":{\"reference\":{\"Id\": $workspace_id}}}}"; | 995 | "Mod+Control+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"FocusWorkspace\":{\"reference\":{\"Id\": $workspace_id}}}}"; |
832 | "Mod+Control+Shift+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"MoveColumnToWorkspace\":{\"reference\":{\"Id\": $workspace_id}}}}"; | 996 | "Mod+Control+Shift+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"MoveColumnToWorkspace\":{\"reference\":{\"Id\": $workspace_id}, \"focus\": true}}}"; |
997 | |||
998 | "Mod+X".action = set-dynamic-cast-window; | ||
999 | "Mod+Shift+X".action = set-dynamic-cast-monitor; | ||
1000 | "Mod+Control+Shift+X".action = clear-dynamic-cast-target; | ||
1001 | |||
1002 | "Mod+D".action = with-urgent-window-action "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; | ||
1003 | "Mod+Shift+D".action = with-focused-window-action "{\"Action\":{\"UnsetUrgent\":{\"id\": .id}}}"; | ||
1004 | |||
1005 | "Mod+K".action = spawn (lib.getExe' pkgs.worktime "worktime-ui"); | ||
1006 | "Mod+Shift+K".action = spawn (lib.getExe' pkgs.worktime "worktime-stop"); | ||
833 | })) | 1007 | })) |
834 | (map ({ name, selector, spawn, key, ...}: if key != null && selector != null && spawn != null then bind key { action = focus-or-spawn-action selector name spawn; } else null) cfg.scratchspaces) | 1008 | (map ({ name, selector, spawn, key, ...}: if key != null && selector != null && spawn != null then bind key { action = focus-or-spawn-action selector name spawn; } else null) cfg.scratchspaces) |
1009 | (map ({ name, moveKey, ...}: if moveKey != null then bind moveKey { action = kdl.magic-leaf "move-column-to-workspace" name; } else null) cfg.scratchspaces) | ||
835 | ] | 1010 | ] |
836 | )) | 1011 | )) |
837 | ]; | 1012 | ]; |
diff --git a/accounts/gkleen@sif/niri/mako.nix b/accounts/gkleen@sif/niri/mako.nix index 2788fb82..eba26caa 100644 --- a/accounts/gkleen@sif/niri/mako.nix +++ b/accounts/gkleen@sif/niri/mako.nix | |||
@@ -3,37 +3,31 @@ | |||
3 | config = { | 3 | config = { |
4 | services.mako = { | 4 | services.mako = { |
5 | enable = true; | 5 | enable = true; |
6 | font = "Fira Sans 10"; | 6 | settings = { |
7 | format = "<i>%s</i>\\n%b"; | 7 | font = "Fira Sans 10"; |
8 | margin = "2"; | 8 | format = "<i>%s</i>\\n%b"; |
9 | maxVisible = -1; | 9 | margin = "2"; |
10 | backgroundColor = "#000000dd"; | 10 | max-visible = -1; |
11 | progressColor = "source #223544ff"; | 11 | background-color = "#000000dd"; |
12 | width = 384; | 12 | progress-color = "source #223544ff"; |
13 | extraConfig = '' | 13 | width = 384; |
14 | outer-margin=1 | 14 | outer-margin = 1; |
15 | max-history=100 | 15 | max-history = 100; |
16 | max-icon-size=48 | 16 | max-icon-size = 48; |
17 | 17 | }; | |
18 | [grouped] | 18 | criteria = { |
19 | format=<b>(%g)</b> <i>%s</i>\n%b | 19 | grouped.format = "<b>(%g)</b> <i>%s</i>\\n%b"; |
20 | 20 | "urgency=low".text-color = "#999999ff"; | |
21 | [urgency=low] | 21 | "urgency=critical".background-color = "#900000dd"; |
22 | text-color=#999999ff | 22 | "app-name=Element".group-by = "summary"; |
23 | 23 | "app-name=poweralertd" = { | |
24 | [urgency=critical] | 24 | history = false; |
25 | background-color=#900000dd | 25 | ignore-timeout = true; |
26 | 26 | default-timeout = 2000; | |
27 | [app-name=Element] | 27 | }; |
28 | group-by=summary | 28 | "app-name=worktime".history = false; |
29 | 29 | "mode=silent".invisible = true; | |
30 | [app-name=poweralertd] | 30 | }; |
31 | ignore-timeout=1 | ||
32 | default-timeout=2000 | ||
33 | |||
34 | [mode=silent] | ||
35 | invisible=1 | ||
36 | ''; | ||
37 | package = pkgs.symlinkJoin { | 31 | package = pkgs.symlinkJoin { |
38 | name = "${pkgs.mako.name}-wrapped"; | 32 | name = "${pkgs.mako.name}-wrapped"; |
39 | paths = with pkgs; [ mako ]; | 33 | paths = with pkgs; [ mako ]; |
diff --git a/accounts/gkleen@sif/niri/swayosd.nix b/accounts/gkleen@sif/niri/swayosd.nix index 984927c2..54ebb302 100644 --- a/accounts/gkleen@sif/niri/swayosd.nix +++ b/accounts/gkleen@sif/niri/swayosd.nix | |||
@@ -3,9 +3,10 @@ | |||
3 | config = { | 3 | config = { |
4 | services.swayosd = { | 4 | services.swayosd = { |
5 | enable = true; | 5 | enable = true; |
6 | topMargin = 0.946154; | 6 | topMargin = 0.4769706078; |
7 | stylePath = pkgs.runCommand "style.css" { | 7 | stylePath = pkgs.runCommand "style.css" { |
8 | src = pkgs.writeText "style.scss" '' | 8 | passAsFile = [ "src" ]; |
9 | src = '' | ||
9 | window#osd { | 10 | window#osd { |
10 | padding: 12px 20px; | 11 | padding: 12px 20px; |
11 | border-radius: 999px; | 12 | border-radius: 999px; |
@@ -59,7 +60,7 @@ | |||
59 | } | 60 | } |
60 | ''; | 61 | ''; |
61 | buildInputs = with pkgs; [sass]; | 62 | buildInputs = with pkgs; [sass]; |
62 | } "scss -C --sourcemap=none --style=compact $src $out"; | 63 | } "scss -C --sourcemap=none --style=compact $srcPath $out"; |
63 | }; | 64 | }; |
64 | }; | 65 | }; |
65 | } | 66 | } |
diff --git a/accounts/gkleen@sif/niri/waybar.nix b/accounts/gkleen@sif/niri/waybar.nix index cc131c08..c02a9a76 100644 --- a/accounts/gkleen@sif/niri/waybar.nix +++ b/accounts/gkleen@sif/niri/waybar.nix | |||
@@ -27,8 +27,14 @@ in { | |||
27 | modules-right = [ "custom/worktime" "custom/worktime-today" | 27 | modules-right = [ "custom/worktime" "custom/worktime-today" |
28 | "custom/weather" | 28 | "custom/weather" |
29 | "custom/keymap" | 29 | "custom/keymap" |
30 | "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "custom/mako" "clock" ]; | 30 | "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "custom/mako" "custom/lid_inhibitor" "clock" ]; |
31 | 31 | ||
32 | "custom/lid_inhibitor" = { | ||
33 | format = "{}"; | ||
34 | return-type = "json"; | ||
35 | exec = lib.getExe pkgs.waybar-systemd-inhibit; | ||
36 | on-click = lib.getExe' pkgs.waybar-systemd-inhibit "waybar-systemd-inhibit-toggle"; | ||
37 | }; | ||
32 | "custom/mako" = { | 38 | "custom/mako" = { |
33 | format = "{}"; | 39 | format = "{}"; |
34 | return-type = "json"; | 40 | return-type = "json"; |
@@ -211,7 +217,7 @@ in { | |||
211 | layer = "top"; | 217 | layer = "top"; |
212 | position = "top"; | 218 | position = "top"; |
213 | height = 14; | 219 | height = 14; |
214 | output = [ "!eDP-1" "!DP-2" "!DP-3" ]; | 220 | output = [ "!eDP-1" "!DP-2" "!DP-3" "*" ]; |
215 | modules-left = [ "niri/workspaces" ]; | 221 | modules-left = [ "niri/workspaces" ]; |
216 | modules-center = [ "niri/window" ]; | 222 | modules-center = [ "niri/window" ]; |
217 | modules-right = [ "clock" ]; | 223 | modules-right = [ "clock" ]; |
@@ -293,7 +299,7 @@ in { | |||
293 | #tray { | 299 | #tray { |
294 | margin: 0; | 300 | margin: 0; |
295 | } | 301 | } |
296 | #battery, #idle_inhibitor, #backlight, #wireplumber, #custom-mako { | 302 | #battery, #idle_inhibitor, #backlight, #wireplumber, #custom-mako, #custom-lid_inhibitor { |
297 | color: @grey; | 303 | color: @grey; |
298 | margin: 0 5px 0 2px; | 304 | margin: 0 5px 0 2px; |
299 | } | 305 | } |
@@ -302,7 +308,11 @@ in { | |||
302 | margin-left: 6px; | 308 | margin-left: 6px; |
303 | } | 309 | } |
304 | #custom-mako { | 310 | #custom-mako { |
305 | margin-right: 2px; | 311 | margin-right: 4px; |
312 | margin-left: 3px; | ||
313 | } | ||
314 | #custom-lid_inhibitor { | ||
315 | margin-right: 3px; | ||
306 | margin-left: 3px; | 316 | margin-left: 3px; |
307 | } | 317 | } |
308 | #battery { | 318 | #battery { |
@@ -330,7 +340,7 @@ in { | |||
330 | color: @orange; | 340 | color: @orange; |
331 | } | 341 | } |
332 | 342 | ||
333 | #idle_inhibitor { | 343 | #idle_inhibitor, #custom-lid_inhibitor { |
334 | padding-top: 1px; | 344 | padding-top: 1px; |
335 | } | 345 | } |
336 | 346 | ||
@@ -340,6 +350,7 @@ in { | |||
340 | } | 350 | } |
341 | #clock { | 351 | #clock { |
342 | /* margin-right: 5px; */ | 352 | /* margin-right: 5px; */ |
353 | font-feature-settings: "tnum"; | ||
343 | } | 354 | } |
344 | ''; | 355 | ''; |
345 | }; | 356 | }; |
diff --git a/accounts/gkleen@sif/synadm/default.nix b/accounts/gkleen@sif/synadm/default.nix new file mode 100644 index 00000000..0a8e0d4c --- /dev/null +++ b/accounts/gkleen@sif/synadm/default.nix | |||
@@ -0,0 +1,9 @@ | |||
1 | { config, pkgs, ... }: | ||
2 | { | ||
3 | home.packages = with pkgs; [ synadm ]; | ||
4 | sops.secrets."synadm.yaml" = { | ||
5 | format = "binary"; | ||
6 | sopsFile = ./synadm_yaml; | ||
7 | path = config.xdg.configHome + "/synadm.yaml"; | ||
8 | }; | ||
9 | } | ||
diff --git a/accounts/gkleen@sif/synadm/synadm_yaml b/accounts/gkleen@sif/synadm/synadm_yaml new file mode 100644 index 00000000..8d951ccc --- /dev/null +++ b/accounts/gkleen@sif/synadm/synadm_yaml | |||
@@ -0,0 +1,15 @@ | |||
1 | { | ||
2 | "data": "ENC[AES256_GCM,data:qJy4Pmbbxja4jmW7OaHsD0mQZ7anZwLhiVmAgkavb+CqwWGDnUBXdz22/MHCbxng5NshcFSpBoCBhgY6B9V2bUiES6bH9AtMlDcs9ebKGMArBTUTnQ2MjWQGfQTqraWdNgy+n327uj9swwCH8EZXdYH/Hlv0t/re470W+VOHeXhGghQ3Y9IGz2sgfvMGr8QxaJNydZz85rgs5QUP/PglCwWIOw2mY1EX2vYwnmiAo49LmIEaxWvRi++KHaeBveDt0nlkJwzUlipL2VOKWxkgpK3yGucQn2mz+FRe1btp+4KGm8H17eUI9FO9sBwq,iv:kgM921ovwCgDYHQj3c5Rupy/8JxHehxUD2jb1k9Ik2Y=,tag:3TLQkJbv679VWy8V2TMugw==,type:str]", | ||
3 | "sops": { | ||
4 | "age": [ | ||
5 | { | ||
6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6bzVHUGNxZTF2WC9MYmZr\neGdVVzJXN3lGdEk3cTBER3J6UTFtcUJna2d3CjdNQmRXd2haZW1MYlJzNkk1dWVD\nVTFQc2gvS0JrejJ6SFh2MXpPWDZpRE0KLS0tIE0wTC85bEpvSnlGdGFkZVFhNjFZ\nbzRiZkxMWUg2ODNVUlBmNFlPNGRrZlkK1VXLJWcssv3ETyZSSM/Hhn5VIaI9iov9\nzShZA9Zx/FX6PYTuUMC29pJ57gKourcIxa/7HwSv/xYn1A6WcYfgSg==\n-----END AGE ENCRYPTED FILE-----\n" | ||
8 | } | ||
9 | ], | ||
10 | "lastmodified": "2025-05-18T11:03:42Z", | ||
11 | "mac": "ENC[AES256_GCM,data:yonJC68PhilAgEHNNJQ8nO53Qo3rx/LnfiOWfuMm24bOUIH9QM3WZZxpigd7bHI4eC4TqRb4LvcSi0nEURTRAhwiTqGNrWbpw2Iv3n5dhLEN9aTcetG5ZuhaXqfVUoML45/ovdBZG/0l8+XIHqxN2M/g/h4JwKoR/6lqzcrVhgo=,iv:xvxBJwy+E5zUdjhGPdZPdy7tnBIEj50hfiDJFsS3wNg=,tag:L4Fas36ZOg4h0QQwC4gjNA==,type:str]", | ||
12 | "unencrypted_suffix": "_unencrypted", | ||
13 | "version": "3.10.2" | ||
14 | } | ||
15 | } | ||
diff --git a/accounts/gkleen@sif/systemd.nix b/accounts/gkleen@sif/systemd.nix index 2237b708..90cccc58 100644 --- a/accounts/gkleen@sif/systemd.nix +++ b/accounts/gkleen@sif/systemd.nix | |||
@@ -242,7 +242,7 @@ in { | |||
242 | "-${lib.getExe pkgs.playerctl} -a pause" | 242 | "-${lib.getExe pkgs.playerctl} -a pause" |
243 | "-${lib.getExe (pkgs.writeShellApplication { | 243 | "-${lib.getExe (pkgs.writeShellApplication { |
244 | name = "generate-css"; | 244 | name = "generate-css"; |
245 | runtimeInputs = with pkgs; [cfg.programs.wpaperd.package jq coreutils imagemagick findutils]; | 245 | runtimeInputs = with pkgs; [cfg.services.wpaperd.package jq coreutils imagemagick findutils]; |
246 | text = '' | 246 | text = '' |
247 | declare -A monitors | 247 | declare -A monitors |
248 | monitors=() | 248 | monitors=() |
@@ -333,21 +333,21 @@ in { | |||
333 | ExecStopPost = "${pkgs.coreutils}/bin/rm -rfv \"$CACHE_DIRECTORY\""; | 333 | ExecStopPost = "${pkgs.coreutils}/bin/rm -rfv \"$CACHE_DIRECTORY\""; |
334 | }; | 334 | }; |
335 | }; | 335 | }; |
336 | wpaperd = { | 336 | # wpaperd = { |
337 | Install = { | 337 | # Install = { |
338 | WantedBy = ["graphical-session.target"]; | 338 | # WantedBy = ["graphical-session.target"]; |
339 | }; | 339 | # }; |
340 | Unit = { | 340 | # Unit = { |
341 | After = [ "graphical-session.target" ]; | 341 | # After = [ "graphical-session.target" ]; |
342 | PartOf = [ "graphical-session.target" ]; | 342 | # PartOf = [ "graphical-session.target" ]; |
343 | }; | 343 | # }; |
344 | Service = { | 344 | # Service = { |
345 | ExecStart = lib.getExe cfg.programs.wpaperd.package; | 345 | # ExecStart = lib.getExe cfg.services.wpaperd.package; |
346 | Type = "simple"; | 346 | # Type = "simple"; |
347 | Restart = "always"; | 347 | # Restart = "always"; |
348 | RestartSec = "2s"; | 348 | # RestartSec = "2s"; |
349 | }; | 349 | # }; |
350 | }; | 350 | # }; |
351 | xembed-sni-proxy = { | 351 | xembed-sni-proxy = { |
352 | Unit = { | 352 | Unit = { |
353 | PartOf = lib.mkForce ["tray.target"]; | 353 | PartOf = lib.mkForce ["tray.target"]; |
diff --git a/accounts/gkleen@sif/utils/async-yt-dlp.nix b/accounts/gkleen@sif/utils/async-yt-dlp.nix new file mode 100644 index 00000000..c3b82ec5 --- /dev/null +++ b/accounts/gkleen@sif/utils/async-yt-dlp.nix | |||
@@ -0,0 +1,57 @@ | |||
1 | { writers, python3Packages, ... }: | ||
2 | |||
3 | writers.writePython3Bin "async-yt-dlp" { | ||
4 | libraries = with python3Packages; [ yt-dlp ]; | ||
5 | flakeIgnore = ["E501"]; | ||
6 | } '' | ||
7 | import sys | ||
8 | import os | ||
9 | |||
10 | import yt_dlp | ||
11 | import yt_dlp.options | ||
12 | from collections import namedtuple | ||
13 | import socket | ||
14 | from pathlib import Path | ||
15 | import json | ||
16 | |||
17 | create_parser = yt_dlp.options.create_parser | ||
18 | |||
19 | |||
20 | def parse_patched_options(opts): | ||
21 | patched_parser = create_parser() | ||
22 | patched_parser.defaults.update({ | ||
23 | 'ignoreerrors': False, | ||
24 | 'retries': 0, | ||
25 | 'fragment_retries': 0, | ||
26 | 'extract_flat': False, | ||
27 | 'concat_playlist': 'never', | ||
28 | }) | ||
29 | yt_dlp.options.create_parser = lambda: patched_parser | ||
30 | try: | ||
31 | return yt_dlp.parse_options(opts) | ||
32 | finally: | ||
33 | yt_dlp.options.create_parser = create_parser | ||
34 | |||
35 | |||
36 | default_opts = parse_patched_options([]).ydl_opts | ||
37 | |||
38 | |||
39 | def cli_to_api(opts): | ||
40 | opts = parse_patched_options(opts) | ||
41 | urls = opts.urls | ||
42 | opts = opts.ydl_opts | ||
43 | |||
44 | diff = {k: v for k, v in opts.items() if default_opts[k] != v} | ||
45 | if 'postprocessors' in diff: | ||
46 | diff['postprocessors'] = [pp for pp in diff['postprocessors'] | ||
47 | if pp not in default_opts['postprocessors']] | ||
48 | return namedtuple('Options', ('params', 'urls'))(diff, urls) | ||
49 | |||
50 | |||
51 | if __name__ == '__main__': | ||
52 | opts = cli_to_api(sys.argv[1:]) | ||
53 | with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock: | ||
54 | sock.connect(str(Path(os.environ["XDG_RUNTIME_DIR"]) / "yt-dlp.sock").encode('utf-8')) | ||
55 | with sock.makefile(mode='w', buffering=1, encoding='utf-8') as fh: | ||
56 | json.dump(opts._asdict(), fh) | ||
57 | '' | ||
diff --git a/accounts/gkleen@sif/zshrc b/accounts/gkleen@sif/zshrc index c628e2e9..abc200c6 100644 --- a/accounts/gkleen@sif/zshrc +++ b/accounts/gkleen@sif/zshrc | |||
@@ -2,17 +2,14 @@ dir() { | |||
2 | curlArchive=false | 2 | curlArchive=false |
3 | templateArchive="" | 3 | templateArchive="" |
4 | repoUrl="" | 4 | repoUrl="" |
5 | nixShell="" | ||
6 | findNix=false | ||
7 | dir="" | 5 | dir="" |
8 | forceShell=false | 6 | forceShell=false |
9 | wormhole=false | 7 | wormhole=false |
10 | gitWorktree="" | 8 | gitWorktree="" |
11 | # notmuchMsg="" | ||
12 | quickserve=false | ||
13 | modifyPDF="" | 9 | modifyPDF="" |
10 | miniserve=false | ||
14 | 11 | ||
15 | while getopts ':t:a:s:Sd:ir:wqg:p:' arg; do | 12 | while getopts ':t:a:d:ir:wg:p:m' arg; do |
16 | case $arg in | 13 | case $arg in |
17 | "t") ;; | 14 | "t") ;; |
18 | "a") | 15 | "a") |
@@ -23,16 +20,13 @@ dir() { | |||
23 | templateArchive=${OPTARG:a} | 20 | templateArchive=${OPTARG:a} |
24 | fi | 21 | fi |
25 | ;; | 22 | ;; |
26 | "s") nixShell=${OPTARG:a} ;; | ||
27 | "S") findNix=true ;; | ||
28 | "d") dir=${OPTARG} ;; | 23 | "d") dir=${OPTARG} ;; |
29 | "i") forceShell=true ;; | 24 | "i") forceShell=true ;; |
30 | "r") repoUrl=${OPTARG} ;; | 25 | "r") repoUrl=${OPTARG} ;; |
31 | "w") wormhole=true ;; | 26 | "w") wormhole=true ;; |
32 | "g") gitWorktree=${OPTARG} ;; | 27 | "g") gitWorktree=${OPTARG} ;; |
33 | # "n") notmuchMsg=${OPTARG} ;; | ||
34 | "q") quickserve=true ;; | ||
35 | "p") modifyPDF=${OPTARG:a} ;; | 28 | "p") modifyPDF=${OPTARG:a} ;; |
29 | "m") miniserve=true ;; | ||
36 | *) printf "Invalid option: %s\n" $arg >&2; exit 2 ;; | 30 | *) printf "Invalid option: %s\n" $arg >&2; exit 2 ;; |
37 | esac | 31 | esac |
38 | done | 32 | done |
@@ -56,20 +50,34 @@ dir() { | |||
56 | gitWorktree="" | 50 | gitWorktree="" |
57 | fi | 51 | fi |
58 | 52 | ||
53 | miniservePIDFile="" | ||
54 | if [[ ${miniserve} = "true" ]]; then | ||
55 | miniservePIDFile=$(mktemp --tmpdir --suffix=.pid) | ||
56 | fi | ||
57 | |||
59 | cleanup() | 58 | cleanup() |
60 | { | 59 | { |
61 | cd ${modifyPDF:h} | 60 | if [[ -n ${modifyPDF} ]]; then |
62 | [[ -n ${modifyPDF} ]] && nix shell 'nixos#imagemagick' -c convert -verbose ${dir}/${modifyPDF:t:r}_*.png(on) ${modifyPDF} | 61 | cd ${modifyPDF:h} |
62 | typeset -a pages | ||
63 | eval 'pages=(${dir}/${modifyPDF:t:r}_*.png(on))' | ||
64 | magick -verbose "$pages" ${modifyPDF} | ||
65 | modifyPDF="" | ||
66 | fi | ||
67 | if [[ -n ${miniservePIDFile} ]]; then | ||
68 | command kill --verbose -- $(cat ${miniservePIDFile}) && wait $(cat ${miniservePIDFile}) | ||
69 | miniservePIDFile="" | ||
70 | fi | ||
63 | } | 71 | } |
64 | 72 | ||
65 | ( | 73 | ( |
74 | set -o localoptions -o localtraps | ||
75 | trap 'return 1' INT TERM | ||
66 | trap cleanup EXIT | 76 | trap cleanup EXIT |
67 | 77 | ||
68 | cd ${dir} | 78 | cd ${dir} |
69 | export dir; | 79 | export dir; |
70 | 80 | ||
71 | ${findNix} && { nixShell=$(findNix) || return $? } | ||
72 | |||
73 | [[ -n ${repoUrl} ]] && git clone -- ${repoUrl} . | 81 | [[ -n ${repoUrl} ]] && git clone -- ${repoUrl} . |
74 | 82 | ||
75 | [[ -n ${modifyPDF} ]] && templateArchive=${modifyPDF} | 83 | [[ -n ${modifyPDF} ]] && templateArchive=${modifyPDF} |
@@ -82,7 +90,7 @@ dir() { | |||
82 | } | 90 | } |
83 | trap cleanup EXIT | 91 | trap cleanup EXIT |
84 | 92 | ||
85 | if ${curlArchive}; then | 93 | if [[ $curlArchive = "true" ]]; then |
86 | archiveFile=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t}") | 94 | archiveFile=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t}") |
87 | 95 | ||
88 | curl -L -o ${archiveFile} ${templateArchive} | 96 | curl -L -o ${archiveFile} ${templateArchive} |
@@ -91,14 +99,14 @@ dir() { | |||
91 | fi | 99 | fi |
92 | 100 | ||
93 | unpack=true | 101 | unpack=true |
94 | while ${unpack}; do | 102 | while [[ $unpack = "true" ]]; do |
95 | case $(file --brief --mime-type --dereference ${templateArchive}) in | 103 | case $(file --brief --mime-type --dereference ${templateArchive}) in |
96 | application/zip) | 104 | application/zip) |
97 | unzip ${templateArchive} | 105 | unzip ${templateArchive} |
98 | unpack=false | 106 | unpack=false |
99 | ;; | 107 | ;; |
100 | application/vnd.debian.binary-package) | 108 | application/vnd.debian.binary-package) |
101 | nix shell 'nixos#binutils' --command ar x ${templateArchive} | 109 | ar x ${templateArchive} |
102 | mkdir control data | 110 | mkdir control data |
103 | tar -C control -xvaf control.* | 111 | tar -C control -xvaf control.* |
104 | tar -C data -xvaf data.* | 112 | tar -C data -xvaf data.* |
@@ -106,7 +114,7 @@ dir() { | |||
106 | ;; | 114 | ;; |
107 | application/x-rpm) | 115 | application/x-rpm) |
108 | cpioArchive=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t:r}.cpio") | 116 | cpioArchive=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t:r}.cpio") |
109 | nix shell 'nixos#busybox' --command rpm2cpio ${templateArchive} > ${cpioArchive} | 117 | rpm2cpio ${templateArchive} > ${cpioArchive} |
110 | templateArchive=${cpioArchive} | 118 | templateArchive=${cpioArchive} |
111 | unpack=true | 119 | unpack=true |
112 | ;; | 120 | ;; |
@@ -115,12 +123,12 @@ dir() { | |||
115 | unpack=false | 123 | unpack=false |
116 | ;; | 124 | ;; |
117 | application/pdf) | 125 | application/pdf) |
118 | nix shell 'nixos#ghostscript' 'nixos#imagemagick' -c convert -verbose -density 400 ${templateArchive} ${modifyPDF:t:r}_%0d.png | 126 | magick -verbose -density 400 ${templateArchive} ${modifyPDF:t:r}_%0d.png |
119 | unpack=false | 127 | unpack=false |
120 | ;; | 128 | ;; |
121 | application/octet-stream) | 129 | application/octet-stream) |
122 | if [[ $(file --brief --dereferenc ${templateArchive}) =~ Squashfs ]]; then | 130 | if [[ $(file --brief --dereference ${templateArchive}) =~ Squashfs ]]; then |
123 | nix shell 'nixos#squashfsTools' -c unsquashfs -d . ${templateArchive} | 131 | unsquashfs -d . ${templateArchive} |
124 | unpack=false | 132 | unpack=false |
125 | fi | 133 | fi |
126 | ;; | 134 | ;; |
@@ -134,25 +142,21 @@ dir() { | |||
134 | fi | 142 | fi |
135 | 143 | ||
136 | 144 | ||
137 | ${wormhole} && wormhole receive --accept-file | 145 | [[ $wormhole = "true" ]] && wormhole receive --accept-file |
138 | 146 | ||
139 | 147 | ||
140 | if ${quickserve}; then | 148 | if [[ ${#@} -gt 0 ]]; then |
141 | quickserve --root . --upload . --show-hidden --tar gz | 149 | ${@} |
142 | fi | 150 | fi |
143 | 151 | ||
152 | cd $(pwd) # Needed for mounting to work | ||
144 | 153 | ||
145 | if [[ ${#@} -eq 0 ]] || ${forceShell}; then | 154 | if [[ ${miniserve} = "true" ]]; then |
146 | if [[ ${#@} -gt 0 ]]; then | 155 | miniserve --random-route --hidden --enable-tar-gz --enable-zip . & |
147 | if [[ -z ${nixShell} ]]; then | 156 | echo $! > "${miniservePIDFile}" |
148 | ${@} | 157 | fi |
149 | else | ||
150 | nix-shell ${nixShell} --run "${@}" | ||
151 | fi | ||
152 | fi | ||
153 | |||
154 | cd $(pwd) # Needed for mounting to work | ||
155 | 158 | ||
159 | if [[ ${#@} -eq 0 ]] && [[ ${miniserve} != "true" ]] || [[ $forceShell = "true" ]]; then | ||
156 | isSingleDir() { | 160 | isSingleDir() { |
157 | typeset -a contents | 161 | typeset -a contents |
158 | contents=(*(N) .*(N)) | 162 | contents=(*(N) .*(N)) |
@@ -166,18 +170,9 @@ dir() { | |||
166 | } | 170 | } |
167 | while d=$(isSingleDir); do cd ${d}; done | 171 | while d=$(isSingleDir); do cd ${d}; done |
168 | 172 | ||
169 | 173 | zsh | |
170 | if [[ -z ${nixShell} ]]; then | 174 | elif [[ ${miniserve} == "true" ]]; then |
171 | zsh | 175 | wait $(cat "${miniservePIDFile}") |
172 | else | ||
173 | nix-shell ${nixShell} --run zsh | ||
174 | fi | ||
175 | else | ||
176 | if [[ -z ${nixShell} ]]; then | ||
177 | ${@} | ||
178 | else | ||
179 | nix-shell ${nixShell} --run "${@}" | ||
180 | fi | ||
181 | fi | 176 | fi |
182 | ) | 177 | ) |
183 | } | 178 | } |
@@ -185,27 +180,30 @@ dir() { | |||
185 | tmpdir() { | 180 | tmpdir() { |
186 | cleanup() | 181 | cleanup() |
187 | { | 182 | { |
188 | cd / | 183 | cd / |
189 | unmount() { | 184 | unmount() { |
190 | printf "Unmounting %s\n" ${1} >&2 | 185 | printf "Unmounting %s\n" ${1} >&2 |
191 | fusermount -u ${1} || umount ${1} || sudo umount ${1} | 186 | fusermount -u ${1} || umount ${1} || sudo umount ${1} |
192 | } | 187 | } |
193 | 188 | ||
194 | if mountpoint -q -- ${dir}; then | 189 | if [[ -n ${dir} ]]; then |
195 | unmount ${dir} || return $? | 190 | if mountpoint -q -- ${dir}; then |
196 | else | 191 | unmount ${dir} || return $? |
197 | while read -d $'\0' subDir; do | 192 | else |
198 | mountpoint -q -- ${subDir} || continue | 193 | while read -d $'\0' subDir; do |
199 | unmount ${subDir} || return $? | 194 | mountpoint -q -- ${subDir} || continue |
200 | done <<<$(find ${dir} -xdev -type d -print0 | sort -zr) | 195 | unmount ${subDir} || return $? |
201 | fi | 196 | done <<<$(find ${dir} -xdev -type d -print0 | sort -zr) |
202 | 197 | fi | |
203 | rm -rfv --one-file-system -- ${dir} | 198 | |
199 | rm -rfv --one-file-system -- ${dir} | ||
200 | dir="" | ||
201 | fi | ||
204 | } | 202 | } |
205 | 203 | ||
206 | local tmpdir="" | 204 | local tmpdir="" |
207 | 205 | ||
208 | while getopts ':t:a:s:Sd:ir:wqg:p:' arg; do | 206 | while getopts ':t:a:d:ir:wg:p:m' arg; do |
209 | case $arg in | 207 | case $arg in |
210 | "t") tmpdir="=${OPTARG}" ;; | 208 | "t") tmpdir="=${OPTARG}" ;; |
211 | "?"|":") printf "Invalid option: %s\n" $arg >&2; exit 2 ;; | 209 | "?"|":") printf "Invalid option: %s\n" $arg >&2; exit 2 ;; |
@@ -213,6 +211,8 @@ tmpdir() { | |||
213 | done | 211 | done |
214 | 212 | ||
215 | ( | 213 | ( |
214 | set -o localoptions -o localtraps | ||
215 | trap 'return 1' INT TERM | ||
216 | trap cleanup EXIT | 216 | trap cleanup EXIT |
217 | 217 | ||
218 | 218 | ||
@@ -234,16 +234,6 @@ public-ip() { | |||
234 | curl -s -H 'Accept: application/json' $@ ifconfig.co | jq -r '.ip' | 234 | curl -s -H 'Accept: application/json' $@ ifconfig.co | jq -r '.ip' |
235 | } | 235 | } |
236 | 236 | ||
237 | nix-ghci() { | ||
238 | pkgExpr="" | ||
239 | if [[ ${#@} -gt 0 ]]; then | ||
240 | pkgExpr="${1}" | ||
241 | shift | ||
242 | fi | ||
243 | |||
244 | nix-shell -p "with (import <nixpkgs> {}); pkgs.haskellPackages.ghcWithPackages (p: with p; [${pkgExpr}])" --run "ghci ${@}" | ||
245 | } | ||
246 | |||
247 | swap() { | 237 | swap() { |
248 | f1=${1} | 238 | f1=${1} |
249 | f2=${2} | 239 | f2=${2} |
@@ -271,14 +261,6 @@ l() { | |||
271 | ls --long --binary --git --time-style=iso --header $@ | 261 | ls --long --binary --git --time-style=iso --header $@ |
272 | } | 262 | } |
273 | 263 | ||
274 | re() { | ||
275 | systemctl restart $@ | ||
276 | } | ||
277 | |||
278 | ure() { | ||
279 | systemctl --user restart $@ | ||
280 | } | ||
281 | |||
282 | ssh-installer() { | 264 | ssh-installer() { |
283 | ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentityFile=~/.ssh/gkleen@sif.midgard.yggdrasil $@ | 265 | ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentityFile=~/.ssh/gkleen@sif.midgard.yggdrasil $@ |
284 | } | 266 | } |
diff --git a/accounts/gkleen@surtr.nix b/accounts/gkleen@surtr.nix index 58c4f21d..8f678ac9 100644 --- a/accounts/gkleen@surtr.nix +++ b/accounts/gkleen@surtr.nix | |||
@@ -1,3 +1,7 @@ | |||
1 | { userName, ... }: { | 1 | { flake, userName, ... }: { |
2 | home-manager.users.${userName}.home.stateVersion = "20.09"; | 2 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
3 | zsh tmux | ||
4 | ]; | ||
5 | |||
6 | config.home-manager.users.${userName}.home.stateVersion = "20.09"; | ||
3 | } | 7 | } |
diff --git a/accounts/gkleen@vidhar.nix b/accounts/gkleen@vidhar.nix index 8509c2f4..3a37c4bd 100644 --- a/accounts/gkleen@vidhar.nix +++ b/accounts/gkleen@vidhar.nix | |||
@@ -1,4 +1,8 @@ | |||
1 | { flake, pkgs, userName, config, ... }: { | 1 | { flake, pkgs, userName, config, ... }: { |
2 | imports = with flake.nixosModules.userProfiles.${userName}; [ | ||
3 | zsh tmux | ||
4 | ]; | ||
5 | |||
2 | config = { | 6 | config = { |
3 | users.users.${userName} = { | 7 | users.users.${userName} = { |
4 | uid = 1000; | 8 | uid = 1000; |
diff --git a/accounts/mherold@eostre.nix b/accounts/mherold@eostre.nix index 51e4529a..0e2f37aa 100644 --- a/accounts/mherold@eostre.nix +++ b/accounts/mherold@eostre.nix | |||
@@ -7,9 +7,9 @@ | |||
7 | home-manager.users.${userName} = { | 7 | home-manager.users.${userName} = { |
8 | home.stateVersion = "20.09"; | 8 | home.stateVersion = "20.09"; |
9 | 9 | ||
10 | nixpkgs.config = { | 10 | # nixpkgs.config = { |
11 | allowUnfree = true; | 11 | # allowUnfree = true; |
12 | }; | 12 | # }; |
13 | 13 | ||
14 | home.packages = with pkgs; [ | 14 | home.packages = with pkgs; [ |
15 | thunderbird libreoffice element-desktop keepassxc vlc | 15 | thunderbird libreoffice element-desktop keepassxc vlc |
diff --git a/accounts/root@installer.nix b/accounts/root@installer.nix index c7a418f8..5fe1db38 100644 --- a/accounts/root@installer.nix +++ b/accounts/root@installer.nix | |||
@@ -1,7 +1,11 @@ | |||
1 | { userName, ... }: | 1 | { flake, userName, ... }: |
2 | 2 | ||
3 | { | 3 | { |
4 | home-manager.users.${userName} = { config, ... } : { | 4 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
5 | zsh tmux | ||
6 | ]; | ||
7 | |||
8 | config.home-manager.users.${userName} = { config, ... } : { | ||
5 | home.stateVersion = config.home.version.release; | 9 | home.stateVersion = config.home.version.release; |
6 | }; | 10 | }; |
7 | } | 11 | } |
diff --git a/accounts/root@sif.nix b/accounts/root@sif.nix index c9e129a0..bb816230 100644 --- a/accounts/root@sif.nix +++ b/accounts/root@sif.nix | |||
@@ -1,6 +1,10 @@ | |||
1 | { userName, ... }: | 1 | { flake, userName, ... }: |
2 | { | 2 | { |
3 | home-manager.users.${userName} = { | 3 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
4 | zsh tmux | ||
5 | ]; | ||
6 | |||
7 | config.home-manager.users.${userName} = { | ||
4 | home.stateVersion = "20.09"; | 8 | home.stateVersion = "20.09"; |
5 | 9 | ||
6 | programs.ssh.matchBlocks = { | 10 | programs.ssh.matchBlocks = { |
diff --git a/accounts/root@surtr.nix b/accounts/root@surtr.nix index 58c4f21d..8f678ac9 100644 --- a/accounts/root@surtr.nix +++ b/accounts/root@surtr.nix | |||
@@ -1,3 +1,7 @@ | |||
1 | { userName, ... }: { | 1 | { flake, userName, ... }: { |
2 | home-manager.users.${userName}.home.stateVersion = "20.09"; | 2 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
3 | zsh tmux | ||
4 | ]; | ||
5 | |||
6 | config.home-manager.users.${userName}.home.stateVersion = "20.09"; | ||
3 | } | 7 | } |
diff --git a/accounts/root@vidhar.nix b/accounts/root@vidhar.nix index e82414a8..0fc56633 100644 --- a/accounts/root@vidhar.nix +++ b/accounts/root@vidhar.nix | |||
@@ -1,6 +1,11 @@ | |||
1 | { config, userName, ... }: | 1 | { flake, config, userName, ... }: |
2 | |||
2 | { | 3 | { |
3 | home-manager.users.${userName} = { | 4 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
5 | zsh tmux | ||
6 | ]; | ||
7 | |||
8 | config.home-manager.users.${userName} = { | ||
4 | home.stateVersion = "20.09"; | 9 | home.stateVersion = "20.09"; |
5 | 10 | ||
6 | programs.ssh.matchBlocks = { | 11 | programs.ssh.matchBlocks = { |
@@ -115,11 +115,11 @@ | |||
115 | "flake-compat_3": { | 115 | "flake-compat_3": { |
116 | "flake": false, | 116 | "flake": false, |
117 | "locked": { | 117 | "locked": { |
118 | "lastModified": 1733328505, | 118 | "lastModified": 1747046372, |
119 | "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", | 119 | "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", |
120 | "owner": "edolstra", | 120 | "owner": "edolstra", |
121 | "repo": "flake-compat", | 121 | "repo": "flake-compat", |
122 | "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", | 122 | "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", |
123 | "type": "github" | 123 | "type": "github" |
124 | }, | 124 | }, |
125 | "original": { | 125 | "original": { |
@@ -322,11 +322,11 @@ | |||
322 | ] | 322 | ] |
323 | }, | 323 | }, |
324 | "locked": { | 324 | "locked": { |
325 | "lastModified": 1738691953, | 325 | "lastModified": 1746904907, |
326 | "narHash": "sha256-JY/w2Xyrj3mhUhLJkSgk8t7MSf3LGZjewPTQ7QtCbHE=", | 326 | "narHash": "sha256-XYo6bwc7xwo4lO6a/D2ttYRN4yDmsAjyt5O1E0vOLDg=", |
327 | "owner": "gkleen", | 327 | "owner": "gkleen", |
328 | "repo": "home-manager", | 328 | "repo": "home-manager", |
329 | "rev": "c077fc8684289ab1b1c2231bab1566879e099c97", | 329 | "rev": "696495266c65b76f08d8196b87aa7bd835906570", |
330 | "type": "github" | 330 | "type": "github" |
331 | }, | 331 | }, |
332 | "original": { | 332 | "original": { |
@@ -343,11 +343,11 @@ | |||
343 | ] | 343 | ] |
344 | }, | 344 | }, |
345 | "locked": { | 345 | "locked": { |
346 | "lastModified": 1710245356, | 346 | "lastModified": 1747139300, |
347 | "narHash": "sha256-8cQGUn+N1dTgklMWMejSLN2q8Oz+7Rnqsfaw2rt3bU4=", | 347 | "narHash": "sha256-V+YnIIM2wMprHGgzOU0HzyeWQEjP6EhG8kc4IffWFeg=", |
348 | "owner": "gkleen", | 348 | "owner": "gkleen", |
349 | "repo": "home-manager", | 349 | "repo": "home-manager", |
350 | "rev": "a14fe0c27d04dfa3d80abe2db743e9a7f4f2a33d", | 350 | "rev": "50182497604587a24bdbe97d6400b1696eac57b1", |
351 | "type": "github" | 351 | "type": "github" |
352 | }, | 352 | }, |
353 | "original": { | 353 | "original": { |
@@ -397,11 +397,11 @@ | |||
397 | "xwayland-satellite-unstable": "xwayland-satellite-unstable" | 397 | "xwayland-satellite-unstable": "xwayland-satellite-unstable" |
398 | }, | 398 | }, |
399 | "locked": { | 399 | "locked": { |
400 | "lastModified": 1745483403, | 400 | "lastModified": 1747638609, |
401 | "narHash": "sha256-fNemxNtPugDzCK7ofPApufFhD4EW5PiA0v3+aS1O6rY=", | 401 | "narHash": "sha256-rPTN667tMqC1IQYgsnotVfXbVNbOzScxn0ontMkkSPk=", |
402 | "owner": "sodiboo", | 402 | "owner": "sodiboo", |
403 | "repo": "niri-flake", | 403 | "repo": "niri-flake", |
404 | "rev": "17ebd40a372527ad20cc799b1835beaf7abf7200", | 404 | "rev": "af697f3a8665c8d0770485a2e659ddde88430e3b", |
405 | "type": "github" | 405 | "type": "github" |
406 | }, | 406 | }, |
407 | "original": { | 407 | "original": { |
@@ -431,11 +431,11 @@ | |||
431 | "niri-unstable": { | 431 | "niri-unstable": { |
432 | "flake": false, | 432 | "flake": false, |
433 | "locked": { | 433 | "locked": { |
434 | "lastModified": 1745351516, | 434 | "lastModified": 1747635487, |
435 | "narHash": "sha256-nQRp1Q+kV137Dsk7WCsnq6zQA7YrvRll2wVcG7wZpHA=", | 435 | "narHash": "sha256-za7ctGh4MaW1h5Drm1WtwNZxiXvQK9yXZAeeIyY9b2Q=", |
436 | "owner": "YaLTeR", | 436 | "owner": "YaLTeR", |
437 | "repo": "niri", | 437 | "repo": "niri", |
438 | "rev": "6ab055a4b968ccf115a1be3b65b0d5ec4d7c33f1", | 438 | "rev": "3f2b7e63ba15cf33475116d32e8b7d22208a8438", |
439 | "type": "github" | 439 | "type": "github" |
440 | }, | 440 | }, |
441 | "original": { | 441 | "original": { |
@@ -472,11 +472,11 @@ | |||
472 | ] | 472 | ] |
473 | }, | 473 | }, |
474 | "locked": { | 474 | "locked": { |
475 | "lastModified": 1745120797, | 475 | "lastModified": 1747540584, |
476 | "narHash": "sha256-owQ0VQ+7cSanTVPxaZMWEzI22Q4bGnuvhVjLAJBNQ3E=", | 476 | "narHash": "sha256-cxCQ413JTUuRv9Ygd8DABJ1D6kuB/nTfQqC0Lu9C0ls=", |
477 | "owner": "Mic92", | 477 | "owner": "Mic92", |
478 | "repo": "nix-index-database", | 478 | "repo": "nix-index-database", |
479 | "rev": "69716041f881a2af935021c1182ed5b0cc04d40e", | 479 | "rev": "ec179dd13fb7b4c6844f55be91436f7857226dce", |
480 | "type": "github" | 480 | "type": "github" |
481 | }, | 481 | }, |
482 | "original": { | 482 | "original": { |
@@ -493,11 +493,11 @@ | |||
493 | ] | 493 | ] |
494 | }, | 494 | }, |
495 | "locked": { | 495 | "locked": { |
496 | "lastModified": 1737219791, | 496 | "lastModified": 1745680380, |
497 | "narHash": "sha256-OU0NPjJ3woNDFNx7HtWuUXBb4eI6Ggre/Uj2qhiSjrg=", | 497 | "narHash": "sha256-Z8PknjkmIr/8ZCH+dmc2Pc+UltiOr7/oKg37PXuVvuU=", |
498 | "owner": "ners", | 498 | "owner": "ners", |
499 | "repo": "nix-monitored", | 499 | "repo": "nix-monitored", |
500 | "rev": "6ed8ed4832ff26c616e5856ba19f5b8141d61bd3", | 500 | "rev": "60f3baa4701d58eab86c2d1d9c3d7e820074d461", |
501 | "type": "github" | 501 | "type": "github" |
502 | }, | 502 | }, |
503 | "original": { | 503 | "original": { |
@@ -514,11 +514,11 @@ | |||
514 | ] | 514 | ] |
515 | }, | 515 | }, |
516 | "locked": { | 516 | "locked": { |
517 | "lastModified": 1741549407, | 517 | "lastModified": 1747637556, |
518 | "narHash": "sha256-f9SXK+/rvlryDNlc++Eva/hYjbkf7OCalWwmwifRhtI=", | 518 | "narHash": "sha256-AYd1nE+BLWTZS8J0eFQ7kuNiuE+XjhhndoXinj7en/M=", |
519 | "owner": "AshleyYakeley", | 519 | "owner": "AshleyYakeley", |
520 | "repo": "NixVirt", | 520 | "repo": "NixVirt", |
521 | "rev": "9950b932dce4ae6b9bda7c83d41705c1a14e10f0", | 521 | "rev": "a7d3d3ae8b9a0cf3ec3cf504bb593df0618a6dbc", |
522 | "type": "github" | 522 | "type": "github" |
523 | }, | 523 | }, |
524 | "original": { | 524 | "original": { |
@@ -529,11 +529,11 @@ | |||
529 | }, | 529 | }, |
530 | "nixos-hardware": { | 530 | "nixos-hardware": { |
531 | "locked": { | 531 | "locked": { |
532 | "lastModified": 1745392233, | 532 | "lastModified": 1747129300, |
533 | "narHash": "sha256-xmqG4MZArM1JNxPJ33s0MtuBzgnaCO9laARoU3AfP8E=", | 533 | "narHash": "sha256-L3clA5YGeYCF47ghsI7Tcex+DnaaN/BbQ4dR2wzoiKg=", |
534 | "owner": "NixOS", | 534 | "owner": "NixOS", |
535 | "repo": "nixos-hardware", | 535 | "repo": "nixos-hardware", |
536 | "rev": "8bf8a2a0822365bd8f44fd1a19d7ed0a1d629d64", | 536 | "rev": "e81fd167b33121269149c57806599045fd33eeed", |
537 | "type": "github" | 537 | "type": "github" |
538 | }, | 538 | }, |
539 | "original": { | 539 | "original": { |
@@ -651,11 +651,11 @@ | |||
651 | }, | 651 | }, |
652 | "nixpkgs-stable_2": { | 652 | "nixpkgs-stable_2": { |
653 | "locked": { | 653 | "locked": { |
654 | "lastModified": 1745279238, | 654 | "lastModified": 1747485343, |
655 | "narHash": "sha256-AQ7M9wTa/Pa/kK5pcGTgX/DGqMHyzsyINfN7ktsI7Fo=", | 655 | "narHash": "sha256-YbsZyuRE1tobO9sv0PUwg81QryYo3L1F3R3rF9bcG38=", |
656 | "owner": "NixOS", | 656 | "owner": "NixOS", |
657 | "repo": "nixpkgs", | 657 | "repo": "nixpkgs", |
658 | "rev": "9684b53175fc6c09581e94cc85f05ab77464c7e3", | 658 | "rev": "9b5ac7ad45298d58640540d0323ca217f32a6762", |
659 | "type": "github" | 659 | "type": "github" |
660 | }, | 660 | }, |
661 | "original": { | 661 | "original": { |
@@ -699,11 +699,11 @@ | |||
699 | }, | 699 | }, |
700 | "nixpkgs_2": { | 700 | "nixpkgs_2": { |
701 | "locked": { | 701 | "locked": { |
702 | "lastModified": 1745391562, | 702 | "lastModified": 1747542820, |
703 | "narHash": "sha256-sPwcCYuiEopaafePqlG826tBhctuJsLx/mhKKM5Fmjo=", | 703 | "narHash": "sha256-GaOZntlJ6gPPbbkTLjbd8BMWaDYafhuuYRNrxCGnPJw=", |
704 | "owner": "NixOS", | 704 | "owner": "NixOS", |
705 | "repo": "nixpkgs", | 705 | "repo": "nixpkgs", |
706 | "rev": "8a2f738d9d1f1d986b5a4cd2fd2061a7127237d7", | 706 | "rev": "292fa7d4f6519c074f0a50394dbbe69859bb6043", |
707 | "type": "github" | 707 | "type": "github" |
708 | }, | 708 | }, |
709 | "original": { | 709 | "original": { |
@@ -879,6 +879,52 @@ | |||
879 | "type": "gitlab" | 879 | "type": "gitlab" |
880 | } | 880 | } |
881 | }, | 881 | }, |
882 | "pyproject-build-systems": { | ||
883 | "inputs": { | ||
884 | "nixpkgs": [ | ||
885 | "nixpkgs" | ||
886 | ], | ||
887 | "pyproject-nix": [ | ||
888 | "pyproject-nix" | ||
889 | ], | ||
890 | "uv2nix": [ | ||
891 | "uv2nix" | ||
892 | ] | ||
893 | }, | ||
894 | "locked": { | ||
895 | "lastModified": 1744599653, | ||
896 | "narHash": "sha256-nysSwVVjG4hKoOjhjvE6U5lIKA8sEr1d1QzEfZsannU=", | ||
897 | "owner": "pyproject-nix", | ||
898 | "repo": "build-system-pkgs", | ||
899 | "rev": "7dba6dbc73120e15b558754c26024f6c93015dd7", | ||
900 | "type": "github" | ||
901 | }, | ||
902 | "original": { | ||
903 | "owner": "pyproject-nix", | ||
904 | "repo": "build-system-pkgs", | ||
905 | "type": "github" | ||
906 | } | ||
907 | }, | ||
908 | "pyproject-nix": { | ||
909 | "inputs": { | ||
910 | "nixpkgs": [ | ||
911 | "nixpkgs" | ||
912 | ] | ||
913 | }, | ||
914 | "locked": { | ||
915 | "lastModified": 1746540146, | ||
916 | "narHash": "sha256-QxdHGNpbicIrw5t6U3x+ZxeY/7IEJ6lYbvsjXmcxFIM=", | ||
917 | "owner": "pyproject-nix", | ||
918 | "repo": "pyproject.nix", | ||
919 | "rev": "e09c10c24ebb955125fda449939bfba664c467fd", | ||
920 | "type": "github" | ||
921 | }, | ||
922 | "original": { | ||
923 | "owner": "pyproject-nix", | ||
924 | "repo": "pyproject.nix", | ||
925 | "type": "github" | ||
926 | } | ||
927 | }, | ||
882 | "root": { | 928 | "root": { |
883 | "inputs": { | 929 | "inputs": { |
884 | "backup-utils": "backup-utils", | 930 | "backup-utils": "backup-utils", |
@@ -902,7 +948,10 @@ | |||
902 | "nvfetcher": "nvfetcher", | 948 | "nvfetcher": "nvfetcher", |
903 | "poetry2nix": "poetry2nix", | 949 | "poetry2nix": "poetry2nix", |
904 | "prometheus-borg-exporter": "prometheus-borg-exporter", | 950 | "prometheus-borg-exporter": "prometheus-borg-exporter", |
951 | "pyproject-build-systems": "pyproject-build-systems", | ||
952 | "pyproject-nix": "pyproject-nix", | ||
905 | "sops-nix": "sops-nix", | 953 | "sops-nix": "sops-nix", |
954 | "uv2nix": "uv2nix", | ||
906 | "waybar": "waybar" | 955 | "waybar": "waybar" |
907 | } | 956 | } |
908 | }, | 957 | }, |
@@ -913,11 +962,11 @@ | |||
913 | ] | 962 | ] |
914 | }, | 963 | }, |
915 | "locked": { | 964 | "locked": { |
916 | "lastModified": 1745310711, | 965 | "lastModified": 1747603214, |
917 | "narHash": "sha256-ePyTpKEJTgX0gvgNQWd7tQYQ3glIkbqcW778RpHlqgA=", | 966 | "narHash": "sha256-lAblXm0VwifYCJ/ILPXJwlz0qNY07DDYdLD+9H+Wc8o=", |
918 | "owner": "Mic92", | 967 | "owner": "Mic92", |
919 | "repo": "sops-nix", | 968 | "repo": "sops-nix", |
920 | "rev": "5e3e92b16d6fdf9923425a8d4df7496b2434f39c", | 969 | "rev": "8d215e1c981be3aa37e47aeabd4e61bb069548fd", |
921 | "type": "github" | 970 | "type": "github" |
922 | }, | 971 | }, |
923 | "original": { | 972 | "original": { |
@@ -978,6 +1027,29 @@ | |||
978 | "type": "github" | 1027 | "type": "github" |
979 | } | 1028 | } |
980 | }, | 1029 | }, |
1030 | "uv2nix": { | ||
1031 | "inputs": { | ||
1032 | "nixpkgs": [ | ||
1033 | "nixpkgs" | ||
1034 | ], | ||
1035 | "pyproject-nix": [ | ||
1036 | "pyproject-nix" | ||
1037 | ] | ||
1038 | }, | ||
1039 | "locked": { | ||
1040 | "lastModified": 1747441483, | ||
1041 | "narHash": "sha256-W8BFXk5R0TuJcjIhcGoMpSOaIufGXpizK0pm+uTqynA=", | ||
1042 | "owner": "pyproject-nix", | ||
1043 | "repo": "uv2nix", | ||
1044 | "rev": "582024dc64663e9f88d467c2f7f7b20d278349de", | ||
1045 | "type": "github" | ||
1046 | }, | ||
1047 | "original": { | ||
1048 | "owner": "pyproject-nix", | ||
1049 | "repo": "uv2nix", | ||
1050 | "type": "github" | ||
1051 | } | ||
1052 | }, | ||
981 | "waybar": { | 1053 | "waybar": { |
982 | "inputs": { | 1054 | "inputs": { |
983 | "flake-compat": [ | 1055 | "flake-compat": [ |
@@ -988,16 +1060,16 @@ | |||
988 | ] | 1060 | ] |
989 | }, | 1061 | }, |
990 | "locked": { | 1062 | "locked": { |
991 | "lastModified": 1742140394, | 1063 | "lastModified": 1747383113, |
992 | "narHash": "sha256-U1Lp5HZbpnWQRetOLzQl3dURplY2BRfAZYkjBawYrVM=", | 1064 | "narHash": "sha256-/YW7eOKU3gsNplxvUDpEj1LiXtcCENSFpS1c8kXSDWw=", |
993 | "owner": "gkleen", | 1065 | "owner": "gkleen", |
994 | "repo": "Waybar", | 1066 | "repo": "Waybar", |
995 | "rev": "f310667db199c570b599a08152d49b7f80db93f2", | 1067 | "rev": "919036587381595f15010ac95644992fe6d7343d", |
996 | "type": "github" | 1068 | "type": "github" |
997 | }, | 1069 | }, |
998 | "original": { | 1070 | "original": { |
999 | "owner": "gkleen", | 1071 | "owner": "gkleen", |
1000 | "ref": "feat/niri-workspaces-hide", | 1072 | "ref": "feat/niri-urgency", |
1001 | "repo": "Waybar", | 1073 | "repo": "Waybar", |
1002 | "type": "github" | 1074 | "type": "github" |
1003 | } | 1075 | } |
@@ -1022,11 +1094,11 @@ | |||
1022 | "xwayland-satellite-unstable": { | 1094 | "xwayland-satellite-unstable": { |
1023 | "flake": false, | 1095 | "flake": false, |
1024 | "locked": { | 1096 | "locked": { |
1025 | "lastModified": 1745372360, | 1097 | "lastModified": 1747111562, |
1026 | "narHash": "sha256-5DX9lYmEbkdANCzME2v3coV0EnWOhS7NsTlGBQuqmjM=", | 1098 | "narHash": "sha256-GAqhWoxaBIk0tgoecZPa8gTHDHxNc0JtlwWHZN2iOOo=", |
1027 | "owner": "Supreeeme", | 1099 | "owner": "Supreeeme", |
1028 | "repo": "xwayland-satellite", | 1100 | "repo": "xwayland-satellite", |
1029 | "rev": "c31679aa41966ee9272bb240703755cb1e7c72e3", | 1101 | "rev": "ec9ff64c1e0cbec42710b580b7c0f759b1694e72", |
1030 | "type": "github" | 1102 | "type": "github" |
1031 | }, | 1103 | }, |
1032 | "original": { | 1104 | "original": { |
@@ -125,6 +125,21 @@ | |||
125 | nixpkgs.follows = "nixpkgs"; | 125 | nixpkgs.follows = "nixpkgs"; |
126 | }; | 126 | }; |
127 | }; | 127 | }; |
128 | pyproject-nix = { | ||
129 | url = "github:pyproject-nix/pyproject.nix"; | ||
130 | inputs.nixpkgs.follows = "nixpkgs"; | ||
131 | }; | ||
132 | uv2nix = { | ||
133 | url = "github:pyproject-nix/uv2nix"; | ||
134 | inputs.pyproject-nix.follows = "pyproject-nix"; | ||
135 | inputs.nixpkgs.follows = "nixpkgs"; | ||
136 | }; | ||
137 | pyproject-build-systems = { | ||
138 | url = "github:pyproject-nix/build-system-pkgs"; | ||
139 | inputs.pyproject-nix.follows = "pyproject-nix"; | ||
140 | inputs.uv2nix.follows = "uv2nix"; | ||
141 | inputs.nixpkgs.follows = "nixpkgs"; | ||
142 | }; | ||
128 | 143 | ||
129 | ca-util = { | 144 | ca-util = { |
130 | type = "gitlab"; | 145 | type = "gitlab"; |
@@ -172,7 +187,7 @@ | |||
172 | type = "github"; | 187 | type = "github"; |
173 | owner = "gkleen"; | 188 | owner = "gkleen"; |
174 | repo = "Waybar"; | 189 | repo = "Waybar"; |
175 | ref = "feat/niri-workspaces-hide"; | 190 | ref = "feat/niri-urgency"; |
176 | inputs = { | 191 | inputs = { |
177 | nixpkgs.follows = "nixpkgs"; | 192 | nixpkgs.follows = "nixpkgs"; |
178 | flake-compat.follows = "flake-compat"; | 193 | flake-compat.follows = "flake-compat"; |
@@ -348,7 +363,7 @@ | |||
348 | 363 | ||
349 | overlays = mapAttrs (_name: path: mkOverlay path) overlayPaths; | 364 | overlays = mapAttrs (_name: path: mkOverlay path) overlayPaths; |
350 | 365 | ||
351 | packages = forAllSystems (system: systemPkgs: nixImport rec { dir = ./tools; _import = _path: name: import "${toString dir}/${name}" ({ inherit system; } // inputs); }); | 366 | packages = forAllSystems (system: systemPkgs: nixImport rec { dir = ./tools; _import = name: _base: import (dir + "/${name}") ({ inherit system; } // inputs); }); |
352 | 367 | ||
353 | # packages = mapAttrs (_name: filterAttrs (_name: isDerivation)) packages; | 368 | # packages = mapAttrs (_name: filterAttrs (_name: isDerivation)) packages; |
354 | # packages' = mapAttrs (_name: filterAttrs (_name: value: !(isDerivation value))) packages; | 369 | # packages' = mapAttrs (_name: filterAttrs (_name: value: !(isDerivation value))) packages; |
@@ -360,6 +375,8 @@ | |||
360 | activateNixosConfigurations activateHomeManagerConfigurations | 375 | activateNixosConfigurations activateHomeManagerConfigurations |
361 | ]; | 376 | ]; |
362 | 377 | ||
378 | lib = nixImport rec { dir = ./lib; _import = name: _base: import (dir + "/${name}") inputs; }; | ||
379 | |||
363 | devShells = forAllSystems (system: systemPkgs: { default = import ./shell.nix ({ inherit system; } // inputs); } // installerShells system systemPkgs); | 380 | devShells = forAllSystems (system: systemPkgs: { default = import ./shell.nix ({ inherit system; } // inputs); } // installerShells system systemPkgs); |
364 | 381 | ||
365 | templates.default = { | 382 | templates.default = { |
@@ -383,7 +400,7 @@ | |||
383 | # path = activateHomeManager (self.nixosConfigurations.${hostname}.config.nixpkgs.system) usercfg.home; | 400 | # path = activateHomeManager (self.nixosConfigurations.${hostname}.config.nixpkgs.system) usercfg.home; |
384 | # }) self.nixosConfigurations.${hostname}.config.home-manager.users); | 401 | # }) self.nixosConfigurations.${hostname}.config.home-manager.users); |
385 | }) (nixImport { dir = ./hosts; _import = (_path: name: name); }); | 402 | }) (nixImport { dir = ./hosts; _import = (_path: name: name); }); |
386 | overrides = if pathExists ./deploy then nixImport { dir = ./deploy; _import = path: _name: import (./deploy + "/${path}") inputs; } else {}; | 403 | overrides = if pathExists ./deploy then nixImport rec { dir = ./deploy; _import = path: _name: import (dir + "/${path}") inputs; } else {}; |
387 | filterEnabled = attrs: mapAttrs (_n: v: filterAttrs (n: _v: n != "enabled") v) (filterAttrs (_n: v: v.enabled or true) attrs); | 404 | filterEnabled = attrs: mapAttrs (_n: v: filterAttrs (n: _v: n != "enabled") v) (filterAttrs (_n: v: v.enabled or true) attrs); |
388 | in mapAttrs (_n: v: if v ? "profiles" then v // { profiles = filterEnabled v.profiles; } else v) (filterEnabled (recursiveUpdate defaults overrides)); | 405 | in mapAttrs (_n: v: if v ? "profiles" then v // { profiles = filterEnabled v.profiles; } else v) (filterEnabled (recursiveUpdate defaults overrides)); |
389 | 406 | ||
diff --git a/home-modules/nixpkgs-release-check.nix b/home-modules/nixpkgs-release-check.nix new file mode 100644 index 00000000..baf2713a --- /dev/null +++ b/home-modules/nixpkgs-release-check.nix | |||
@@ -0,0 +1,4 @@ | |||
1 | { ... }: | ||
2 | { | ||
3 | config.home.enableNixpkgsReleaseCheck = false; | ||
4 | } | ||
diff --git a/hosts/sif/default.nix b/hosts/sif/default.nix index 0897e1d8..6214569a 100644 --- a/hosts/sif/default.nix +++ b/hosts/sif/default.nix | |||
@@ -12,7 +12,7 @@ let | |||
12 | in { | 12 | in { |
13 | imports = with flake.nixosModules.systemProfiles; [ | 13 | imports = with flake.nixosModules.systemProfiles; [ |
14 | ./hw.nix | 14 | ./hw.nix |
15 | ./mail ./libvirt ./greetd | 15 | ./email ./libvirt ./greetd |
16 | tmpfs-root bcachefs initrd-all-crypto-modules default-locale openssh rebuild-machines niri-unstable networkmanager | 16 | tmpfs-root bcachefs initrd-all-crypto-modules default-locale openssh rebuild-machines niri-unstable networkmanager |
17 | flakeInputs.nixos-hardware.nixosModules.lenovo-thinkpad-p1 | 17 | flakeInputs.nixos-hardware.nixosModules.lenovo-thinkpad-p1 |
18 | flakeInputs.impermanence.nixosModules.impermanence | 18 | flakeInputs.impermanence.nixosModules.impermanence |
@@ -126,40 +126,16 @@ in { | |||
126 | rulesetFile = ./ruleset.nft; | 126 | rulesetFile = ./ruleset.nft; |
127 | }; | 127 | }; |
128 | 128 | ||
129 | # firewall = { | ||
130 | # enable = true; | ||
131 | # allowedTCPPorts = [ 22 # ssh | ||
132 | # 8000 # quickserve | ||
133 | # ]; | ||
134 | # }; | ||
135 | |||
136 | # wlanInterfaces = { | ||
137 | # wlan0 = { | ||
138 | # device = "wlp82s0"; | ||
139 | # }; | ||
140 | # }; | ||
141 | |||
142 | # bonds = { | ||
143 | # "lan" = { | ||
144 | # interfaces = [ "wlan0" "enp0s31f6" "dock0" ]; | ||
145 | # driverOptions = { | ||
146 | # miimon = "1000"; | ||
147 | # mode = "active-backup"; | ||
148 | # primary_reselect = "always"; | ||
149 | # }; | ||
150 | # }; | ||
151 | # }; | ||
152 | |||
153 | useDHCP = false; | 129 | useDHCP = false; |
154 | useNetworkd = true; | 130 | useNetworkd = true; |
155 | |||
156 | # interfaces."tinc.yggdrasil" = { | ||
157 | # virtual = true; | ||
158 | # virtualType = config.services.tinc.networks.yggdrasil.interfaceType; | ||
159 | # macAddress = "5c:93:21:c3:61:39"; | ||
160 | # }; | ||
161 | }; | 131 | }; |
162 | 132 | ||
133 | environment.etc."NetworkManager/dnsmasq.d/dnssec.conf" = { | ||
134 | text = '' | ||
135 | conf-file=${pkgs.dnsmasq}/share/dnsmasq/trust-anchors.conf | ||
136 | dnssec | ||
137 | ''; | ||
138 | }; | ||
163 | environment.etc."NetworkManager/dnsmasq.d/libvirt_dnsmasq.conf" = { | 139 | environment.etc."NetworkManager/dnsmasq.d/libvirt_dnsmasq.conf" = { |
164 | text = '' | 140 | text = '' |
165 | except-interface=virbr0 | 141 | except-interface=virbr0 |
@@ -402,19 +378,6 @@ in { | |||
402 | ]; | 378 | ]; |
403 | 379 | ||
404 | services = { | 380 | services = { |
405 | uucp = { | ||
406 | enable = true; | ||
407 | nodeName = "sif"; | ||
408 | remoteNodes = { | ||
409 | "ymir" = { | ||
410 | publicKeys = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG6KNtsCOl5fsZ4rV7udTulGMphJweLBoKapzerWNoLY root@ymir"]; | ||
411 | hostnames = ["ymir.yggdrasil.li" "ymir.niflheim.yggdrasil"]; | ||
412 | }; | ||
413 | }; | ||
414 | |||
415 | defaultCommands = lib.mkForce []; | ||
416 | }; | ||
417 | |||
418 | avahi.enable = true; | 381 | avahi.enable = true; |
419 | 382 | ||
420 | fwupd.enable = true; | 383 | fwupd.enable = true; |
@@ -433,8 +396,8 @@ in { | |||
433 | 396 | ||
434 | logind = { | 397 | logind = { |
435 | lidSwitch = "suspend"; | 398 | lidSwitch = "suspend"; |
436 | lidSwitchDocked = "lock"; | 399 | lidSwitchDocked = "ignore"; |
437 | lidSwitchExternalPower = "lock"; | 400 | lidSwitchExternalPower = "ignore"; |
438 | }; | 401 | }; |
439 | 402 | ||
440 | atd = { | 403 | atd = { |
@@ -640,25 +603,6 @@ in { | |||
640 | 603 | ||
641 | environment.etc."X11/xorg.conf.d/50-wacom.conf".source = lib.mkForce ./wacom.conf; | 604 | environment.etc."X11/xorg.conf.d/50-wacom.conf".source = lib.mkForce ./wacom.conf; |
642 | 605 | ||
643 | systemd.services."ac-plugged" = { | ||
644 | description = "Inhibit handling of lid-switch and sleep"; | ||
645 | |||
646 | path = with pkgs; [ systemd coreutils ]; | ||
647 | |||
648 | script = '' | ||
649 | exec systemd-inhibit --what=handle-lid-switch --why="AC is connected" --mode=block sleep infinity | ||
650 | ''; | ||
651 | |||
652 | serviceConfig = { | ||
653 | Type = "simple"; | ||
654 | }; | ||
655 | }; | ||
656 | |||
657 | services.udev.extraRules = with pkgs; lib.mkAfter '' | ||
658 | SUBSYSTEM=="power_supply", ENV{POWER_SUPPLY_ONLINE}=="0", RUN+="${systemd}/bin/systemctl --no-block stop ac-plugged.service" | ||
659 | SUBSYSTEM=="power_supply", ENV{POWER_SUPPLY_ONLINE}=="1", RUN+="${systemd}/bin/systemctl --no-block start ac-plugged.service" | ||
660 | ''; | ||
661 | |||
662 | systemd.services."nix-daemon".serviceConfig = { | 606 | systemd.services."nix-daemon".serviceConfig = { |
663 | MemoryAccounting = true; | 607 | MemoryAccounting = true; |
664 | MemoryHigh = "50%"; | 608 | MemoryHigh = "50%"; |
@@ -718,7 +662,7 @@ in { | |||
718 | directories = [ | 662 | directories = [ |
719 | "/nix" | 663 | "/nix" |
720 | "/root" | 664 | "/root" |
721 | "/home" | 665 | "/home" |
722 | "/var/log" | 666 | "/var/log" |
723 | "/var/lib/sops-nix" | 667 | "/var/lib/sops-nix" |
724 | "/var/lib/nixos" | 668 | "/var/lib/nixos" |
@@ -729,8 +673,6 @@ in { | |||
729 | "/var/lib/upower" | 673 | "/var/lib/upower" |
730 | "/var/lib/postfix" | 674 | "/var/lib/postfix" |
731 | "/etc/NetworkManager/system-connections" | 675 | "/etc/NetworkManager/system-connections" |
732 | { directory = "/var/uucp"; user = "uucp"; group = "uucp"; mode = "0700"; } | ||
733 | { directory = "/var/spool/uucp"; user = "uucp"; group = "uucp"; mode = "0750"; } | ||
734 | ]; | 676 | ]; |
735 | files = [ | 677 | files = [ |
736 | ]; | 678 | ]; |
@@ -751,10 +693,6 @@ in { | |||
751 | 693 | ||
752 | home-manager.sharedModules = [ flakeInputs.nixVirt.homeModules.default ]; | 694 | home-manager.sharedModules = [ flakeInputs.nixVirt.homeModules.default ]; |
753 | 695 | ||
754 | environment.pathsToLink = [ | ||
755 | "share/zsh" | ||
756 | ]; | ||
757 | |||
758 | system.stateVersion = "24.11"; | 696 | system.stateVersion = "24.11"; |
759 | }; | 697 | }; |
760 | } | 698 | } |
diff --git a/hosts/sif/email/default.nix b/hosts/sif/email/default.nix new file mode 100644 index 00000000..4eda236e --- /dev/null +++ b/hosts/sif/email/default.nix | |||
@@ -0,0 +1,110 @@ | |||
1 | { config, lib, pkgs, ... }: | ||
2 | { | ||
3 | services.postfix = { | ||
4 | enable = true; | ||
5 | enableSmtp = false; | ||
6 | enableSubmission = false; | ||
7 | setSendmail = true; | ||
8 | networksStyle = "host"; | ||
9 | hostname = "sif.midgard.yggdrasil"; | ||
10 | destination = []; | ||
11 | recipientDelimiter = "+"; | ||
12 | config = { | ||
13 | mydomain = "yggdrasil.li"; | ||
14 | |||
15 | local_transport = "error:5.1.1 No local delivery"; | ||
16 | alias_database = []; | ||
17 | alias_maps = []; | ||
18 | local_recipient_maps = []; | ||
19 | |||
20 | inet_interfaces = "loopback-only"; | ||
21 | |||
22 | message_size_limit = "0"; | ||
23 | |||
24 | authorized_submit_users = "inline:{ gkleen= }"; | ||
25 | authorized_flush_users = "inline:{ gkleen= }"; | ||
26 | authorized_mailq_users = "inline:{ gkleen= }"; | ||
27 | |||
28 | smtp_generic_maps = "inline:{ root=root+sif }"; | ||
29 | |||
30 | mynetworks = ["127.0.0.0/8" "[::1]/128"]; | ||
31 | smtpd_client_restrictions = ["permit_mynetworks" "reject"]; | ||
32 | smtpd_relay_restrictions = ["permit_mynetworks" "reject"]; | ||
33 | |||
34 | sender_dependent_default_transport_maps = ''regexp:${pkgs.writeText "sender_relay" '' | ||
35 | /@(cip|stud)\.ifi\.(lmu|uni-muenchen)\.de$/ smtp:smtp.ifi.lmu.de | ||
36 | /@ifi\.(lmu|uni-muenchen)\.de$/ smtp:smtpin1.ifi.lmu.de:587 | ||
37 | /@math(ematik)?\.(lmu|uni-muenchen)\.de$/ smtps:smtp.math.lmu.de:465 | ||
38 | /@(campus\.)?lmu\.de$/ smtp:postout.lrz.de | ||
39 | ''}''; | ||
40 | sender_bcc_maps = ''regexp:${pkgs.writeText "sender_bcc" '' | ||
41 | /^uni2work(-[^@]*)?@ifi\.lmu\.de$/ uni2work@ifi.lmu.de | ||
42 | /@ifi\.lmu\.de$/ gregor.kleen@ifi.lmu.de | ||
43 | ''}''; | ||
44 | relayhost = "[surtr.yggdrasil.li]:465"; | ||
45 | default_transport = "relay"; | ||
46 | |||
47 | smtp_sasl_auth_enable = true; | ||
48 | smtp_sender_dependent_authentication = true; | ||
49 | smtp_sasl_tls_security_options = "noanonymous"; | ||
50 | smtp_sasl_mechanism_filter = ["plain"]; | ||
51 | smtp_sasl_password_maps = "regexp:/run/credentials/postfix.service/sasl_passwd"; | ||
52 | smtp_cname_overrides_servername = false; | ||
53 | smtp_always_send_ehlo = true; | ||
54 | smtp_tls_security_level = "dane"; | ||
55 | |||
56 | smtp_tls_loglevel = "1"; | ||
57 | smtp_dns_support_level = "dnssec"; | ||
58 | }; | ||
59 | masterConfig = { | ||
60 | submission = { | ||
61 | type = "inet"; | ||
62 | private = false; | ||
63 | command = "smtpd"; | ||
64 | args = [ | ||
65 | "-o" "syslog_name=postfix/$service_name" | ||
66 | ]; | ||
67 | }; | ||
68 | smtp = { }; | ||
69 | smtps = { | ||
70 | type = "unix"; | ||
71 | private = true; | ||
72 | privileged = true; | ||
73 | chroot = false; | ||
74 | command = "smtp"; | ||
75 | args = [ | ||
76 | "-o" "smtp_tls_wrappermode=yes" | ||
77 | "-o" "smtp_tls_security_level=encrypt" | ||
78 | ]; | ||
79 | }; | ||
80 | relay = { | ||
81 | command = "smtp"; | ||
82 | args = [ | ||
83 | "-o" "smtp_fallback_relay=" | ||
84 | "-o" "smtp_tls_security_level=verify" | ||
85 | "-o" "smtp_tls_wrappermode=yes" | ||
86 | "-o" "smtp_tls_cert_file=${./relay.crt}" | ||
87 | "-o" "smtp_tls_key_file=/run/credentials/postfix.service/relay.key" | ||
88 | ]; | ||
89 | }; | ||
90 | }; | ||
91 | }; | ||
92 | |||
93 | systemd.services.postfix = { | ||
94 | serviceConfig.LoadCredential = [ | ||
95 | "sasl_passwd:${config.sops.secrets."postfix-sasl-passwd".path}" | ||
96 | "relay.key:${config.sops.secrets."relay-key".path}" | ||
97 | ]; | ||
98 | }; | ||
99 | |||
100 | sops.secrets = { | ||
101 | postfix-sasl-passwd = { | ||
102 | key = "sasl-passwd"; | ||
103 | sopsFile = ./secrets.yaml; | ||
104 | }; | ||
105 | relay-key = { | ||
106 | format = "binary"; | ||
107 | sopsFile = ./relay.key; | ||
108 | }; | ||
109 | }; | ||
110 | } | ||
diff --git a/hosts/sif/email/relay.crt b/hosts/sif/email/relay.crt new file mode 100644 index 00000000..ac13e7cb --- /dev/null +++ b/hosts/sif/email/relay.crt | |||
@@ -0,0 +1,11 @@ | |||
1 | -----BEGIN CERTIFICATE----- | ||
2 | MIIBjDCCAQygAwIBAgIPQAAAAGgLfNoL/PSMAsutMAUGAytlcTAXMRUwEwYDVQQD | ||
3 | DAx5Z2dkcmFzaWwubGkwHhcNMjUwNDI1MTIwOTQ1WhcNMzUwNDI2MTIxNDQ1WjAR | ||
4 | MQ8wDQYDVQQDDAZna2xlZW4wKjAFBgMrZXADIQB3outi3/3F4YO7Q97WAAaMHW0a | ||
5 | m+Blldrgee+EZnWnD6N1MHMwHwYDVR0jBBgwFoAUTtn+VjMw6Ge1f68KD8dT1CWn | ||
6 | l3YwHQYDVR0OBBYEFFOa4rYZYMbXUVdKv98NB504GUhjMA4GA1UdDwEB/wQEAwID | ||
7 | 6DAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAUGAytlcQNzABC0 | ||
8 | 0UgIt7gLZrU1TmzGoqPBris8R1DbKOJacicF5CU0MIIjHcX7mPFW8KtB4qm6KcPq | ||
9 | kF6IaEPmgKpX3Nubk8HJik9vhIy9ysfINcVTvzXx8pO1bxbvREJRyA/apj10nzav | ||
10 | yauId0cXHvN6g5RLAMsMAA== | ||
11 | -----END CERTIFICATE----- | ||
diff --git a/hosts/sif/email/relay.key b/hosts/sif/email/relay.key new file mode 100644 index 00000000..412a44e0 --- /dev/null +++ b/hosts/sif/email/relay.key | |||
@@ -0,0 +1,19 @@ | |||
1 | { | ||
2 | "data": "ENC[AES256_GCM,data:lBlTuzOS75pvRmcTKT4KhHMH44RlE2SvCFAUP+GfsXws1Uai7DZ1MmbhvxxCa+pcLW19+sQYxrXLRNZWby1yOeKBJ2UQeYV5LOk9LSL/WIE3FZkCo5Dv0O0gSFKjjb61WN22a4JnHbLWADf/mLT3GZv91XfvFDo=,iv:ho8wQH3UNzX9JPW5gVcUGtxZzdVwsMFus0Z4KYe5t48=,tag:dAgZyHOva2xVVhE1nTl+lg==,type:str]", | ||
3 | "sops": { | ||
4 | "age": [ | ||
5 | { | ||
6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6eTVRSUdFNUZGZmcxSUlT\nWmlsOGNyWXIzMGNTZjlKbXlhcEdZUXFRVkR3Cll0T0RMd0h2UW16QkR3SHlhYmNZ\nNDFrYXh3Rkp5NWsvcWc3UFJJaHVwT1UKLS0tIHhXVEI0VHBZVkpDQ1FzWENjMmJH\nb1FQWXVUUTBiZ1pKWG00MTNqVEo2SjAKK3VOU+QgRuxWYWEcrJiVMRFCprBICz4F\ngD+9zuPUzPezyJkYwTs+M+wX5GYkXppqm5W58yQLS2UDD38sr+SRjg==\n-----END AGE ENCRYPTED FILE-----\n" | ||
8 | }, | ||
9 | { | ||
10 | "recipient": "age1fj65apkhfkrwyv5tx6zcs9nkjg8267fy733qph30sc7zfn7vapjqkd5kne", | ||
11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzWmJmZDVFazN2bDY1TkNG\nNXpJN2twMFFjZUxMTVdSNzJwQTFiYktrcGdrCjk4eFVHTko0bFVMSlFFWm9tbjMr\nbWNHMEQ1Rm1qUVhodlB1RGw2aDc4TUEKLS0tIERBK0J5NkN4OXJEZ1ZOZXhNc1Jm\naWNnUmZGbTIxdmNkYi9TZ2h2bGs3MVEKPQGaEf7M/5/xvSOfawpIp50fB3QfFSuz\nPgkrPMneaBeUx+uBYMyEFX4rpzLIBR3pnYMjAfoc+bjWaOtGQuEqyQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
12 | } | ||
13 | ], | ||
14 | "lastmodified": "2025-04-25T12:14:44Z", | ||
15 | "mac": "ENC[AES256_GCM,data:pObl2bJA93az9E3Ya+hA3ekI8TKKZ9NNTi0KzmWZBOiQwi9FuQYtpnmmT80L1KXWyOKJV6wGdAri3mNe/ue2S0TziSbQ/4+Dj4ubFKgkH7thb5q2dFyxw5FzhYzRQiXFqD/pxcNN9uL0lQI2Al0Eci0zX8Kcd1rAQ6RzLEoSmco=,iv:zo/3QFKTUEDxLy1k5yyU7Z1JMZ7cKdYUc6GHjaTTZKQ=,tag:f63Eja3lBfwJCYAOyEt56g==,type:str]", | ||
16 | "unencrypted_suffix": "_unencrypted", | ||
17 | "version": "3.10.2" | ||
18 | } | ||
19 | } | ||
diff --git a/hosts/sif/mail/secrets.yaml b/hosts/sif/email/secrets.yaml index 3c74b710..3c74b710 100644 --- a/hosts/sif/mail/secrets.yaml +++ b/hosts/sif/email/secrets.yaml | |||
diff --git a/hosts/sif/mail/default.nix b/hosts/sif/mail/default.nix deleted file mode 100644 index 8d6cd705..00000000 --- a/hosts/sif/mail/default.nix +++ /dev/null | |||
@@ -1,70 +0,0 @@ | |||
1 | { config, lib, pkgs, ... }: | ||
2 | { | ||
3 | services.postfix = { | ||
4 | enable = true; | ||
5 | enableSmtp = true; | ||
6 | enableSubmission = false; | ||
7 | setSendmail = true; | ||
8 | networksStyle = "host"; | ||
9 | hostname = "sif.midgard.yggdrasil"; | ||
10 | destination = []; | ||
11 | relayHost = "uucp:ymir"; | ||
12 | recipientDelimiter = "+"; | ||
13 | masterConfig = { | ||
14 | uucp = { | ||
15 | type = "unix"; | ||
16 | private = true; | ||
17 | privileged = true; | ||
18 | chroot = false; | ||
19 | command = "pipe"; | ||
20 | args = [ "flags=Fqhu" "user=uucp" ''argv=${config.security.wrapperDir}/uux -z -a $sender - $nexthop!rmail ($recipient)'' ]; | ||
21 | }; | ||
22 | smtps = { | ||
23 | type = "unix"; | ||
24 | private = true; | ||
25 | privileged = true; | ||
26 | chroot = false; | ||
27 | command = "smtp"; | ||
28 | args = [ "-o" "smtp_tls_wrappermode=yes" "-o" "smtp_tls_security_level=encrypt" ]; | ||
29 | }; | ||
30 | }; | ||
31 | config = { | ||
32 | default_transport = "uucp:ymir"; | ||
33 | |||
34 | inet_interfaces = "loopback-only"; | ||
35 | |||
36 | authorized_submit_users = ["!uucp" "static:anyone"]; | ||
37 | message_size_limit = "0"; | ||
38 | |||
39 | sender_dependent_default_transport_maps = ''regexp:${pkgs.writeText "sender_relay" '' | ||
40 | /@(cip|stud)\.ifi\.(lmu|uni-muenchen)\.de$/ smtp:smtp.ifi.lmu.de | ||
41 | /@ifi\.(lmu|uni-muenchen)\.de$/ smtp:smtpin1.ifi.lmu.de:587 | ||
42 | /@math(ematik)?\.(lmu|uni-muenchen)\.de$/ smtps:smtp.math.lmu.de:465 | ||
43 | /@(campus\.)?lmu\.de$/ smtp:postout.lrz.de | ||
44 | ''}''; | ||
45 | sender_bcc_maps = ''regexp:${pkgs.writeText "sender_bcc" '' | ||
46 | /^uni2work(-[^@]*)?@ifi\.lmu\.de$/ uni2work@ifi.lmu.de | ||
47 | /@ifi\.lmu\.de$/ gregor.kleen@ifi.lmu.de | ||
48 | ''}''; | ||
49 | |||
50 | smtp_sasl_auth_enable = true; | ||
51 | smtp_sender_dependent_authentication = true; | ||
52 | smtp_sasl_tls_security_options = "noanonymous"; | ||
53 | smtp_sasl_mechanism_filter = ["plain"]; | ||
54 | smtp_sasl_password_maps = "regexp:/var/db/postfix/sasl_passwd"; | ||
55 | smtp_cname_overrides_servername = false; | ||
56 | smtp_always_send_ehlo = true; | ||
57 | smtp_tls_security_level = "dane"; | ||
58 | |||
59 | smtp_tls_loglevel = "1"; | ||
60 | smtp_dns_support_level = "dnssec"; | ||
61 | }; | ||
62 | }; | ||
63 | |||
64 | sops.secrets.postfix-sasl-passwd = { | ||
65 | key = "sasl-passwd"; | ||
66 | path = "/var/db/postfix/sasl_passwd"; | ||
67 | owner = "postfix"; | ||
68 | sopsFile = ./secrets.yaml; | ||
69 | }; | ||
70 | } | ||
diff --git a/hosts/sif/ruleset.nft b/hosts/sif/ruleset.nft index 2af8b2ee..62339f69 100644 --- a/hosts/sif/ruleset.nft +++ b/hosts/sif/ruleset.nft | |||
@@ -61,7 +61,7 @@ table inet filter { | |||
61 | counter mosh-rx {} | 61 | counter mosh-rx {} |
62 | counter wg-rx {} | 62 | counter wg-rx {} |
63 | counter yggdrasil-gre-rx {} | 63 | counter yggdrasil-gre-rx {} |
64 | counter quickserve-rx {} | 64 | counter miniserve-rx {} |
65 | counter ausweisapp2-rx {} | 65 | counter ausweisapp2-rx {} |
66 | 66 | ||
67 | counter established-rx {} | 67 | counter established-rx {} |
@@ -81,7 +81,7 @@ table inet filter { | |||
81 | counter mosh-tx {} | 81 | counter mosh-tx {} |
82 | counter wg-tx {} | 82 | counter wg-tx {} |
83 | counter yggdrasil-gre-tx {} | 83 | counter yggdrasil-gre-tx {} |
84 | counter quickserve-tx {} | 84 | counter miniserve-tx {} |
85 | 85 | ||
86 | counter tx {} | 86 | counter tx {} |
87 | 87 | ||
@@ -134,7 +134,7 @@ table inet filter { | |||
134 | tcp dport 22 counter name ssh-rx accept | 134 | tcp dport 22 counter name ssh-rx accept |
135 | udp dport 60000-61000 counter name mosh-rx accept | 135 | udp dport 60000-61000 counter name mosh-rx accept |
136 | 136 | ||
137 | tcp dport 8000 counter name quickserve-rx accept | 137 | tcp dport 8080 counter name miniserve-rx accept |
138 | udp dport 24727 counter name ausweisapp2-rx accept | 138 | udp dport 24727 counter name ausweisapp2-rx accept |
139 | 139 | ||
140 | udp dport 51820-51822 counter name wg-rx accept | 140 | udp dport 51820-51822 counter name wg-rx accept |
@@ -173,7 +173,7 @@ table inet filter { | |||
173 | udp sport 51820-51822 counter name wg-tx | 173 | udp sport 51820-51822 counter name wg-tx |
174 | iifname "yggdrasil-wg-*" meta l4proto gre counter name yggdrasil-gre-tx | 174 | iifname "yggdrasil-wg-*" meta l4proto gre counter name yggdrasil-gre-tx |
175 | 175 | ||
176 | tcp sport 8000 counter name quickserve-tx accept | 176 | tcp sport 8080 counter name miniserve-tx accept |
177 | 177 | ||
178 | oifname virbr0 udp sport 67 counter name libvirt-dhcp accept | 178 | oifname virbr0 udp sport 67 counter name libvirt-dhcp accept |
179 | oifname virbr0 udp sport 547 counter name libvirt-dhcp accept | 179 | oifname virbr0 udp sport 547 counter name libvirt-dhcp accept |
diff --git a/hosts/surtr/audiobookshelf.nix b/hosts/surtr/audiobookshelf.nix new file mode 100644 index 00000000..728851a4 --- /dev/null +++ b/hosts/surtr/audiobookshelf.nix | |||
@@ -0,0 +1,66 @@ | |||
1 | { config, ... }: | ||
2 | |||
3 | { | ||
4 | config = { | ||
5 | security.acme.rfc2136Domains = { | ||
6 | "audiobookshelf.yggdrasil.li" = { | ||
7 | restartUnits = ["nginx.service"]; | ||
8 | }; | ||
9 | }; | ||
10 | |||
11 | services.nginx = { | ||
12 | upstreams."audiobookshelf" = { | ||
13 | servers = { | ||
14 | "[2a03:4000:52:ada:4:1::]:28982" = {}; | ||
15 | }; | ||
16 | extraConfig = '' | ||
17 | keepalive 8; | ||
18 | ''; | ||
19 | }; | ||
20 | virtualHosts = { | ||
21 | "audiobookshelf.yggdrasil.li" = { | ||
22 | kTLS = true; | ||
23 | http3 = true; | ||
24 | forceSSL = true; | ||
25 | sslCertificate = "/run/credentials/nginx.service/audiobookshelf.yggdrasil.li.pem"; | ||
26 | sslCertificateKey = "/run/credentials/nginx.service/audiobookshelf.yggdrasil.li.key.pem"; | ||
27 | sslTrustedCertificate = "/run/credentials/nginx.service/audiobookshelf.yggdrasil.li.chain.pem"; | ||
28 | extraConfig = '' | ||
29 | charset utf-8; | ||
30 | ''; | ||
31 | |||
32 | locations = { | ||
33 | "/".extraConfig = '' | ||
34 | proxy_pass http://audiobookshelf; | ||
35 | |||
36 | proxy_http_version 1.1; | ||
37 | proxy_set_header Upgrade $http_upgrade; | ||
38 | proxy_set_header Connection "upgrade"; | ||
39 | |||
40 | proxy_redirect off; | ||
41 | proxy_set_header Host $host; | ||
42 | proxy_set_header X-Real-IP $remote_addr; | ||
43 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||
44 | proxy_set_header X-Forwarded-Host $server_name; | ||
45 | proxy_set_header X-Forwarded-Proto $scheme; | ||
46 | |||
47 | client_max_body_size 0; | ||
48 | proxy_request_buffering off; | ||
49 | proxy_buffering off; | ||
50 | ''; | ||
51 | }; | ||
52 | }; | ||
53 | }; | ||
54 | }; | ||
55 | |||
56 | systemd.services.nginx = { | ||
57 | serviceConfig = { | ||
58 | LoadCredential = [ | ||
59 | "audiobookshelf.yggdrasil.li.key.pem:${config.security.acme.certs."audiobookshelf.yggdrasil.li".directory}/key.pem" | ||
60 | "audiobookshelf.yggdrasil.li.pem:${config.security.acme.certs."audiobookshelf.yggdrasil.li".directory}/fullchain.pem" | ||
61 | "audiobookshelf.yggdrasil.li.chain.pem:${config.security.acme.certs."audiobookshelf.yggdrasil.li".directory}/chain.pem" | ||
62 | ]; | ||
63 | }; | ||
64 | }; | ||
65 | }; | ||
66 | } | ||
diff --git a/hosts/surtr/bifrost/default.nix b/hosts/surtr/bifrost/default.nix index fbfde757..52ab43f5 100644 --- a/hosts/surtr/bifrost/default.nix +++ b/hosts/surtr/bifrost/default.nix | |||
@@ -18,7 +18,7 @@ in { | |||
18 | ListenPort = 51822; | 18 | ListenPort = 51822; |
19 | }; | 19 | }; |
20 | wireguardPeers = [ | 20 | wireguardPeers = [ |
21 | { AllowedIPs = [ "2a03:4000:52:ada:4:1::/96" ]; | 21 | { AllowedIPs = [ "2a03:4000:52:ada:4:1::/96" "2a03:4000:52:ada:6::/80" ]; |
22 | PublicKey = trim (readFile ../../vidhar/network/bifrost/vidhar.pub); | 22 | PublicKey = trim (readFile ../../vidhar/network/bifrost/vidhar.pub); |
23 | } | 23 | } |
24 | ]; | 24 | ]; |
@@ -34,6 +34,8 @@ in { | |||
34 | routes = [ | 34 | routes = [ |
35 | { Destination = "2a03:4000:52:ada:4::/80"; | 35 | { Destination = "2a03:4000:52:ada:4::/80"; |
36 | } | 36 | } |
37 | { Destination = "2a03:4000:52:ada:6::/80"; | ||
38 | } | ||
37 | ]; | 39 | ]; |
38 | linkConfig = { | 40 | linkConfig = { |
39 | RequiredForOnline = false; | 41 | RequiredForOnline = false; |
diff --git a/hosts/surtr/default.nix b/hosts/surtr/default.nix index 0a79d047..9d3101c0 100644 --- a/hosts/surtr/default.nix +++ b/hosts/surtr/default.nix | |||
@@ -7,7 +7,7 @@ with lib; | |||
7 | tmpfs-root qemu-guest openssh rebuild-machines zfs | 7 | tmpfs-root qemu-guest openssh rebuild-machines zfs |
8 | ./zfs.nix ./dns ./tls ./http ./bifrost ./matrix ./postgresql | 8 | ./zfs.nix ./dns ./tls ./http ./bifrost ./matrix ./postgresql |
9 | ./prometheus ./email ./vpn ./borg.nix ./etebase ./immich.nix | 9 | ./prometheus ./email ./vpn ./borg.nix ./etebase ./immich.nix |
10 | ./paperless.nix ./hledger.nix | 10 | ./paperless.nix ./hledger.nix ./audiobookshelf.nix ./kimai.nix |
11 | ]; | 11 | ]; |
12 | 12 | ||
13 | config = { | 13 | config = { |
diff --git a/hosts/surtr/dns/default.nix b/hosts/surtr/dns/default.nix index e878d625..8aca2b97 100644 --- a/hosts/surtr/dns/default.nix +++ b/hosts/surtr/dns/default.nix | |||
@@ -157,7 +157,7 @@ in { | |||
157 | ${concatMapStringsSep "\n" mkZone [ | 157 | ${concatMapStringsSep "\n" mkZone [ |
158 | { domain = "yggdrasil.li"; | 158 | { domain = "yggdrasil.li"; |
159 | addACLs = { "yggdrasil.li" = ["ymir_acme_acl"]; }; | 159 | addACLs = { "yggdrasil.li" = ["ymir_acme_acl"]; }; |
160 | acmeDomains = ["surtr.yggdrasil.li" "yggdrasil.li" "etesync.yggdrasil.li" "immich.yggdrasil.li" "app.etesync.yggdrasil.li" "paperless.yggdrasil.li" "hledger.yggdrasil.li"]; | 160 | acmeDomains = ["surtr.yggdrasil.li" "yggdrasil.li" "etesync.yggdrasil.li" "immich.yggdrasil.li" "app.etesync.yggdrasil.li" "paperless.yggdrasil.li" "hledger.yggdrasil.li" "audiobookshelf.yggdrasil.li" "kimai.yggdrasil.li"]; |
161 | } | 161 | } |
162 | { domain = "nights.email"; | 162 | { domain = "nights.email"; |
163 | addACLs = { "nights.email" = ["ymir_acme_acl"]; }; | 163 | addACLs = { "nights.email" = ["ymir_acme_acl"]; }; |
diff --git a/hosts/surtr/dns/keys/audiobookshelf.yggdrasil.li_acme b/hosts/surtr/dns/keys/audiobookshelf.yggdrasil.li_acme new file mode 100644 index 00000000..e3af9966 --- /dev/null +++ b/hosts/surtr/dns/keys/audiobookshelf.yggdrasil.li_acme | |||
@@ -0,0 +1,19 @@ | |||
1 | { | ||
2 | "data": "ENC[AES256_GCM,data:5BLk/MZtQsLjpdKGiZjtOH1soIXB05MkEoPWyr5m27ho7H5udDjRZE0/OjvSlMzQNjFNJc+OwbeHwaJ8sPLCnXqoFHJXikAmi0gpdFC0uN0JGYCBT1EaK7j6yVHyiqFXo6xwVSCO/zP/fbjItiuqU+B4llGx5N2I4HQqRLFoW/35PZazkR15xI1zo8LeC8T+jm16apFQw2Ih/RqsJK7XlHqnXq9SzeA2qhgkCbJf6aJ6zDS7eQVFt7qHh2mVtV7Al++bZiIkJiNJ5SJO1ck18w2t9HwXBhfMFKXvfFW0I6kKur1qSJk=,iv:rxm5PwOzXDaK+nj2k3bUqlYbIFFA49ispyfamtQqU/A=,tag:7dOua39f+0JsLldLRLr1NQ==,type:str]", | ||
3 | "sops": { | ||
4 | "age": [ | ||
5 | { | ||
6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwV1hIcWNNRVJXOG1xYlFK\nb1VxWG03dGVmS3dmQTltUTR0VmEzYjRFanlBCnE1M1BTZjNCQnpsNkU5ZlR5T3BR\nOHliWUV2bXMrK0w1K3JXbUE0dEhKdzgKLS0tIDdkUEZ4QUE0NUN2TXFYcklybjBS\nRVpxV2J6U1BnUng1dytWTGRkd0FrRGcKVaMCzcrX+BQqbmh95JEk3lRdfnv9uO8n\nwotKxp/+xX7GEF42BOzJ/mWcgyVjABlnWeVyaRxfpbCUrmNuFYO6XA==\n-----END AGE ENCRYPTED FILE-----\n" | ||
8 | }, | ||
9 | { | ||
10 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkZmRFR0gwc1pjVVorK0t6\nVHU4U0xob09Dd2J5QUJXbndZdWp3cytpYWhRCnFnMlZ0cjNGdXN1RWEvQlVHSE1M\nMEdzSjlEb2NkaE1zMEJJOTlCMC9FVFEKLS0tIGJiTEJSRVZZbVUzZVFGeXQwYm1w\ndWVYQVdoVlJnRENTYzhnQm1BcWVRdW8K0ECfLVQBQuZWFFFjdHLcJBz+CDzsOOwh\nOA38qxHD4EWfXR1c3G8RDbow7nB2MGc1Zohc7qhtuTj2wL0qhVKWqA==\n-----END AGE ENCRYPTED FILE-----\n" | ||
12 | } | ||
13 | ], | ||
14 | "lastmodified": "2025-05-09T17:07:16Z", | ||
15 | "mac": "ENC[AES256_GCM,data:K5I6YXQXCUPHFBNVlXIdLLKqiNPVZh95KoHni2m16SdAvTyBab79SZ5xNvotrtKXp0iISCogEgdMm+OWbxYywEiZ+sUsxgx6RE5nAXruZOiAwzuyUr88qgCHTBZnKzgaDZlbYOgWB+LCzr8s5JbcTD4G6/RaXYyqmx7igygubHA=,iv:Zbij4eoDqoP5XYhAsDGBGqlcP5ACQAY/QngTmrJYRzs=,tag:WYxhUEZwmXkQmnpyOuP2Bg==,type:str]", | ||
16 | "unencrypted_suffix": "_unencrypted", | ||
17 | "version": "3.10.2" | ||
18 | } | ||
19 | } | ||
diff --git a/hosts/surtr/dns/keys/kimai.yggdrasil.li_acme b/hosts/surtr/dns/keys/kimai.yggdrasil.li_acme new file mode 100644 index 00000000..bdfb135a --- /dev/null +++ b/hosts/surtr/dns/keys/kimai.yggdrasil.li_acme | |||
@@ -0,0 +1,19 @@ | |||
1 | { | ||
2 | "data": "ENC[AES256_GCM,data:sKFt4pH0Xn7Qm6JFMg/2N7Ht7jtMJukfN+U3dQaoYXPbhRJ+heEtDpXV/WP4AlfbfpIOgTPW3mcmQCwKFNhS00vEsQA4728FfXZzDDmZCa3hwg51wDbL7XUOr0OePgzi86lt0Q193K6CkGqEAa1vFIb//ElEfBYIwdATbmcoAsM3mHhz58X7c1qf8LNuB93o/1N2xXXZI3NWOhOjlviTc2DAhffXDwlMJSYUhldnwtDKmLM1mooJzLgm2p9w7gRD7WPqEqZFq9uFDK69P9uX5T9hFHg=,iv:rAE4sYxxLou4tyD4RWTp3LjQP0cya95coy1MvwfEK/U=,tag:u4SSk8SZFlj0ks7d6tDocw==,type:str]", | ||
3 | "sops": { | ||
4 | "age": [ | ||
5 | { | ||
6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2KzdNUWhEcDB6QmtUTnVh\nNS9Nc2I4UjAzekxhRXo1UmY3SklPejV1TURJCm9NY2lVOERoMDFKTU56Mmh1NHEr\naGV4M1RoVldHV0xyc3Z0MnVqakpjMFUKLS0tIEYxSk9OUm9kMkdtcG5POWRGQVkx\nY1FEaXYwMGo0L0Z0aTVTZDA5aUFDWEUKJ+e/7lR/rNPNVnIy+wkiKiAYMxWp4L7q\nwnSTx451vSnxv9j3JWB43Y7XQC08cisWDj06ULw8FnEbKYOvTYj9mQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
8 | }, | ||
9 | { | ||
10 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwOTU3dUEzaXM5T1VkbDRO\nMm14OG1mUkk2bDRhdnBsMHBkc3kvUzlyNlQwCktFSHJhMnhoQ2J6bC9vUHNLWTRC\nRFpYeHo3N2xjWUhjQnRwQ2Nrc1pRUmsKLS0tIDdPeFBVdkxDd1JWSmcxQ0tLMTBD\ncHU3VExZOUhYUlJvbGNoK3FMK2VIbGMKFk94P9aBY04CPIi983f3Aalgh4fnU+/K\n2mxawSMf9jz8704N5XJfmr2hwNy8hqLIn8bjsEMAPTfE1YBGga4w0g==\n-----END AGE ENCRYPTED FILE-----\n" | ||
12 | } | ||
13 | ], | ||
14 | "lastmodified": "2025-05-24T09:42:23Z", | ||
15 | "mac": "ENC[AES256_GCM,data:diCeJGvBmM0Ng722eKoFwDe7pqZrdLPSLn5j9LfdaFI64BAbSbA5bAq4NFXqdJ1vttarD2A5rEafYoXUxP8228x2GhNyWUGW5AWgBjVPUc59gjs4wYKR5HlkVMIadhTwNheEyoEjrxX40GNBgCG7X3ocOtOYKbKECp433gdAPDg=,iv:d+yJMWj2RyFnveo2ZNrpNeV+amXM+H7vdC0A2F7mwjA=,tag:yjibG2iusdprp0ORghYWhw==,type:str]", | ||
16 | "unencrypted_suffix": "_unencrypted", | ||
17 | "version": "3.10.2" | ||
18 | } | ||
19 | } | ||
diff --git a/hosts/surtr/dns/zones/email.nights.soa b/hosts/surtr/dns/zones/email.nights.soa index 913a88d4..34209a99 100644 --- a/hosts/surtr/dns/zones/email.nights.soa +++ b/hosts/surtr/dns/zones/email.nights.soa | |||
@@ -1,7 +1,7 @@ | |||
1 | $ORIGIN nights.email. | 1 | $ORIGIN nights.email. |
2 | $TTL 3600 | 2 | $TTL 3600 |
3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( |
4 | 2023013000 ; serial | 4 | 2025060700 ; serial |
5 | 10800 ; refresh | 5 | 10800 ; refresh |
6 | 3600 ; retry | 6 | 3600 ; retry |
7 | 604800 ; expire | 7 | 604800 ; expire |
@@ -27,11 +27,7 @@ $TTL 3600 | |||
27 | 27 | ||
28 | _acme-challenge IN NS ns.yggdrasil.li. | 28 | _acme-challenge IN NS ns.yggdrasil.li. |
29 | 29 | ||
30 | ymir._domainkey IN TXT ( | 30 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
31 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | ||
32 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
33 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
34 | ) | ||
35 | 31 | ||
36 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | 32 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. |
37 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | 33 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. |
diff --git a/hosts/surtr/dns/zones/li.141.soa b/hosts/surtr/dns/zones/li.141.soa index ab117f09..78d137bb 100644 --- a/hosts/surtr/dns/zones/li.141.soa +++ b/hosts/surtr/dns/zones/li.141.soa | |||
@@ -1,7 +1,7 @@ | |||
1 | $ORIGIN 141.li. | 1 | $ORIGIN 141.li. |
2 | $TTL 3600 | 2 | $TTL 3600 |
3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( |
4 | 2025020900 ; serial | 4 | 2025060701 ; serial |
5 | 10800 ; refresh | 5 | 10800 ; refresh |
6 | 3600 ; retry | 6 | 3600 ; retry |
7 | 604800 ; expire | 7 | 604800 ; expire |
@@ -45,11 +45,8 @@ ymir IN AAAA 2a03:4000:6:d004:: | |||
45 | ymir IN MX 0 ymir.yggdrasil.li | 45 | ymir IN MX 0 ymir.yggdrasil.li |
46 | ymir IN TXT "v=spf1 redirect=ymir.yggdrasil.li" | 46 | ymir IN TXT "v=spf1 redirect=ymir.yggdrasil.li" |
47 | 47 | ||
48 | ymir._domainkey IN TXT ( | 48 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
49 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | 49 | surtr._domainkey IN CNAME surtr._domainkey.yggdrasil.li. |
50 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
51 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
52 | ) | ||
53 | 50 | ||
54 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | 51 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. |
55 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | 52 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. |
diff --git a/hosts/surtr/dns/zones/li.kleen.soa b/hosts/surtr/dns/zones/li.kleen.soa index a1c7d35a..5dd3e697 100644 --- a/hosts/surtr/dns/zones/li.kleen.soa +++ b/hosts/surtr/dns/zones/li.kleen.soa | |||
@@ -1,7 +1,7 @@ | |||
1 | $ORIGIN kleen.li. | 1 | $ORIGIN kleen.li. |
2 | $TTL 3600 | 2 | $TTL 3600 |
3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( |
4 | 2023013000 ; serial | 4 | 2025060701 ; serial |
5 | 10800 ; refresh | 5 | 10800 ; refresh |
6 | 3600 ; retry | 6 | 3600 ; retry |
7 | 604800 ; expire | 7 | 604800 ; expire |
@@ -27,11 +27,8 @@ $TTL 3600 | |||
27 | 27 | ||
28 | _acme-challenge IN NS ns.yggdrasil.li. | 28 | _acme-challenge IN NS ns.yggdrasil.li. |
29 | 29 | ||
30 | ymir._domainkey IN TXT ( | 30 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
31 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | 31 | surtr._domainkey IN CNAME surtr._domainkey.yggdrasil.li. |
32 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
33 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
34 | ) | ||
35 | 32 | ||
36 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | 33 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. |
37 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | 34 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. |
diff --git a/hosts/surtr/dns/zones/li.synapse.soa b/hosts/surtr/dns/zones/li.synapse.soa index 086d4a85..247cf025 100644 --- a/hosts/surtr/dns/zones/li.synapse.soa +++ b/hosts/surtr/dns/zones/li.synapse.soa | |||
@@ -1,7 +1,7 @@ | |||
1 | $ORIGIN synapse.li. | 1 | $ORIGIN synapse.li. |
2 | $TTL 3600 | 2 | $TTL 3600 |
3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( |
4 | 2023092100 ; serial | 4 | 2025060701 ; serial |
5 | 10800 ; refresh | 5 | 10800 ; refresh |
6 | 3600 ; retry | 6 | 3600 ; retry |
7 | 604800 ; expire | 7 | 604800 ; expire |
diff --git a/hosts/surtr/dns/zones/li.yggdrasil.soa b/hosts/surtr/dns/zones/li.yggdrasil.soa index 2ef120e6..500194ae 100644 --- a/hosts/surtr/dns/zones/li.yggdrasil.soa +++ b/hosts/surtr/dns/zones/li.yggdrasil.soa | |||
@@ -1,7 +1,7 @@ | |||
1 | $ORIGIN yggdrasil.li. | 1 | $ORIGIN yggdrasil.li. |
2 | $TTL 3600 | 2 | $TTL 3600 |
3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( |
4 | 2025021901 ; serial | 4 | 2025060700 ; serial |
5 | 10800 ; refresh | 5 | 10800 ; refresh |
6 | 3600 ; retry | 6 | 3600 ; retry |
7 | 604800 ; expire | 7 | 604800 ; expire |
@@ -93,12 +93,30 @@ _acme-challenge.hledger IN NS ns.yggdrasil.li. | |||
93 | 93 | ||
94 | hledger IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | 94 | hledger IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" |
95 | 95 | ||
96 | audiobookshelf IN A 202.61.241.61 | ||
97 | audiobookshelf IN AAAA 2a03:4000:52:ada:: | ||
98 | audiobookshelf IN MX 0 surtr.yggdrasil.li | ||
99 | audiobookshelf IN TXT "v=spf1 redirect=surtr.yggdrasil.li" | ||
100 | _acme-challenge.audiobookshelf IN NS ns.yggdrasil.li. | ||
101 | |||
102 | audiobookshelf IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | ||
103 | |||
104 | kimai IN A 202.61.241.61 | ||
105 | kimai IN AAAA 2a03:4000:52:ada:: | ||
106 | kimai IN MX 0 surtr.yggdrasil.li | ||
107 | kimai IN TXT "v=spf1 redirect=surtr.yggdrasil.li" | ||
108 | _acme-challenge.kimai IN NS ns.yggdrasil.li. | ||
109 | |||
110 | kimai IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | ||
111 | |||
96 | vidhar IN AAAA 2a03:4000:52:ada:4:1:: | 112 | vidhar IN AAAA 2a03:4000:52:ada:4:1:: |
97 | vidhar IN MX 0 ymir.yggdrasil.li | 113 | vidhar IN MX 0 ymir.yggdrasil.li |
98 | vidhar IN TXT "v=spf1 redirect=yggdrasil.li" | 114 | vidhar IN TXT "v=spf1 redirect=yggdrasil.li" |
99 | 115 | ||
100 | mailout IN A 188.68.51.254 | 116 | mailout IN A 188.68.51.254 |
101 | mailout IN AAAA 2a03:4000:6:d004:: | 117 | mailout IN AAAA 2a03:4000:6:d004:: |
118 | mailout IN A 202.61.241.61 | ||
119 | mailout IN AAAA 2a03:4000:52:ada:: | ||
102 | mailout IN MX 0 ymir.yggdrasil.li | 120 | mailout IN MX 0 ymir.yggdrasil.li |
103 | mailout IN TXT "v=spf1 redirect=yggdrasil.li" | 121 | mailout IN TXT "v=spf1 redirect=yggdrasil.li" |
104 | 122 | ||
diff --git a/hosts/surtr/dns/zones/org.praseodym.soa b/hosts/surtr/dns/zones/org.praseodym.soa index df505b4c..2b97ca19 100644 --- a/hosts/surtr/dns/zones/org.praseodym.soa +++ b/hosts/surtr/dns/zones/org.praseodym.soa | |||
@@ -1,7 +1,7 @@ | |||
1 | $ORIGIN praseodym.org. | 1 | $ORIGIN praseodym.org. |
2 | $TTL 3600 | 2 | $TTL 3600 |
3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( |
4 | 2023013000 ; serial | 4 | 2025060701 ; serial |
5 | 10800 ; refresh | 5 | 10800 ; refresh |
6 | 3600 ; retry | 6 | 3600 ; retry |
7 | 604800 ; expire | 7 | 604800 ; expire |
@@ -32,11 +32,8 @@ surtr IN AAAA 2a03:4000:52:ada:: | |||
32 | surtr IN MX 0 ymir.yggdrasil.li | 32 | surtr IN MX 0 ymir.yggdrasil.li |
33 | surtr IN TXT "v=spf1 redirect=yggdrasil.li" | 33 | surtr IN TXT "v=spf1 redirect=yggdrasil.li" |
34 | 34 | ||
35 | ymir._domainkey IN TXT ( | 35 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
36 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | 36 | surtr._domainkey IN CNAME surtr._domainkey.yggdrasil.li. |
37 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
38 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
39 | ) | ||
40 | 37 | ||
41 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | 38 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. |
42 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | 39 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. |
diff --git a/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py b/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py index 00182523..7c931559 100644 --- a/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py +++ b/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py | |||
@@ -28,10 +28,12 @@ class PolicyHandler(StreamRequestHandler): | |||
28 | 28 | ||
29 | allowed = False | 29 | allowed = False |
30 | user = None | 30 | user = None |
31 | relay_eligible = False | ||
31 | if self.args['sasl_username']: | 32 | if self.args['sasl_username']: |
32 | user = self.args['sasl_username'] | 33 | user = self.args['sasl_username'] |
33 | if self.args['ccert_subject']: | 34 | if self.args['ccert_subject']: |
34 | user = self.args['ccert_subject'] | 35 | user = self.args['ccert_subject'] |
36 | relay_eligible = True | ||
35 | 37 | ||
36 | if user: | 38 | if user: |
37 | with self.server.db_pool.connection() as conn: | 39 | with self.server.db_pool.connection() as conn: |
@@ -44,10 +46,16 @@ class PolicyHandler(StreamRequestHandler): | |||
44 | 46 | ||
45 | with conn.cursor() as cur: | 47 | with conn.cursor() as cur: |
46 | cur.row_factory = namedtuple_row | 48 | cur.row_factory = namedtuple_row |
47 | cur.execute('SELECT "mailbox"."mailbox" as "user", "local", "extension", "domain" FROM "mailbox" INNER JOIN "mailbox_mapping" ON "mailbox".id = "mailbox_mapping"."mailbox" WHERE "mailbox"."mailbox" = %(user)s AND ("local" = %(local)s OR "local" IS NULL) AND ("extension" = %(extension)s OR "extension" IS NULL) AND "domain" = %(domain)s', params = {'user': user, 'local': local, 'extension': extension if extension is not None else '', 'domain': domain}, prepare=True) | 49 | |
48 | for record in cur: | 50 | if relay_eligible: |
49 | logger.debug('Received result: %s', record) | 51 | cur.execute('SELECT EXISTS(SELECT true FROM "mailbox" INNER JOIN "relay_access" ON "mailbox".id = "relay_access"."mailbox" WHERE "mailbox"."mailbox" = %(user)s AND ("domain" = %(domain)s OR %(domain)s ilike CONCAT(\'%%_.\', "domain"))) as "exists"', params = {'user': user, 'domain': domain}) |
50 | allowed = True | 52 | if (row := cur.fetchone()) is not None: |
53 | allowed = row.exists | ||
54 | |||
55 | if not allowed: | ||
56 | cur.execute('SELECT EXISTS(SELECT true FROM "mailbox" INNER JOIN "mailbox_mapping" ON "mailbox".id = "mailbox_mapping"."mailbox" WHERE "mailbox"."mailbox" = %(user)s AND ("local" = %(local)s OR "local" IS NULL) AND ("extension" = %(extension)s OR "extension" IS NULL) AND "domain" = %(domain)s) as "exists"', params = {'user': user, 'local': local, 'extension': extension if extension is not None else '', 'domain': domain}, prepare=True) | ||
57 | if (row := cur.fetchone()) is not None: | ||
58 | allowed = row.exists | ||
51 | 59 | ||
52 | action = '550 5.7.0 Sender address not authorized for current user' | 60 | action = '550 5.7.0 Sender address not authorized for current user' |
53 | if allowed: | 61 | if allowed: |
diff --git a/hosts/surtr/email/default.nix b/hosts/surtr/email/default.nix index 13b33c7f..c993bb18 100644 --- a/hosts/surtr/email/default.nix +++ b/hosts/surtr/email/default.nix | |||
@@ -1,4 +1,4 @@ | |||
1 | { config, pkgs, lib, flakeInputs, ... }: | 1 | { config, pkgs, lib, flake, flakeInputs, ... }: |
2 | 2 | ||
3 | with lib; | 3 | with lib; |
4 | 4 | ||
@@ -15,7 +15,7 @@ let | |||
15 | 15 | ||
16 | for file in $out/pipe/bin/*; do | 16 | for file in $out/pipe/bin/*; do |
17 | wrapProgram $file \ | 17 | wrapProgram $file \ |
18 | --set PATH "${pkgs.coreutils}/bin:${pkgs.rspamd}/bin" | 18 | --set PATH "${makeBinPath (with pkgs; [coreutils rspamd])}" |
19 | done | 19 | done |
20 | ''; | 20 | ''; |
21 | }; | 21 | }; |
@@ -33,12 +33,28 @@ let | |||
33 | }); | 33 | }); |
34 | }); | 34 | }); |
35 | }; | 35 | }; |
36 | internal-policy-server = | ||
37 | let | ||
38 | workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./internal-policy-server; }; | ||
39 | pythonSet = flake.lib.pythonSet { | ||
40 | inherit pkgs; | ||
41 | python = pkgs.python312; | ||
42 | overlay = workspace.mkPyprojectOverlay { | ||
43 | sourcePreference = "wheel"; | ||
44 | }; | ||
45 | }; | ||
46 | virtualEnv = pythonSet.mkVirtualEnv "internal-policy-server-env" workspace.deps.default; | ||
47 | in virtualEnv.overrideAttrs (oldAttrs: { | ||
48 | meta = (oldAttrs.meta or {}) // { | ||
49 | mainProgram = "internal-policy-server"; | ||
50 | }; | ||
51 | }); | ||
36 | 52 | ||
37 | nftables-nologin-script = pkgs.writeScript "nftables-mail-nologin" '' | 53 | nftables-nologin-script = pkgs.resholve.writeScript "nftables-mail-nologin" { |
38 | #!${pkgs.zsh}/bin/zsh | 54 | inputs = with pkgs; [inetutils nftables gnugrep findutils]; |
39 | 55 | interpreter = lib.getExe pkgs.zsh; | |
56 | } '' | ||
40 | set -e | 57 | set -e |
41 | export PATH="${lib.makeBinPath (with pkgs; [inetutils nftables])}:$PATH" | ||
42 | 58 | ||
43 | typeset -a as_sets mnt_bys route route6 | 59 | typeset -a as_sets mnt_bys route route6 |
44 | as_sets=(${lib.escapeShellArgs config.services.email.nologin.ASSets}) | 60 | as_sets=(${lib.escapeShellArgs config.services.email.nologin.ASSets}) |
@@ -51,7 +67,7 @@ let | |||
51 | elif [[ "''${line}" =~ "^route6:\s+(.+)$" ]]; then | 67 | elif [[ "''${line}" =~ "^route6:\s+(.+)$" ]]; then |
52 | route6+=($match[1]) | 68 | route6+=($match[1]) |
53 | fi | 69 | fi |
54 | done < <(whois -h whois.radb.net "!i''${as_set},1" | egrep -o 'AS[0-9]+' | xargs -- whois -h whois.radb.net -- -i origin) | 70 | done < <(whois -h whois.radb.net "!i''${as_set},1" | grep -Eo 'AS[0-9]+' | xargs whois -h whois.radb.net -- -i origin) |
55 | done | 71 | done |
56 | for mnt_by in $mnt_bys; do | 72 | for mnt_by in $mnt_bys; do |
57 | while IFS=$'\n' read line; do | 73 | while IFS=$'\n' read line; do |
@@ -190,6 +206,7 @@ in { | |||
190 | "reject_unauth_destination" | 206 | "reject_unauth_destination" |
191 | "reject_unknown_recipient_domain" | 207 | "reject_unknown_recipient_domain" |
192 | "reject_unverified_recipient" | 208 | "reject_unverified_recipient" |
209 | "check_policy_service unix:/run/postfix-internal-policy.sock" | ||
193 | ]; | 210 | ]; |
194 | unverified_recipient_reject_code = "550"; | 211 | unverified_recipient_reject_code = "550"; |
195 | unverified_recipient_reject_reason = "Recipient address lookup failed"; | 212 | unverified_recipient_reject_reason = "Recipient address lookup failed"; |
@@ -215,7 +232,7 @@ in { | |||
215 | smtpd_client_event_limit_exceptions = ""; | 232 | smtpd_client_event_limit_exceptions = ""; |
216 | 233 | ||
217 | milter_default_action = "accept"; | 234 | milter_default_action = "accept"; |
218 | smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock"]; | 235 | smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock" "local:/run/postsrsd/postsrsd-milter.sock"]; |
219 | non_smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock"]; | 236 | non_smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock"]; |
220 | 237 | ||
221 | alias_maps = ""; | 238 | alias_maps = ""; |
@@ -237,11 +254,6 @@ in { | |||
237 | ::/0 silent-discard, dsn | 254 | ::/0 silent-discard, dsn |
238 | ''}"; | 255 | ''}"; |
239 | 256 | ||
240 | sender_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.forwardPort}"; | ||
241 | sender_canonical_classes = "envelope_sender"; | ||
242 | recipient_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.reversePort}"; | ||
243 | recipient_canonical_classes = ["envelope_recipient" "header_recipient"]; | ||
244 | |||
245 | virtual_mailbox_domains = ''pgsql:${pkgs.writeText "virtual_mailbox_domains.cf" '' | 257 | virtual_mailbox_domains = ''pgsql:${pkgs.writeText "virtual_mailbox_domains.cf" '' |
246 | hosts = postgresql:///email | 258 | hosts = postgresql:///email |
247 | dbname = email | 259 | dbname = email |
@@ -256,11 +268,24 @@ in { | |||
256 | virtual_transport = "dvlmtp:unix:/run/dovecot-lmtp"; | 268 | virtual_transport = "dvlmtp:unix:/run/dovecot-lmtp"; |
257 | smtputf8_enable = false; | 269 | smtputf8_enable = false; |
258 | 270 | ||
259 | authorized_submit_users = "inline:{ root= postfwd= }"; | 271 | authorized_submit_users = "inline:{ root= postfwd= dovecot2= }"; |
272 | authorized_flush_users = "inline:{ root= }"; | ||
273 | authorized_mailq_users = "inline:{ root= }"; | ||
260 | 274 | ||
261 | postscreen_access_list = ""; | 275 | postscreen_access_list = ""; |
262 | postscreen_denylist_action = "drop"; | 276 | postscreen_denylist_action = "drop"; |
263 | postscreen_greet_action = "enforce"; | 277 | postscreen_greet_action = "enforce"; |
278 | |||
279 | sender_bcc_maps = ''pgsql:${pkgs.writeText "sender_bcc_maps.cf" '' | ||
280 | hosts = postgresql:///email | ||
281 | dbname = email | ||
282 | query = SELECT value FROM sender_bcc_maps WHERE key = '%s' | ||
283 | ''}''; | ||
284 | recipient_bcc_maps = ''pgsql:${pkgs.writeText "recipient_bcc_maps.cf" '' | ||
285 | hosts = postgresql:///email | ||
286 | dbname = email | ||
287 | query = SELECT value FROM recipient_bcc_maps WHERE key = '%s' | ||
288 | ''}''; | ||
264 | }; | 289 | }; |
265 | masterConfig = { | 290 | masterConfig = { |
266 | "465" = { | 291 | "465" = { |
@@ -285,7 +310,7 @@ in { | |||
285 | hosts = postgresql:///email | 310 | hosts = postgresql:///email |
286 | dbname = email | 311 | dbname = email |
287 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) | 312 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) |
288 | ''},permit_tls_all_clientcerts,reject}'' | 313 | ''},check_policy_service unix:/run/postfix-internal-policy.sock,permit_tls_all_clientcerts,reject}'' |
289 | "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject" | 314 | "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject" |
290 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" | 315 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" |
291 | "-o" "unverified_sender_reject_code=550" | 316 | "-o" "unverified_sender_reject_code=550" |
@@ -315,7 +340,7 @@ in { | |||
315 | hosts = postgresql:///email | 340 | hosts = postgresql:///email |
316 | dbname = email | 341 | dbname = email |
317 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) | 342 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) |
318 | ''},permit_sasl_authenticated,reject}'' | 343 | ''},check_policy_service unix:/run/postfix-internal-policy.sock,permit_sasl_authenticated,reject}'' |
319 | "-o" "smtpd_relay_restrictions=permit_sasl_authenticated,reject" | 344 | "-o" "smtpd_relay_restrictions=permit_sasl_authenticated,reject" |
320 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" | 345 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" |
321 | "-o" "unverified_sender_reject_code=550" | 346 | "-o" "unverified_sender_reject_code=550" |
@@ -366,17 +391,19 @@ in { | |||
366 | 391 | ||
367 | services.postsrsd = { | 392 | services.postsrsd = { |
368 | enable = true; | 393 | enable = true; |
369 | domain = "surtr.yggdrasil.li"; | 394 | domains = [ "surtr.yggdrasil.li" ] ++ concatMap (domain: [".${domain}" domain]) emailDomains; |
370 | separator = "+"; | 395 | separator = "+"; |
371 | excludeDomains = [ "surtr.yggdrasil.li" | 396 | extraConfig = '' |
372 | ] ++ concatMap (domain: [".${domain}" domain]) emailDomains; | 397 | socketmap = unix:/run/postsrsd/postsrsd-socketmap.sock |
398 | milter = unix:/run/postsrsd/postsrsd-milter.sock | ||
399 | ''; | ||
373 | }; | 400 | }; |
374 | 401 | ||
375 | services.opendkim = { | 402 | services.opendkim = { |
376 | enable = true; | 403 | enable = true; |
377 | user = "postfix"; group = "postfix"; | 404 | user = "postfix"; group = "postfix"; |
378 | socket = "local:/run/opendkim/opendkim.sock"; | 405 | socket = "local:/run/opendkim/opendkim.sock"; |
379 | domains = ''csl:${concatStringsSep "," (["surtr.yggdrasil.li"] ++ emailDomains)}''; | 406 | domains = ''csl:${concatStringsSep "," (["surtr.yggdrasil.li" "yggdrasil.li" "141.li" "kleen.li" "synapse.li" "praseodym.org"] ++ emailDomains)}''; |
380 | selector = "surtr"; | 407 | selector = "surtr"; |
381 | configFile = builtins.toFile "opendkim.conf" '' | 408 | configFile = builtins.toFile "opendkim.conf" '' |
382 | Syslog true | 409 | Syslog true |
@@ -675,7 +702,7 @@ in { | |||
675 | plugin { | 702 | plugin { |
676 | plugin = fts fts_xapian | 703 | plugin = fts fts_xapian |
677 | fts = xapian | 704 | fts = xapian |
678 | fts_xapian = partial=2 full=20 attachments=1 verbose=1 | 705 | fts_xapian = partial=3 full=20 attachments=1 verbose=1 |
679 | 706 | ||
680 | fts_autoindex = yes | 707 | fts_autoindex = yes |
681 | 708 | ||
@@ -695,7 +722,7 @@ in { | |||
695 | startAt = "*-*-* 22:00:00 Europe/Berlin"; | 722 | startAt = "*-*-* 22:00:00 Europe/Berlin"; |
696 | serviceConfig = { | 723 | serviceConfig = { |
697 | Type = "oneshot"; | 724 | Type = "oneshot"; |
698 | ExecStart = "${pkgs.dovecot}/bin/doveadm fts optimize -A"; | 725 | ExecStart = "${getExe' pkgs.dovecot "doveadm"} fts optimize -A"; |
699 | PrivateDevices = true; | 726 | PrivateDevices = true; |
700 | PrivateNetwork = true; | 727 | PrivateNetwork = true; |
701 | ProtectKernelTunables = true; | 728 | ProtectKernelTunables = true; |
@@ -780,7 +807,7 @@ in { | |||
780 | systemd.services.dovecot2 = { | 807 | systemd.services.dovecot2 = { |
781 | preStart = '' | 808 | preStart = '' |
782 | for f in /etc/dovecot/sieve_flag.d/*.sieve /etc/dovecot/sieve_before.d/*.sieve; do | 809 | for f in /etc/dovecot/sieve_flag.d/*.sieve /etc/dovecot/sieve_before.d/*.sieve; do |
783 | ${pkgs.dovecot_pigeonhole}/bin/sievec $f | 810 | ${getExe' pkgs.dovecot_pigeonhole "sievec"} $f |
784 | done | 811 | done |
785 | ''; | 812 | ''; |
786 | 813 | ||
@@ -847,15 +874,16 @@ in { | |||
847 | charset utf-8; | 874 | charset utf-8; |
848 | source_charset utf-8; | 875 | source_charset utf-8; |
849 | ''; | 876 | ''; |
850 | root = pkgs.runCommand "mta-sts.${domain}" {} '' | 877 | root = pkgs.writeTextFile { |
851 | mkdir -p $out/.well-known | 878 | name = "mta-sts.${domain}"; |
852 | cp ${pkgs.writeText "mta-sts.${domain}.txt" '' | 879 | destination = "/.well-known/mta-sts.txt"; |
880 | text = '' | ||
853 | version: STSv1 | 881 | version: STSv1 |
854 | mode: enforce | 882 | mode: enforce |
855 | max_age: 2419200 | 883 | max_age: 2419200 |
856 | mx: mailin.${domain} | 884 | mx: mailin.${domain} |
857 | ''} $out/.well-known/mta-sts.txt | 885 | ''; |
858 | ''; | 886 | }; |
859 | }; | 887 | }; |
860 | }) emailDomains); | 888 | }) emailDomains); |
861 | }; | 889 | }; |
@@ -872,7 +900,7 @@ in { | |||
872 | systemd.services.spm = { | 900 | systemd.services.spm = { |
873 | serviceConfig = { | 901 | serviceConfig = { |
874 | Type = "notify"; | 902 | Type = "notify"; |
875 | ExecStart = "${pkgs.spm}/bin/spm-server"; | 903 | ExecStart = getExe' pkgs.spm "spm-server"; |
876 | User = "spm"; | 904 | User = "spm"; |
877 | Group = "spm"; | 905 | Group = "spm"; |
878 | 906 | ||
@@ -930,7 +958,7 @@ in { | |||
930 | serviceConfig = { | 958 | serviceConfig = { |
931 | Type = "notify"; | 959 | Type = "notify"; |
932 | 960 | ||
933 | ExecStart = "${ccert-policy-server}/bin/ccert-policy-server"; | 961 | ExecStart = getExe' ccert-policy-server "ccert-policy-server"; |
934 | 962 | ||
935 | Environment = [ | 963 | Environment = [ |
936 | "PGDATABASE=email" | 964 | "PGDATABASE=email" |
@@ -963,6 +991,53 @@ in { | |||
963 | }; | 991 | }; |
964 | users.groups."postfix-ccert-sender-policy" = {}; | 992 | users.groups."postfix-ccert-sender-policy" = {}; |
965 | 993 | ||
994 | systemd.sockets."postfix-internal-policy" = { | ||
995 | requiredBy = ["postfix.service"]; | ||
996 | wants = ["postfix-internal-policy.service"]; | ||
997 | socketConfig = { | ||
998 | ListenStream = "/run/postfix-internal-policy.sock"; | ||
999 | }; | ||
1000 | }; | ||
1001 | systemd.services."postfix-internal-policy" = { | ||
1002 | after = [ "postgresql.service" ]; | ||
1003 | bindsTo = [ "postgresql.service" ]; | ||
1004 | |||
1005 | serviceConfig = { | ||
1006 | Type = "notify"; | ||
1007 | |||
1008 | ExecStart = lib.getExe internal-policy-server; | ||
1009 | |||
1010 | Environment = [ | ||
1011 | "PGDATABASE=email" | ||
1012 | ]; | ||
1013 | |||
1014 | DynamicUser = false; | ||
1015 | User = "postfix-internal-policy"; | ||
1016 | Group = "postfix-internal-policy"; | ||
1017 | ProtectSystem = "strict"; | ||
1018 | SystemCallFilter = "@system-service"; | ||
1019 | NoNewPrivileges = true; | ||
1020 | ProtectKernelTunables = true; | ||
1021 | ProtectKernelModules = true; | ||
1022 | ProtectKernelLogs = true; | ||
1023 | ProtectControlGroups = true; | ||
1024 | MemoryDenyWriteExecute = true; | ||
1025 | RestrictSUIDSGID = true; | ||
1026 | KeyringMode = "private"; | ||
1027 | ProtectClock = true; | ||
1028 | RestrictRealtime = true; | ||
1029 | PrivateDevices = true; | ||
1030 | PrivateTmp = true; | ||
1031 | ProtectHostname = true; | ||
1032 | ReadWritePaths = ["/run/postgresql"]; | ||
1033 | }; | ||
1034 | }; | ||
1035 | users.users."postfix-internal-policy" = { | ||
1036 | isSystemUser = true; | ||
1037 | group = "postfix-internal-policy"; | ||
1038 | }; | ||
1039 | users.groups."postfix-internal-policy" = {}; | ||
1040 | |||
966 | services.postfwd = { | 1041 | services.postfwd = { |
967 | enable = true; | 1042 | enable = true; |
968 | cache = false; | 1043 | cache = false; |
diff --git a/hosts/surtr/email/internal-policy-server/.envrc b/hosts/surtr/email/internal-policy-server/.envrc new file mode 100644 index 00000000..2c909235 --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/.envrc | |||
@@ -0,0 +1,4 @@ | |||
1 | use flake | ||
2 | |||
3 | [[ -d ".venv" ]] || ( uv venv && uv sync ) | ||
4 | . .venv/bin/activate | ||
diff --git a/hosts/surtr/email/internal-policy-server/.gitignore b/hosts/surtr/email/internal-policy-server/.gitignore new file mode 100644 index 00000000..4ccfae70 --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | .venv | ||
2 | **/__pycache__ | ||
diff --git a/hosts/surtr/email/internal-policy-server/internal_policy_server/__init__.py b/hosts/surtr/email/internal-policy-server/internal_policy_server/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/internal_policy_server/__init__.py | |||
diff --git a/hosts/surtr/email/internal-policy-server/internal_policy_server/__main__.py b/hosts/surtr/email/internal-policy-server/internal_policy_server/__main__.py new file mode 100644 index 00000000..04f1a59a --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/internal_policy_server/__main__.py | |||
@@ -0,0 +1,106 @@ | |||
1 | from systemd.daemon import listen_fds | ||
2 | from sdnotify import SystemdNotifier | ||
3 | from socketserver import StreamRequestHandler, ThreadingMixIn | ||
4 | from systemd_socketserver import SystemdSocketServer | ||
5 | import sys | ||
6 | from threading import Thread | ||
7 | from psycopg_pool import ConnectionPool | ||
8 | from psycopg.rows import namedtuple_row | ||
9 | |||
10 | import logging | ||
11 | |||
12 | |||
13 | class PolicyHandler(StreamRequestHandler): | ||
14 | def handle(self): | ||
15 | logger.debug('Handling new connection...') | ||
16 | |||
17 | self.args = dict() | ||
18 | |||
19 | line = None | ||
20 | while line := self.rfile.readline().removesuffix(b'\n'): | ||
21 | if b'=' not in line: | ||
22 | break | ||
23 | |||
24 | key, val = line.split(sep=b'=', maxsplit=1) | ||
25 | self.args[key.decode()] = val.decode() | ||
26 | |||
27 | logger.info('Connection parameters: %s', self.args) | ||
28 | |||
29 | allowed = False | ||
30 | user = None | ||
31 | if self.args['sasl_username']: | ||
32 | user = self.args['sasl_username'] | ||
33 | if self.args['ccert_subject']: | ||
34 | user = self.args['ccert_subject'] | ||
35 | |||
36 | with self.server.db_pool.connection() as conn: | ||
37 | local, domain = self.args['recipient'].split(sep='@', maxsplit=1) | ||
38 | extension = None | ||
39 | if '+' in local: | ||
40 | local, extension = local.split(sep='+', maxsplit=1) | ||
41 | |||
42 | logger.debug('Parsed recipient address: %s', {'local': local, 'extension': extension, 'domain': domain}) | ||
43 | |||
44 | with conn.cursor() as cur: | ||
45 | cur.row_factory = namedtuple_row | ||
46 | cur.execute('SELECT id, internal FROM "mailbox_mapping" WHERE ("local" = %(local)s OR "local" IS NULL) AND ("extension" = %(extension)s OR "extension" IS NULL) AND "domain" = %(domain)s', params = {'local': local, 'extension': extension if extension is not None else '', 'domain': domain}, prepare = True) | ||
47 | if (row := cur.fetchone()) is not None: | ||
48 | if not row.internal: | ||
49 | logger.debug('Recipient mailbox is not internal') | ||
50 | allowed = True | ||
51 | elif user: | ||
52 | cur.execute('SELECT EXISTS(SELECT true FROM "mailbox_mapping_access" INNER JOIN "mailbox" ON "mailbox".id = "mailbox_mapping_access"."mailbox" WHERE mailbox_mapping = %(mailbox_mapping)s AND "mailbox"."mailbox" = %(user)s) as "exists"', params = { 'mailbox_mapping': row.id, 'user': user }, prepare = True) | ||
53 | if (row := cur.fetchone()) is not None: | ||
54 | allowed = row.exists | ||
55 | else: | ||
56 | logger.debug('Recipient is not local') | ||
57 | allowed = True | ||
58 | |||
59 | action = '550 5.7.0 Recipient mailbox mapping not authorized for current user' | ||
60 | if allowed: | ||
61 | action = 'DUNNO' | ||
62 | |||
63 | logger.info('Reached verdict: %s', {'allowed': allowed, 'action': action}) | ||
64 | self.wfile.write(f'action={action}\n\n'.encode()) | ||
65 | |||
66 | class ThreadedSystemdSocketServer(ThreadingMixIn, SystemdSocketServer): | ||
67 | def __init__(self, fd, RequestHandlerClass): | ||
68 | super().__init__(fd, RequestHandlerClass) | ||
69 | |||
70 | self.db_pool = ConnectionPool(min_size=1) | ||
71 | self.db_pool.wait() | ||
72 | |||
73 | def main(): | ||
74 | global logger | ||
75 | logger = logging.getLogger(__name__) | ||
76 | console_handler = logging.StreamHandler() | ||
77 | console_handler.setFormatter( logging.Formatter('[%(levelname)s](%(name)s): %(message)s') ) | ||
78 | if sys.stderr.isatty(): | ||
79 | console_handler.setFormatter( logging.Formatter('%(asctime)s [%(levelname)s](%(name)s): %(message)s') ) | ||
80 | logger.addHandler(console_handler) | ||
81 | logger.setLevel(logging.DEBUG) | ||
82 | |||
83 | # log uncaught exceptions | ||
84 | def log_exceptions(type, value, tb): | ||
85 | global logger | ||
86 | |||
87 | logger.error(value) | ||
88 | sys.__excepthook__(type, value, tb) # calls default excepthook | ||
89 | |||
90 | sys.excepthook = log_exceptions | ||
91 | |||
92 | fds = listen_fds() | ||
93 | servers = [ThreadedSystemdSocketServer(fd, PolicyHandler) for fd in fds] | ||
94 | |||
95 | if servers: | ||
96 | for server in servers: | ||
97 | Thread(name=f'Server for fd{server.fileno()}', target=server.serve_forever).start() | ||
98 | else: | ||
99 | return 2 | ||
100 | |||
101 | SystemdNotifier().notify('READY=1') | ||
102 | |||
103 | return 0 | ||
104 | |||
105 | if __name__ == '__main__': | ||
106 | sys.exit(main()) | ||
diff --git a/hosts/surtr/email/internal-policy-server/pyproject.toml b/hosts/surtr/email/internal-policy-server/pyproject.toml new file mode 100644 index 00000000..c697cd01 --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/pyproject.toml | |||
@@ -0,0 +1,18 @@ | |||
1 | [project] | ||
2 | name = "internal-policy-server" | ||
3 | version = "0.1.0" | ||
4 | requires-python = ">=3.12" | ||
5 | dependencies = [ | ||
6 | "psycopg>=3.2.9", | ||
7 | "psycopg-binary>=3.2.9", | ||
8 | "psycopg-pool>=3.2.6", | ||
9 | "sdnotify>=0.3.2", | ||
10 | "systemd-socketserver>=1.0", | ||
11 | ] | ||
12 | |||
13 | [project.scripts] | ||
14 | internal-policy-server = "internal_policy_server.__main__:main" | ||
15 | |||
16 | [build-system] | ||
17 | requires = ["hatchling"] | ||
18 | build-backend = "hatchling.build" | ||
diff --git a/hosts/surtr/email/internal-policy-server/uv.lock b/hosts/surtr/email/internal-policy-server/uv.lock new file mode 100644 index 00000000..f7a4e729 --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/uv.lock | |||
@@ -0,0 +1,119 @@ | |||
1 | version = 1 | ||
2 | revision = 2 | ||
3 | requires-python = ">=3.12" | ||
4 | |||
5 | [[package]] | ||
6 | name = "internal-policy-server" | ||
7 | version = "0.1.0" | ||
8 | source = { editable = "." } | ||
9 | dependencies = [ | ||
10 | { name = "psycopg" }, | ||
11 | { name = "psycopg-binary" }, | ||
12 | { name = "psycopg-pool" }, | ||
13 | { name = "sdnotify" }, | ||
14 | { name = "systemd-socketserver" }, | ||
15 | ] | ||
16 | |||
17 | [package.metadata] | ||
18 | requires-dist = [ | ||
19 | { name = "psycopg", specifier = ">=3.2.9" }, | ||
20 | { name = "psycopg-binary", specifier = ">=3.2.9" }, | ||
21 | { name = "psycopg-pool", specifier = ">=3.2.6" }, | ||
22 | { name = "sdnotify", specifier = ">=0.3.2" }, | ||
23 | { name = "systemd-socketserver", specifier = ">=1.0" }, | ||
24 | ] | ||
25 | |||
26 | [[package]] | ||
27 | name = "psycopg" | ||
28 | version = "3.2.9" | ||
29 | source = { registry = "https://pypi.org/simple" } | ||
30 | dependencies = [ | ||
31 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, | ||
32 | { name = "tzdata", marker = "sys_platform == 'win32'" }, | ||
33 | ] | ||
34 | sdist = { url = "https://files.pythonhosted.org/packages/27/4a/93a6ab570a8d1a4ad171a1f4256e205ce48d828781312c0bbaff36380ecb/psycopg-3.2.9.tar.gz", hash = "sha256:2fbb46fcd17bc81f993f28c47f1ebea38d66ae97cc2dbc3cad73b37cefbff700", size = 158122, upload-time = "2025-05-13T16:11:15.533Z" } | ||
35 | wheels = [ | ||
36 | { url = "https://files.pythonhosted.org/packages/44/b0/a73c195a56eb6b92e937a5ca58521a5c3346fb233345adc80fd3e2f542e2/psycopg-3.2.9-py3-none-any.whl", hash = "sha256:01a8dadccdaac2123c916208c96e06631641c0566b22005493f09663c7a8d3b6", size = 202705, upload-time = "2025-05-13T16:06:26.584Z" }, | ||
37 | ] | ||
38 | |||
39 | [[package]] | ||
40 | name = "psycopg-binary" | ||
41 | version = "3.2.9" | ||
42 | source = { registry = "https://pypi.org/simple" } | ||
43 | wheels = [ | ||
44 | { url = "https://files.pythonhosted.org/packages/29/6f/ec9957e37a606cd7564412e03f41f1b3c3637a5be018d0849914cb06e674/psycopg_binary-3.2.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:be7d650a434921a6b1ebe3fff324dbc2364393eb29d7672e638ce3e21076974e", size = 4022205, upload-time = "2025-05-13T16:07:48.195Z" }, | ||
45 | { url = "https://files.pythonhosted.org/packages/6b/ba/497b8bea72b20a862ac95a94386967b745a472d9ddc88bc3f32d5d5f0d43/psycopg_binary-3.2.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6a76b4722a529390683c0304501f238b365a46b1e5fb6b7249dbc0ad6fea51a0", size = 4083795, upload-time = "2025-05-13T16:07:50.917Z" }, | ||
46 | { url = "https://files.pythonhosted.org/packages/42/07/af9503e8e8bdad3911fd88e10e6a29240f9feaa99f57d6fac4a18b16f5a0/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96a551e4683f1c307cfc3d9a05fec62c00a7264f320c9962a67a543e3ce0d8ff", size = 4655043, upload-time = "2025-05-13T16:07:54.857Z" }, | ||
47 | { url = "https://files.pythonhosted.org/packages/28/ed/aff8c9850df1648cc6a5cc7a381f11ee78d98a6b807edd4a5ae276ad60ad/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:61d0a6ceed8f08c75a395bc28cb648a81cf8dee75ba4650093ad1a24a51c8724", size = 4477972, upload-time = "2025-05-13T16:07:57.925Z" }, | ||
48 | { url = "https://files.pythonhosted.org/packages/5c/bd/8e9d1b77ec1a632818fe2f457c3a65af83c68710c4c162d6866947d08cc5/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad280bbd409bf598683dda82232f5215cfc5f2b1bf0854e409b4d0c44a113b1d", size = 4737516, upload-time = "2025-05-13T16:08:01.616Z" }, | ||
49 | { url = "https://files.pythonhosted.org/packages/46/ec/222238f774cd5a0881f3f3b18fb86daceae89cc410f91ef6a9fb4556f236/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76eddaf7fef1d0994e3d536ad48aa75034663d3a07f6f7e3e601105ae73aeff6", size = 4436160, upload-time = "2025-05-13T16:08:04.278Z" }, | ||
50 | { url = "https://files.pythonhosted.org/packages/37/78/af5af2a1b296eeca54ea7592cd19284739a844974c9747e516707e7b3b39/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:52e239cd66c4158e412318fbe028cd94b0ef21b0707f56dcb4bdc250ee58fd40", size = 3753518, upload-time = "2025-05-13T16:08:07.567Z" }, | ||
51 | { url = "https://files.pythonhosted.org/packages/ec/ac/8a3ed39ea069402e9e6e6a2f79d81a71879708b31cc3454283314994b1ae/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:08bf9d5eabba160dd4f6ad247cf12f229cc19d2458511cab2eb9647f42fa6795", size = 3313598, upload-time = "2025-05-13T16:08:09.999Z" }, | ||
52 | { url = "https://files.pythonhosted.org/packages/da/43/26549af068347c808fbfe5f07d2fa8cef747cfff7c695136172991d2378b/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1b2cf018168cad87580e67bdde38ff5e51511112f1ce6ce9a8336871f465c19a", size = 3407289, upload-time = "2025-05-13T16:08:12.66Z" }, | ||
53 | { url = "https://files.pythonhosted.org/packages/67/55/ea8d227c77df8e8aec880ded398316735add8fda5eb4ff5cc96fac11e964/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:14f64d1ac6942ff089fc7e926440f7a5ced062e2ed0949d7d2d680dc5c00e2d4", size = 3472493, upload-time = "2025-05-13T16:08:15.672Z" }, | ||
54 | { url = "https://files.pythonhosted.org/packages/3c/02/6ff2a5bc53c3cd653d281666728e29121149179c73fddefb1e437024c192/psycopg_binary-3.2.9-cp312-cp312-win_amd64.whl", hash = "sha256:7a838852e5afb6b4126f93eb409516a8c02a49b788f4df8b6469a40c2157fa21", size = 2927400, upload-time = "2025-05-13T16:08:18.652Z" }, | ||
55 | { url = "https://files.pythonhosted.org/packages/28/0b/f61ff4e9f23396aca674ed4d5c9a5b7323738021d5d72d36d8b865b3deaf/psycopg_binary-3.2.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:98bbe35b5ad24a782c7bf267596638d78aa0e87abc7837bdac5b2a2ab954179e", size = 4017127, upload-time = "2025-05-13T16:08:21.391Z" }, | ||
56 | { url = "https://files.pythonhosted.org/packages/bc/00/7e181fb1179fbfc24493738b61efd0453d4b70a0c4b12728e2b82db355fd/psycopg_binary-3.2.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:72691a1615ebb42da8b636c5ca9f2b71f266be9e172f66209a361c175b7842c5", size = 4080322, upload-time = "2025-05-13T16:08:24.049Z" }, | ||
57 | { url = "https://files.pythonhosted.org/packages/58/fd/94fc267c1d1392c4211e54ccb943be96ea4032e761573cf1047951887494/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25ab464bfba8c401f5536d5aa95f0ca1dd8257b5202eede04019b4415f491351", size = 4655097, upload-time = "2025-05-13T16:08:27.376Z" }, | ||
58 | { url = "https://files.pythonhosted.org/packages/41/17/31b3acf43de0b2ba83eac5878ff0dea5a608ca2a5c5dd48067999503a9de/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e8aeefebe752f46e3c4b769e53f1d4ad71208fe1150975ef7662c22cca80fab", size = 4482114, upload-time = "2025-05-13T16:08:30.781Z" }, | ||
59 | { url = "https://files.pythonhosted.org/packages/85/78/b4d75e5fd5a85e17f2beb977abbba3389d11a4536b116205846b0e1cf744/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7e4e4dd177a8665c9ce86bc9caae2ab3aa9360b7ce7ec01827ea1baea9ff748", size = 4737693, upload-time = "2025-05-13T16:08:34.625Z" }, | ||
60 | { url = "https://files.pythonhosted.org/packages/3b/95/7325a8550e3388b00b5e54f4ced5e7346b531eb4573bf054c3dbbfdc14fe/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fc2915949e5c1ea27a851f7a472a7da7d0a40d679f0a31e42f1022f3c562e87", size = 4437423, upload-time = "2025-05-13T16:08:37.444Z" }, | ||
61 | { url = "https://files.pythonhosted.org/packages/1a/db/cef77d08e59910d483df4ee6da8af51c03bb597f500f1fe818f0f3b925d3/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a1fa38a4687b14f517f049477178093c39c2a10fdcced21116f47c017516498f", size = 3758667, upload-time = "2025-05-13T16:08:40.116Z" }, | ||
62 | { url = "https://files.pythonhosted.org/packages/95/3e/252fcbffb47189aa84d723b54682e1bb6d05c8875fa50ce1ada914ae6e28/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5be8292d07a3ab828dc95b5ee6b69ca0a5b2e579a577b39671f4f5b47116dfd2", size = 3320576, upload-time = "2025-05-13T16:08:43.243Z" }, | ||
63 | { url = "https://files.pythonhosted.org/packages/1c/cd/9b5583936515d085a1bec32b45289ceb53b80d9ce1cea0fef4c782dc41a7/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:778588ca9897b6c6bab39b0d3034efff4c5438f5e3bd52fda3914175498202f9", size = 3411439, upload-time = "2025-05-13T16:08:47.321Z" }, | ||
64 | { url = "https://files.pythonhosted.org/packages/45/6b/6f1164ea1634c87956cdb6db759e0b8c5827f989ee3cdff0f5c70e8331f2/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f0d5b3af045a187aedbd7ed5fc513bd933a97aaff78e61c3745b330792c4345b", size = 3477477, upload-time = "2025-05-13T16:08:51.166Z" }, | ||
65 | { url = "https://files.pythonhosted.org/packages/7b/1d/bf54cfec79377929da600c16114f0da77a5f1670f45e0c3af9fcd36879bc/psycopg_binary-3.2.9-cp313-cp313-win_amd64.whl", hash = "sha256:2290bc146a1b6a9730350f695e8b670e1d1feb8446597bed0bbe7c3c30e0abcb", size = 2928009, upload-time = "2025-05-13T16:08:53.67Z" }, | ||
66 | ] | ||
67 | |||
68 | [[package]] | ||
69 | name = "psycopg-pool" | ||
70 | version = "3.2.6" | ||
71 | source = { registry = "https://pypi.org/simple" } | ||
72 | dependencies = [ | ||
73 | { name = "typing-extensions" }, | ||
74 | ] | ||
75 | sdist = { url = "https://files.pythonhosted.org/packages/cf/13/1e7850bb2c69a63267c3dbf37387d3f71a00fd0e2fa55c5db14d64ba1af4/psycopg_pool-3.2.6.tar.gz", hash = "sha256:0f92a7817719517212fbfe2fd58b8c35c1850cdd2a80d36b581ba2085d9148e5", size = 29770, upload-time = "2025-02-26T12:03:47.129Z" } | ||
76 | wheels = [ | ||
77 | { url = "https://files.pythonhosted.org/packages/47/fd/4feb52a55c1a4bd748f2acaed1903ab54a723c47f6d0242780f4d97104d4/psycopg_pool-3.2.6-py3-none-any.whl", hash = "sha256:5887318a9f6af906d041a0b1dc1c60f8f0dda8340c2572b74e10907b51ed5da7", size = 38252, upload-time = "2025-02-26T12:03:45.073Z" }, | ||
78 | ] | ||
79 | |||
80 | [[package]] | ||
81 | name = "sdnotify" | ||
82 | version = "0.3.2" | ||
83 | source = { registry = "https://pypi.org/simple" } | ||
84 | sdist = { url = "https://files.pythonhosted.org/packages/ce/d8/9fdc36b2a912bf78106de4b3f0de3891ff8f369e7a6f80be842b8b0b6bd5/sdnotify-0.3.2.tar.gz", hash = "sha256:73977fc746b36cc41184dd43c3fe81323e7b8b06c2bb0826c4f59a20c56bb9f1", size = 2459, upload-time = "2017-08-02T20:03:44.395Z" } | ||
85 | |||
86 | [[package]] | ||
87 | name = "systemd-python" | ||
88 | version = "235" | ||
89 | source = { registry = "https://pypi.org/simple" } | ||
90 | sdist = { url = "https://files.pythonhosted.org/packages/10/9e/ab4458e00367223bda2dd7ccf0849a72235ee3e29b36dce732685d9b7ad9/systemd-python-235.tar.gz", hash = "sha256:4e57f39797fd5d9e2d22b8806a252d7c0106c936039d1e71c8c6b8008e695c0a", size = 61677, upload-time = "2023-02-11T13:42:16.588Z" } | ||
91 | |||
92 | [[package]] | ||
93 | name = "systemd-socketserver" | ||
94 | version = "1.0" | ||
95 | source = { registry = "https://pypi.org/simple" } | ||
96 | dependencies = [ | ||
97 | { name = "systemd-python" }, | ||
98 | ] | ||
99 | wheels = [ | ||
100 | { url = "https://files.pythonhosted.org/packages/d8/4f/b28b7f08880120a26669b080ca74487c8c67e8b54dcb0467a8f0c9f38ed6/systemd_socketserver-1.0-py3-none-any.whl", hash = "sha256:987a8bfbf28d959e7c2966c742ad7bad482f05e121077defcf95bb38267db9a8", size = 3248, upload-time = "2020-04-26T05:26:40.661Z" }, | ||
101 | ] | ||
102 | |||
103 | [[package]] | ||
104 | name = "typing-extensions" | ||
105 | version = "4.13.2" | ||
106 | source = { registry = "https://pypi.org/simple" } | ||
107 | sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } | ||
108 | wheels = [ | ||
109 | { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, | ||
110 | ] | ||
111 | |||
112 | [[package]] | ||
113 | name = "tzdata" | ||
114 | version = "2025.2" | ||
115 | source = { registry = "https://pypi.org/simple" } | ||
116 | sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } | ||
117 | wheels = [ | ||
118 | { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, | ||
119 | ] | ||
diff --git a/hosts/surtr/kimai.nix b/hosts/surtr/kimai.nix new file mode 100644 index 00000000..454b3d80 --- /dev/null +++ b/hosts/surtr/kimai.nix | |||
@@ -0,0 +1,68 @@ | |||
1 | { config, ... }: | ||
2 | |||
3 | { | ||
4 | config = { | ||
5 | security.acme.rfc2136Domains = { | ||
6 | "kimai.yggdrasil.li" = { | ||
7 | restartUnits = ["nginx.service"]; | ||
8 | }; | ||
9 | }; | ||
10 | |||
11 | services.nginx = { | ||
12 | upstreams."kimai" = { | ||
13 | servers = { | ||
14 | "[2a03:4000:52:ada:6::2]:80" = {}; | ||
15 | }; | ||
16 | extraConfig = '' | ||
17 | keepalive 8; | ||
18 | ''; | ||
19 | }; | ||
20 | virtualHosts = { | ||
21 | "kimai.yggdrasil.li" = { | ||
22 | kTLS = true; | ||
23 | http3 = true; | ||
24 | forceSSL = true; | ||
25 | sslCertificate = "/run/credentials/nginx.service/kimai.yggdrasil.li.pem"; | ||
26 | sslCertificateKey = "/run/credentials/nginx.service/kimai.yggdrasil.li.key.pem"; | ||
27 | sslTrustedCertificate = "/run/credentials/nginx.service/kimai.yggdrasil.li.chain.pem"; | ||
28 | extraConfig = '' | ||
29 | charset utf-8; | ||
30 | ''; | ||
31 | |||
32 | locations = { | ||
33 | "/".extraConfig = '' | ||
34 | proxy_pass http://kimai; | ||
35 | |||
36 | proxy_http_version 1.1; | ||
37 | proxy_set_header Upgrade $http_upgrade; | ||
38 | proxy_set_header Connection "upgrade"; | ||
39 | |||
40 | proxy_redirect off; | ||
41 | proxy_set_header Host $host; | ||
42 | proxy_set_header X-Real-IP $remote_addr; | ||
43 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||
44 | proxy_set_header X-Forwarded-Host $server_name; | ||
45 | proxy_set_header X-Forwarded-Proto $scheme; | ||
46 | |||
47 | client_max_body_size 0; | ||
48 | proxy_request_buffering off; | ||
49 | proxy_buffering off; | ||
50 | |||
51 | proxy_read_timeout 300; | ||
52 | ''; | ||
53 | }; | ||
54 | }; | ||
55 | }; | ||
56 | }; | ||
57 | |||
58 | systemd.services.nginx = { | ||
59 | serviceConfig = { | ||
60 | LoadCredential = [ | ||
61 | "kimai.yggdrasil.li.key.pem:${config.security.acme.certs."kimai.yggdrasil.li".directory}/key.pem" | ||
62 | "kimai.yggdrasil.li.pem:${config.security.acme.certs."kimai.yggdrasil.li".directory}/fullchain.pem" | ||
63 | "kimai.yggdrasil.li.chain.pem:${config.security.acme.certs."kimai.yggdrasil.li".directory}/chain.pem" | ||
64 | ]; | ||
65 | }; | ||
66 | }; | ||
67 | }; | ||
68 | } | ||
diff --git a/hosts/surtr/postgresql/default.nix b/hosts/surtr/postgresql/default.nix index 059f4088..3786ea7c 100644 --- a/hosts/surtr/postgresql/default.nix +++ b/hosts/surtr/postgresql/default.nix | |||
@@ -280,6 +280,64 @@ in { | |||
280 | CREATE VIEW imap_user ("user", "password", quota_rule) AS SELECT mailbox.mailbox AS "user", "password", quota_rule FROM mailbox_quota_rule INNER JOIN mailbox ON mailbox_quota_rule.mailbox = mailbox.mailbox; | 280 | CREATE VIEW imap_user ("user", "password", quota_rule) AS SELECT mailbox.mailbox AS "user", "password", quota_rule FROM mailbox_quota_rule INNER JOIN mailbox ON mailbox_quota_rule.mailbox = mailbox.mailbox; |
281 | 281 | ||
282 | COMMIT; | 282 | COMMIT; |
283 | |||
284 | BEGIN; | ||
285 | SELECT _v.register_patch('013-internal', ARRAY['000-base'], null); | ||
286 | |||
287 | ALTER TABLE mailbox_mapping ADD COLUMN internal bool NOT NULL DEFAULT false; | ||
288 | CREATE TABLE mailbox_mapping_access ( | ||
289 | id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), | ||
290 | mailbox_mapping uuid REFERENCES mailbox_mapping(id), | ||
291 | mailbox uuid REFERENCES mailbox(id) | ||
292 | ); | ||
293 | CREATE USER "postfix-internal-policy"; | ||
294 | GRANT CONNECT ON DATABASE "email" TO "postfix-internal-policy"; | ||
295 | ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO "postfix-internal-policy"; | ||
296 | GRANT SELECT ON ALL TABLES IN SCHEMA public TO "postfix-internal-policy"; | ||
297 | |||
298 | COMMIT; | ||
299 | |||
300 | BEGIN; | ||
301 | SELECT _v.register_patch('014-relay', ARRAY['000-base'], null); | ||
302 | |||
303 | CREATE TABLE relay_access ( | ||
304 | id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), | ||
305 | mailbox uuid REFERENCES mailbox(id), | ||
306 | domain citext NOT NULL CONSTRAINT domain_non_empty CHECK (domain <> ''') | ||
307 | ); | ||
308 | |||
309 | COMMIT; | ||
310 | |||
311 | BEGIN; | ||
312 | SELECT _v.register_patch('015-relay-unique', ARRAY['000-base', '014-relay'], null); | ||
313 | |||
314 | CREATE UNIQUE INDEX relay_unique ON relay_access (mailbox, domain); | ||
315 | |||
316 | COMMIT; | ||
317 | |||
318 | BEGIN; | ||
319 | SELECT _v.register_patch('015-sender_bcc', null, null); | ||
320 | |||
321 | CREATE TABLE sender_bcc_maps ( | ||
322 | id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), | ||
323 | key text NOT NULL CONSTRAINT key_not_empty CHECK (key <> '''), | ||
324 | value text NOT NULL CONSTRAINT value_not_empty CHECK (value <> '''), | ||
325 | CONSTRAINT key_unique UNIQUE (key) | ||
326 | ); | ||
327 | |||
328 | COMMIT; | ||
329 | |||
330 | BEGIN; | ||
331 | SELECT _v.register_patch('016-recipient_bcc', null, null); | ||
332 | |||
333 | CREATE TABLE recipient_bcc_maps ( | ||
334 | id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), | ||
335 | key text NOT NULL CONSTRAINT key_not_empty CHECK (key <> '''), | ||
336 | value text NOT NULL CONSTRAINT value_not_empty CHECK (value <> '''), | ||
337 | CONSTRAINT recipient_bcc_maps_key_unique UNIQUE (key) | ||
338 | ); | ||
339 | |||
340 | COMMIT; | ||
283 | ''} | 341 | ''} |
284 | 342 | ||
285 | psql etebase postgres -eXf ${pkgs.writeText "etebase.sql" '' | 343 | psql etebase postgres -eXf ${pkgs.writeText "etebase.sql" '' |
diff --git a/hosts/surtr/tls/tsig_keys/audiobookshelf.yggdrasil.li b/hosts/surtr/tls/tsig_keys/audiobookshelf.yggdrasil.li new file mode 100644 index 00000000..8dd610dd --- /dev/null +++ b/hosts/surtr/tls/tsig_keys/audiobookshelf.yggdrasil.li | |||
@@ -0,0 +1,19 @@ | |||
1 | { | ||
2 | "data": "ENC[AES256_GCM,data:r9jhdTlbDnCMq1QLJutn76uz1Ml8MFs7fXYRSiVYh1gafcXXsUZBq5+qqoQI,iv:un/luttuKpCiMf53fa2SRY0ffttGiYwT8DuHCKEnnEI=,tag:SkNULZSulQmP99aB/Ec+Fw==,type:str]", | ||
3 | "sops": { | ||
4 | "age": [ | ||
5 | { | ||
6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkZGJzaEsrSU4raHlTVDVB\nczRnWVlSTTRuNXU0T3F1RTkxKytXeVJRdGpFCk9WMzNBR1NaTTMzN3BGQ2JmTjVt\nRU4rSWxCYjJPYVRzLzR0OVRYQm45TUkKLS0tIDNyMnpPN2VKUFFadTkveXRYeWps\nYUNaTjRJLzdWUnREaUVIWkpFV0FTZ2MKJS0K49SdkLW4p67FlgboHy/OVvCiUA7g\nuv5b+yotkQmh5xJwr7CUvwRewqJh56mg1yhWmE8wzpgLZMIjRXcQCQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
8 | }, | ||
9 | { | ||
10 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQQ1h3M1lXTXVNd0d6cmtU\nU2JtUzFFblJudmEycnJONkkwME9wWm5jWVFzCnRYVEFWaVNvSW9GZ05TRWF4L2ho\nanltVytEU3ZOdHk1VHY5aGJDUkdDdmcKLS0tIEtzOFVkbmpjbWN5d0c1VEpxc1Rr\nSzJwclYxeC9TVWNaK2gwUmJSY0x1ZVUKTNivp5iS+1tzVMjMn17/ncvHcELhjQ/B\n0OVz4VpKM2wv6CjEcIMxmchqT8p8GFYVRrKUdqO2GEKOoe8ANtidWA==\n-----END AGE ENCRYPTED FILE-----\n" | ||
12 | } | ||
13 | ], | ||
14 | "lastmodified": "2025-05-09T17:07:16Z", | ||
15 | "mac": "ENC[AES256_GCM,data:SwS+8UQnPgHORobKLu+u2pNaMdKIvR+etUed8btbbne/IX/Wpxt0qyPYXNNGGRkN3KAxTHWjRRdrKU1bkuTU3ER1c94T935ExDESKJLVjzaEF5VSWCqLyUNCMsY2ANw84UES2swK4YI4zF1CP7rD8tKFFld78IWZoeQ7XNGDMRA=,iv:neLvamISgQ5+aqW1iRj9xJoXq1weNNyy7KCFG2+WRQE=,tag:66SDO61WnKU6DVElo9CImg==,type:str]", | ||
16 | "unencrypted_suffix": "_unencrypted", | ||
17 | "version": "3.10.2" | ||
18 | } | ||
19 | } | ||
diff --git a/hosts/surtr/tls/tsig_keys/kimai.yggdrasil.li b/hosts/surtr/tls/tsig_keys/kimai.yggdrasil.li new file mode 100644 index 00000000..b9199975 --- /dev/null +++ b/hosts/surtr/tls/tsig_keys/kimai.yggdrasil.li | |||
@@ -0,0 +1,19 @@ | |||
1 | { | ||
2 | "data": "ENC[AES256_GCM,data:ATcU3Ix7o5d/49rD5H8je1ozTjoghrloMh5DIZ5WE3oYauUAknpGfr9xq92V,iv:vy9YK5Ot7CCjMtgAGVeAUQuaSw4F5kmmZ0GJYV9kCdQ=,tag:F/MXTUM2AI1fGXa9Ewn8yQ==,type:str]", | ||
3 | "sops": { | ||
4 | "age": [ | ||
5 | { | ||
6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDMEF0cUdydERYVzJCa3pW\nTlo0NUFON0d5RGJFVnVTNVg3cjNEUERQMEdFClEvQW5odlNEd2F1VTFmMWQrL2RB\ncllFZVpIVVJrNTJsSGF4UEdZMnVmQzAKLS0tIFUrQkkzRVZiOFNiTnFCT1pEYVRM\nQm8wV1JkQ3RrR1dkL0FsNkhsY2kxa1kKGnAo/6oibgXexUU31THdLu6X+pRtrkjD\nZnXGPZ2xaESDVUVEYQPVpNrjt9brZGJBI1BasrkEwHAXMbJC236yYQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
8 | }, | ||
9 | { | ||
10 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3MGs1Z2ZqK2pqWHdVYTJH\naTlncHdPa3Zld0JhQW5Ccmc1SStWSnlDR0JrCmpML2d4TGdldUdoZCtaWVpPZVl0\nVm4waWVBS1orRS90ZS96N0Y2M29LY0UKLS0tIEI1Z2VVbVVxRUpOZEN4NnBRRklC\nQXloelZCb04xbmduTlVuL005TlRGMHMKfLB6zA3sj3HgDBC7VGfGVB6I1zJpt0PV\nkCV2yADgvAA2pT9HPg9IWAEpTPysOBiuE2jPNtFvylZYwTDHoumFnQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
12 | } | ||
13 | ], | ||
14 | "lastmodified": "2025-05-24T09:42:23Z", | ||
15 | "mac": "ENC[AES256_GCM,data:0pk1LpWPmX9td/TwJFxwWp5pTDyW78UtHXMDah+V9Tmgi8hH7ONdysgjwpDwS/c4zGnMA3qtobEL286U3//CTXt2qVsiUGLsnngzs2E6yBg8oGMYlGrch4M355Fl5ZxYsc8QLA6qWcuZ4H3QW8PnoqdJixcHoYLoxG01dzh4Bc0=,iv:zchk4enI1D80BkJLji5RLm7OTk3GeF8nYHuwqBxCXIM=,tag:bgkknPMqkSidi6bDFfv6UQ==,type:str]", | ||
16 | "unencrypted_suffix": "_unencrypted", | ||
17 | "version": "3.10.2" | ||
18 | } | ||
19 | } | ||
diff --git a/hosts/surtr/vpn/default.nix b/hosts/surtr/vpn/default.nix index 1bdcf74e..92223144 100644 --- a/hosts/surtr/vpn/default.nix +++ b/hosts/surtr/vpn/default.nix | |||
@@ -1,4 +1,4 @@ | |||
1 | { pkgs, config, lib, ... }: | 1 | { flake, pkgs, config, lib, ... }: |
2 | 2 | ||
3 | with lib; | 3 | with lib; |
4 | 4 | ||
@@ -22,7 +22,11 @@ in { | |||
22 | "--load-credential=surtr.priv:/run/credentials/container@vpn.service/surtr.priv" | 22 | "--load-credential=surtr.priv:/run/credentials/container@vpn.service/surtr.priv" |
23 | "--network-ipvlan=ens3:upstream" | 23 | "--network-ipvlan=ens3:upstream" |
24 | ]; | 24 | ]; |
25 | config = { | 25 | config = let hostConfig = config; in { config, pkgs, ... }: { |
26 | system.stateVersion = lib.mkIf hostConfig.containers."vpn".ephemeral config.system.nixos.release; | ||
27 | system.configurationRevision = mkIf (flake ? rev) flake.rev; | ||
28 | nixpkgs.pkgs = hostConfig.nixpkgs.pkgs; | ||
29 | |||
26 | boot.kernel.sysctl = { | 30 | boot.kernel.sysctl = { |
27 | "net.core.rmem_max" = 4194304; | 31 | "net.core.rmem_max" = 4194304; |
28 | "net.core.wmem_max" = 4194304; | 32 | "net.core.wmem_max" = 4194304; |
diff --git a/hosts/surtr/vpn/geri.pub b/hosts/surtr/vpn/geri.pub index ed5de2b2..2cd9b24e 100644 --- a/hosts/surtr/vpn/geri.pub +++ b/hosts/surtr/vpn/geri.pub | |||
@@ -1 +1 @@ | |||
sYuQSNZHzfegv8HRz71jnZm2nFLGeRnaGwVonhKUj2k= | hhER05bvstOTGfiAG3IJsFkBNWCUZHokBXwaiC5d534= | ||
diff --git a/hosts/vidhar/audiobookshelf/abs-podcast-autoplaylist-gkleen.toml b/hosts/vidhar/audiobookshelf/abs-podcast-autoplaylist-gkleen.toml new file mode 100644 index 00000000..a5319e38 --- /dev/null +++ b/hosts/vidhar/audiobookshelf/abs-podcast-autoplaylist-gkleen.toml | |||
@@ -0,0 +1,19 @@ | |||
1 | { | ||
2 | "data": "ENC[AES256_GCM,data:60OmHwuLC7RJVNNn8lsCFjIFrtDlmmT3yAm3DYn/K2b8OJB/lzKBhMUCyPpoI2lfMm6y47/DMwXI3ExH3QwfgGRf4i/Tcv7p6FCkjFgDc0RhAM7cXNSnh1gKTff8QYtPoNIzmycFCThNr7iZsPsf2/1npVaVHTnt9nTc+cmDLc+lELlvjSE00JOXch/if7KPwFww9K83XlrFmoRvwybfXR0unJqxK2XLvj+dQuKD4Bhyb88iSgu4dX1yw2uBSZBD16S4Io0DaZ+as5Yw4Kon7WMj3Jd5kz8ZxK+0NCy1CVJHOfJIwgYl0SVPp4DpbAPtJO4R/ciXyDQ/XGpoLtHjxnKXaJlJoSiA7FhuSEk+jB/peLHrYV1obdIRE5Dstly01S5cydKlfQ+A0TSjxFSWBYMEiD89sD09Br3iSJX5FejOoS8d2IQJ5faVzgQl4T5aBKsxCNNwmYrEe8m9HN7o2eer8nTKMln5IxZi3ZWhnjgJfrJ4QTXFndxCb78jo8HroN3+7VhoM136UZkqH1OMrIgAH/XSlW08G8m9MRamKsAWklq9aVflcEsPWTHmYW7rjAapQYf+jyK6BbfHcYmyKM82TFZ5iNB60Pth6EJgb2V8PZiChGvDzQvFYYOO3p9a/J8bVqsnPZBXXYcIBt42ZuRPvyyUTfM+75V1eYE9ZGFML+QlofwNCAg+/Rnl+RRy4z+8xQxd8Dn06geDpHsr4yND72FRUTKLbjxF5xfbzBRcZEXjGkyFdEAF7rB78I8xIqii+n6Yt8uEURmd4geI9KWXRQnwofTz9pklaAnRbER8zy/BJIiIYy8zecUHJn9v/DPnsnksfL6RRmG4tHaRBDbpAag0kVkCrpO/flK6dZOl/wvoVVVqT2O69a9/RpHLSV2f//ZS6L9s6vaYe4pXL0M6QymgA22sNHaws6XggJlTxVOFGRejMGYrKqVWtC+2UNbnel+/J0N1qj4luWfQaf9+1j+fq7vyLSzXYFCiyOLAznpqOhzKu6VWy2IbR0UnCoL5ZbhIba9e2MXM7Czy9Yee4xc=,iv:M0GbtFFl1XUeq+y9H+MiD+9z/ASB9hsd06KhpPzSwEo=,tag:vTLIIf+CeZN6DU25CSP8tw==,type:str]", | ||
3 | "sops": { | ||
4 | "age": [ | ||
5 | { | ||
6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0aE1XNUNCM1Q5V0d3R2JG\nbjJZTmdvQ21JbmtyR0ZmODFMdVBGejRoam1vCjMzMGdTb3BReDVCa2JJU0JrSHFP\ndTdicU5TRjIrTWpteDMzeGtDT0xaelkKLS0tIFhaSlFrbzFDUjRZV0lGR0cydVdZ\nY2xma0VSVXlTM1JucFJUSys4dlRvdEUK9gQNQEdKDDf1ikWzd6uTlE50WsfO/EB0\nGH2Ono6oNWbKWTyl/wRO8NzXx0nudwqq66s0oBLIdTMQOpIBBNI0XQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
8 | }, | ||
9 | { | ||
10 | "recipient": "age1qffdqvy9arld9zd5a5cylt0n98xhcns5shxhrhwjq5g4qa844ejselaa4l", | ||
11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiRWFqSHNlY1IvMkkwaEto\ncHZHa2p1Y25SakFkS2JYMlRFcFhnZGY1dVRFCkxSWmxvcHZMampQKzdKRHI0ZVMx\nUTFtR0pHbzFaQ0xQUFA2ZERDSWpwS0UKLS0tIFBaSGczY3VWdy9TKzRDZWZ2SElY\nbVQ4dDNhQllmVmViWGs5c3V4TmNscjQKeugevQJFAN/8JrzeAm4hm2JsQGb26BCb\n3dKYnN1kJU7oVHr1aVfXwMpELNYt9poX6WTY2h9lsdHuRlqoFXAA5Q==\n-----END AGE ENCRYPTED FILE-----\n" | ||
12 | } | ||
13 | ], | ||
14 | "lastmodified": "2025-05-10T10:25:15Z", | ||
15 | "mac": "ENC[AES256_GCM,data:dhj7e+vF3uiR6I22PR5tdNdM8EyrWmGGTIqjj8H7IdNIsZBHzjeHlBDFOwN7z/JMO0BVwIi4DmhApg2BSPGsQZGDQZ28UTCC8TDtd1zmfGtSP8R8AFHADYdLK/desMtHg6BZTnLv5tpba34WWdflMNOQpwgWPZsIk/DkLaoXdvk=,iv:qkoAZngTz2sfWdxDs+h8Mb2IrkF8gqnQoR5iRoeKjbY=,tag:zXrkBJmPM4ItJxMnX8IDxQ==,type:str]", | ||
16 | "unencrypted_suffix": "_unencrypted", | ||
17 | "version": "3.10.2" | ||
18 | } | ||
19 | } | ||
diff --git a/hosts/vidhar/audiobookshelf/default.nix b/hosts/vidhar/audiobookshelf/default.nix new file mode 100644 index 00000000..136bcaff --- /dev/null +++ b/hosts/vidhar/audiobookshelf/default.nix | |||
@@ -0,0 +1,21 @@ | |||
1 | { config, pkgs, lib, ... }: | ||
2 | |||
3 | { | ||
4 | config = { | ||
5 | services.audiobookshelf = { | ||
6 | enable = true; | ||
7 | host = "2a03:4000:52:ada:4:1::"; | ||
8 | port = 28982; | ||
9 | }; | ||
10 | |||
11 | users.groups.audiobookshelf.members = [ "gkleen" ]; | ||
12 | |||
13 | services.abs-podcast-autoplaylist = { | ||
14 | gkleen = {}; | ||
15 | }; | ||
16 | sops.secrets.${config.services.abs-podcast-autoplaylist.gkleen.configSecret} = { | ||
17 | format = "binary"; | ||
18 | sopsFile = ./abs-podcast-autoplaylist-gkleen.toml; | ||
19 | }; | ||
20 | }; | ||
21 | } | ||
diff --git a/hosts/vidhar/default.nix b/hosts/vidhar/default.nix index 1af9c5e0..7da17e6f 100644 --- a/hosts/vidhar/default.nix +++ b/hosts/vidhar/default.nix | |||
@@ -4,7 +4,7 @@ with lib; | |||
4 | 4 | ||
5 | { | 5 | { |
6 | imports = with flake.nixosModules.systemProfiles; [ | 6 | imports = with flake.nixosModules.systemProfiles; [ |
7 | ./zfs.nix ./network ./samba.nix ./dns ./prometheus ./borg ./pgbackrest ./postgresql.nix ./immich.nix ./paperless ./hledger | 7 | ./zfs.nix ./network ./samba.nix ./dns ./prometheus ./borg ./pgbackrest ./postgresql.nix ./immich.nix ./paperless ./hledger ./audiobookshelf ./kimai |
8 | tmpfs-root zfs | 8 | tmpfs-root zfs |
9 | initrd-all-crypto-modules default-locale openssh rebuild-machines | 9 | initrd-all-crypto-modules default-locale openssh rebuild-machines |
10 | build-server | 10 | build-server |
@@ -387,7 +387,7 @@ with lib; | |||
387 | algorithm = "zstd"; | 387 | algorithm = "zstd"; |
388 | }; | 388 | }; |
389 | 389 | ||
390 | environment.systemPackages = with pkgs; [iotop vmtouch inetutils]; | 390 | environment.systemPackages = with pkgs; [iotop vmtouch]; |
391 | 391 | ||
392 | systemd.sysusers.enable = false; | 392 | systemd.sysusers.enable = false; |
393 | system.stateVersion = "21.05"; | 393 | system.stateVersion = "21.05"; |
diff --git a/hosts/vidhar/kimai/default.nix b/hosts/vidhar/kimai/default.nix new file mode 100644 index 00000000..0258697b --- /dev/null +++ b/hosts/vidhar/kimai/default.nix | |||
@@ -0,0 +1,89 @@ | |||
1 | { flake, config, ... }: | ||
2 | |||
3 | { | ||
4 | config = { | ||
5 | boot.enableContainers = true; | ||
6 | boot.kernel.sysctl = { | ||
7 | "net.netfilter.nf_log_all_netns" = true; | ||
8 | }; | ||
9 | |||
10 | containers."kimai" = { | ||
11 | autoStart = true; | ||
12 | ephemeral = true; | ||
13 | bindMounts = { | ||
14 | "/var/lib/kimai" = { | ||
15 | hostPath = "/var/lib/kimai/state"; | ||
16 | isReadOnly = false; | ||
17 | }; | ||
18 | "/var/lib/mysql" = { | ||
19 | hostPath = "/var/lib/kimai/mysql"; | ||
20 | isReadOnly = false; | ||
21 | }; | ||
22 | }; | ||
23 | privateNetwork = true; | ||
24 | # forwardPorts = [ | ||
25 | # { containerPort = 80; | ||
26 | # hostPort = 28983; | ||
27 | # } | ||
28 | # ]; | ||
29 | hostAddress = "192.168.52.113"; | ||
30 | localAddress = "192.168.52.114"; | ||
31 | hostAddress6 = "2a03:4000:52:ada:6::1"; | ||
32 | localAddress6 = "2a03:4000:52:ada:6::2"; | ||
33 | config = let hostConfig = config; in { config, pkgs, lib, ... }: { | ||
34 | system.stateVersion = lib.mkIf hostConfig.containers."kimai".ephemeral config.system.nixos.release; | ||
35 | system.configurationRevision = lib.mkIf (flake ? rev) flake.rev; | ||
36 | nixpkgs.pkgs = hostConfig.nixpkgs.pkgs; | ||
37 | |||
38 | services.kimai.sites."kimai.yggdrasil.li" = { | ||
39 | database.socket = "/run/mysqld/mysqld.sock"; | ||
40 | }; | ||
41 | |||
42 | networking = { | ||
43 | useDHCP = false; | ||
44 | useNetworkd = true; | ||
45 | useHostResolvConf = false; | ||
46 | firewall.enable = false; | ||
47 | nftables = { | ||
48 | enable = true; | ||
49 | rulesetFile = ./ruleset.nft; | ||
50 | }; | ||
51 | }; | ||
52 | |||
53 | services.resolved.fallbackDns = [ | ||
54 | "9.9.9.10#dns10.quad9.net" | ||
55 | "149.112.112.10#dns10.quad9.net" | ||
56 | "2620:fe::10#dns10.quad9.net" | ||
57 | "2620:fe::fe:10#dns10.quad9.net" | ||
58 | ]; | ||
59 | |||
60 | systemd.network = { | ||
61 | networks.upstream = { | ||
62 | name = "eth0"; | ||
63 | matchConfig = { | ||
64 | Name = "eth0"; | ||
65 | }; | ||
66 | linkConfig = { | ||
67 | RequiredForOnline = true; | ||
68 | }; | ||
69 | networkConfig = { | ||
70 | Address = [ "192.168.52.114/32" "2a03:4000:52:ada:6::2/128" ]; | ||
71 | LLMNR = false; | ||
72 | MulticastDNS = false; | ||
73 | }; | ||
74 | routes = [ | ||
75 | { Destination = "192.168.52.113/32"; } | ||
76 | { Destination = "2a03:4000:52:ada:6::1/128"; } | ||
77 | { Destination = "0.0.0.0/0"; | ||
78 | Gateway = "192.168.52.113"; | ||
79 | } | ||
80 | { Destination = "::/0"; | ||
81 | Gateway = "2a03:4000:52:ada:6::1"; | ||
82 | } | ||
83 | ]; | ||
84 | }; | ||
85 | }; | ||
86 | }; | ||
87 | }; | ||
88 | }; | ||
89 | } | ||
diff --git a/hosts/vidhar/kimai/ruleset.nft b/hosts/vidhar/kimai/ruleset.nft new file mode 100644 index 00000000..ad4db6d5 --- /dev/null +++ b/hosts/vidhar/kimai/ruleset.nft | |||
@@ -0,0 +1,149 @@ | |||
1 | define icmp_protos = {ipv6-icmp, icmp, igmp} | ||
2 | |||
3 | table arp filter { | ||
4 | limit lim_arp { | ||
5 | rate over 50 mbytes/second burst 50 mbytes | ||
6 | } | ||
7 | |||
8 | counter arp-rx {} | ||
9 | counter arp-tx {} | ||
10 | |||
11 | counter arp-ratelimit-rx {} | ||
12 | counter arp-ratelimit-tx {} | ||
13 | |||
14 | chain input { | ||
15 | type filter hook input priority filter | ||
16 | policy accept | ||
17 | |||
18 | limit name lim_arp counter name arp-ratelimit-rx drop | ||
19 | |||
20 | counter name arp-rx | ||
21 | } | ||
22 | |||
23 | chain output { | ||
24 | type filter hook output priority filter | ||
25 | policy accept | ||
26 | |||
27 | limit name lim_arp counter name arp-ratelimit-tx drop | ||
28 | |||
29 | counter name arp-tx | ||
30 | } | ||
31 | } | ||
32 | |||
33 | table inet filter { | ||
34 | limit lim_reject { | ||
35 | rate over 1000/second burst 1000 packets | ||
36 | } | ||
37 | |||
38 | limit lim_icmp { | ||
39 | rate over 50 mbytes/second burst 50 mbytes | ||
40 | } | ||
41 | |||
42 | counter invalid-fw {} | ||
43 | counter fw-lo {} | ||
44 | |||
45 | counter reject-ratelimit-fw {} | ||
46 | counter reject-fw {} | ||
47 | counter reject-tcp-fw {} | ||
48 | counter reject-icmp-fw {} | ||
49 | |||
50 | counter drop-fw {} | ||
51 | |||
52 | counter invalid-rx {} | ||
53 | |||
54 | counter rx-lo {} | ||
55 | counter invalid-local4-rx {} | ||
56 | counter invalid-local6-rx {} | ||
57 | |||
58 | counter icmp-ratelimit-rx {} | ||
59 | counter icmp-rx {} | ||
60 | |||
61 | counter kimai-rx {} | ||
62 | |||
63 | counter established-rx {} | ||
64 | |||
65 | counter reject-ratelimit-rx {} | ||
66 | counter reject-rx {} | ||
67 | counter reject-tcp-rx {} | ||
68 | counter reject-icmp-rx {} | ||
69 | |||
70 | counter drop-rx {} | ||
71 | |||
72 | counter tx-lo {} | ||
73 | |||
74 | counter icmp-ratelimit-tx {} | ||
75 | counter icmp-tx {} | ||
76 | |||
77 | counter kimai-tx {} | ||
78 | |||
79 | counter tx {} | ||
80 | |||
81 | chain forward { | ||
82 | type filter hook forward priority filter | ||
83 | policy drop | ||
84 | |||
85 | |||
86 | ct state invalid log level debug prefix "kimai: drop invalid forward: " counter name invalid-fw drop | ||
87 | |||
88 | |||
89 | iifname lo counter name fw-lo accept | ||
90 | |||
91 | |||
92 | limit name lim_reject log level debug prefix "kimai: drop forward: " counter name reject-ratelimit-fw drop | ||
93 | log level debug prefix "kimai: reject forward: " counter name reject-fw | ||
94 | meta l4proto tcp ct state new counter name reject-tcp-fw reject with tcp reset | ||
95 | ct state new counter name reject-icmp-fw reject | ||
96 | |||
97 | |||
98 | counter name drop-fw | ||
99 | } | ||
100 | |||
101 | chain input { | ||
102 | type filter hook input priority filter | ||
103 | policy drop | ||
104 | |||
105 | |||
106 | ct state invalid log level debug prefix "kimai: drop invalid input: " counter name invalid-rx drop | ||
107 | |||
108 | |||
109 | iifname lo counter name rx-lo accept | ||
110 | iif != lo ip daddr 127.0.0.1/8 counter name invalid-local4-rx reject | ||
111 | iif != lo ip6 daddr ::1/128 counter name invalid-local6-rx reject | ||
112 | |||
113 | |||
114 | meta l4proto $icmp_protos limit name lim_icmp counter name icmp-ratelimit-rx drop | ||
115 | meta l4proto $icmp_protos counter name icmp-rx accept | ||
116 | |||
117 | |||
118 | tcp dport 80 counter name kimai-rx accept | ||
119 | |||
120 | |||
121 | ct state { established, related } counter name established-rx accept | ||
122 | |||
123 | |||
124 | limit name lim_reject log level debug prefix "kimai: drop input: " counter name reject-ratelimit-rx drop | ||
125 | log level debug prefix "kimai: reject input: " counter name reject-rx | ||
126 | meta l4proto tcp ct state new counter name reject-tcp-rx reject with tcp reset | ||
127 | ct state new counter name reject-icmp-rx reject | ||
128 | |||
129 | |||
130 | counter name drop-rx | ||
131 | } | ||
132 | |||
133 | chain output { | ||
134 | type filter hook output priority filter | ||
135 | policy accept | ||
136 | |||
137 | |||
138 | oifname lo counter name tx-lo accept | ||
139 | |||
140 | meta l4proto $icmp_protos limit name lim_icmp counter name icmp-ratelimit-tx drop | ||
141 | meta l4proto $icmp_protos counter name icmp-tx accept | ||
142 | |||
143 | |||
144 | tcp sport 80 counter name kimai-tx | ||
145 | |||
146 | |||
147 | counter name tx | ||
148 | } | ||
149 | } | ||
diff --git a/hosts/vidhar/network/default.nix b/hosts/vidhar/network/default.nix index 0643f0bb..92d755f3 100644 --- a/hosts/vidhar/network/default.nix +++ b/hosts/vidhar/network/default.nix | |||
@@ -103,7 +103,14 @@ with lib; | |||
103 | /srv/nfs/nix-store 10.141.0.0/24(ro,async,root_squash) 2a03:4000:52:ada:1::/80(ro,async,root_squash) | 103 | /srv/nfs/nix-store 10.141.0.0/24(ro,async,root_squash) 2a03:4000:52:ada:1::/80(ro,async,root_squash) |
104 | ''; | 104 | ''; |
105 | }; | 105 | }; |
106 | settings.nfsd.vers3 = false; | 106 | settings.nfsd = { |
107 | rdma = true; | ||
108 | vers3 = false; | ||
109 | vers4 = true; | ||
110 | "vers4.0" = false; | ||
111 | "vers4.1" = false; | ||
112 | "vers4.2" = true; | ||
113 | }; | ||
107 | }; | 114 | }; |
108 | 115 | ||
109 | fileSystems = { | 116 | fileSystems = { |
diff --git a/hosts/vidhar/network/dhcp/default.nix b/hosts/vidhar/network/dhcp/default.nix index 4151111d..11460393 100644 --- a/hosts/vidhar/network/dhcp/default.nix +++ b/hosts/vidhar/network/dhcp/default.nix | |||
@@ -1,9 +1,32 @@ | |||
1 | { flake, config, pkgs, lib, ... }: | 1 | { flake, config, pkgs, lib, sources, ... }: |
2 | 2 | ||
3 | with lib; | 3 | with lib; |
4 | 4 | ||
5 | let | 5 | let |
6 | nfsrootBaseUrl = "http://nfsroot.vidhar.yggdrasil"; | 6 | nfsrootBaseUrl = "http://nfsroot.vidhar.yggdrasil"; |
7 | tftpIp = "10.141.0.1"; | ||
8 | nfsIp = tftpIp; | ||
9 | ipxe = pkgs.ipxe.override { | ||
10 | additionalTargets = { | ||
11 | "bin-i386-efi/ipxe.efi" = "i386-ipxe.efi"; | ||
12 | }; | ||
13 | additionalOptions = [ | ||
14 | "NSLOOKUP_CMD" | ||
15 | "PING_CMD" | ||
16 | "CONSOLE_CMD" | ||
17 | ]; | ||
18 | embedScript = pkgs.writeText "yggdrasil.ipxe" '' | ||
19 | #!ipxe | ||
20 | |||
21 | cpair --background 9 1 | ||
22 | cpair --background 9 3 | ||
23 | cpair --background 9 6 | ||
24 | |||
25 | set user-class iPXE-yggdrasil | ||
26 | |||
27 | autoboot | ||
28 | ''; | ||
29 | }; | ||
7 | in { | 30 | in { |
8 | config = { | 31 | config = { |
9 | services.kea = { | 32 | services.kea = { |
@@ -25,41 +48,67 @@ in { | |||
25 | }; | 48 | }; |
26 | 49 | ||
27 | client-classes = [ | 50 | client-classes = [ |
28 | { name = "eostre-ipxe"; | 51 | { name = "ipxe-eostre"; |
29 | test = "hexstring(pkt4.mac, ':') == '00:d8:61:79:c5:40' and option[77].hex == 'iPXE'"; | 52 | test = "hexstring(pkt4.mac, ':') == '00:d8:61:79:c5:40' and option[77].hex == 'iPXE-yggdrasil'"; |
30 | next-server = "10.141.0.1"; | 53 | next-server = tftpIp; |
31 | boot-file-name = "${nfsrootBaseUrl}/eostre.menu.ipxe"; | 54 | boot-file-name = "${nfsrootBaseUrl}/eostre.menu.ipxe"; |
32 | only-if-required = true; | 55 | only-if-required = true; |
33 | } | 56 | } |
34 | { name = "ipxe"; | 57 | { name = "ipxe-yggdrasil"; |
35 | test = "option[77].hex == 'iPXE'"; | 58 | test = "option[77].hex == 'iPXE-yggdrasil'"; |
36 | next-server = "10.141.0.1"; | 59 | next-server = tftpIp; |
37 | boot-file-name = "${nfsrootBaseUrl}/installer-x86_64-linux.menu.ipxe"; | 60 | boot-file-name = "${nfsrootBaseUrl}/installer-x86_64-linux.menu.ipxe"; |
38 | only-if-required = true; | 61 | only-if-required = true; |
39 | } | 62 | } |
63 | |||
64 | { name = "uefi-http"; | ||
65 | test = "option[client-system].hex == 0x0010"; | ||
66 | option-data = [ | ||
67 | { name = "vendor-class-identifier"; data = "HTTPClient"; } | ||
68 | ]; | ||
69 | boot-file-name = "${nfsrootBaseUrl}/ipxe.efi"; | ||
70 | only-if-required = true; | ||
71 | } | ||
72 | |||
73 | { name = "ipxe-uefi-64"; | ||
74 | test = "option[77].hex == 'iPXE' and (substring(option[60].hex,0,20) == 'PXEClient:Arch:00007' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00008' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00009')"; | ||
75 | boot-file-name = "${nfsrootBaseUrl}/ipxe.efi"; | ||
76 | only-if-required = true; | ||
77 | } | ||
78 | { name = "ipxe-uefi-32"; | ||
79 | test = "option[77].hex == 'iPXE' and (substring(option[60].hex,0,20) == 'PXEClient:Arch:00002' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00006')"; | ||
80 | boot-file-name = "${nfsrootBaseUrl}/i386-ipxe.efi"; | ||
81 | only-if-required = true; | ||
82 | } | ||
83 | { name = "ipxe-legacy"; | ||
84 | test = "option[77].hex == 'iPXE' and substring(option[60].hex,0,20) == 'PXEClient:Arch:00000'"; | ||
85 | boot-file-name = "${nfsrootBaseUrl}/ipxe.lkrn"; | ||
86 | only-if-required = true; | ||
87 | } | ||
88 | |||
40 | { name = "uefi-64"; | 89 | { name = "uefi-64"; |
41 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00007' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00008' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00009'"; | 90 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00007' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00008' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00009'"; |
42 | only-if-required = true; | ||
43 | option-data = [ | 91 | option-data = [ |
44 | { name = "tftp-server-name"; data = "10.141.0.1"; } | 92 | { name = "tftp-server-name"; data = tftpIp; } |
45 | ]; | 93 | ]; |
46 | boot-file-name = "ipxe.efi"; | 94 | boot-file-name = "ipxe.efi"; |
95 | only-if-required = true; | ||
47 | } | 96 | } |
48 | { name = "uefi-32"; | 97 | { name = "uefi-32"; |
49 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00002' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00006'"; | 98 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00002' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00006'"; |
50 | only-if-required = true; | ||
51 | option-data = [ | 99 | option-data = [ |
52 | { name = "tftp-server-name"; data = "10.141.0.1"; } | 100 | { name = "tftp-server-name"; data = tftpIp; } |
53 | ]; | 101 | ]; |
54 | boot-file-name = "i386-ipxe.efi"; | 102 | boot-file-name = "i386-ipxe.efi"; |
103 | only-if-required = true; | ||
55 | } | 104 | } |
56 | { name = "legacy"; | 105 | { name = "legacy"; |
57 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00000'"; | 106 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00000'"; |
58 | only-if-required = true; | ||
59 | option-data = [ | 107 | option-data = [ |
60 | { name = "tftp-server-name"; data = "10.141.0.1"; } | 108 | { name = "tftp-server-name"; data = tftpIp; } |
61 | ]; | 109 | ]; |
62 | boot-file-name = "undionly.kpxe"; | 110 | boot-file-name = "ipxe.lkrn"; |
111 | only-if-required = true; | ||
63 | } | 112 | } |
64 | ]; | 113 | ]; |
65 | 114 | ||
@@ -257,30 +306,31 @@ in { | |||
257 | pkgs.symlinkJoin { | 306 | pkgs.symlinkJoin { |
258 | name = "installer-${system}"; | 307 | name = "installer-${system}"; |
259 | paths = [ | 308 | paths = [ |
260 | (let | 309 | (builtins.addErrorContext "while evaluating installer-${system}-nfsroot" (let |
261 | installerBuild = (flake.nixosConfigurations.${"installer-${system}-nfsroot"}.extendModules { | 310 | installerBuild' = (flake.nixosConfigurations.${"installer-${system}-nfsroot"}.extendModules { |
262 | modules = [ | 311 | modules = [ |
263 | ({ ... }: { | 312 | ({ ... }: { |
264 | config.nfsroot.storeDevice = "10.141.0.1:nix-store"; | 313 | config.nfsroot.storeDevice = "${nfsIp}:nix-store"; |
265 | config.nfsroot.registrationUrl = "${nfsrootBaseUrl}/installer-${system}/registration"; | 314 | config.nfsroot.registrationUrl = "${nfsrootBaseUrl}/installer-${system}/registration"; |
315 | config.system.nixos.label = "installer-${system}"; | ||
266 | }) | 316 | }) |
267 | ]; | 317 | ]; |
268 | }).config.system.build; | 318 | }); |
269 | in builtins.toPath (pkgs.runCommandLocal "install-${system}" {} '' | 319 | installerBuild = installerBuild'.config.system.build; |
320 | in builtins.toPath (pkgs.runCommandLocal "installer-${system}" {} '' | ||
270 | mkdir -p $out/installer-${system} | 321 | mkdir -p $out/installer-${system} |
271 | install -m 0444 -t $out/installer-${system} \ | 322 | install -m 0444 -t $out/installer-${system} \ |
272 | ${installerBuild.initialRamdisk}/initrd \ | 323 | ${installerBuild.initialRamdisk}/initrd \ |
273 | ${installerBuild.kernel}/bzImage \ | 324 | ${installerBuild.kernel}/bzImage \ |
274 | ${installerBuild.netbootIpxeScript}/netboot.ipxe \ | 325 | ${installerBuild.netbootIpxeScript}/netboot.ipxe \ |
275 | ${pkgs.closureInfo { rootPaths = installerBuild.storeContents; }}/registration | 326 | ${pkgs.closureInfo { rootPaths = installerBuild.storeContents; }}/registration |
276 | '')) | 327 | install -m 0444 ${pkgs.writeText "installer-${system}.menu.ipxe" '' |
277 | (pkgs.writeTextFile { | 328 | #!ipxe |
278 | name = "installer-${system}.menu.ipxe"; | 329 | |
279 | destination = "/installer-${system}.menu.ipxe"; | ||
280 | text = '' | ||
281 | :start | 330 | :start |
282 | menu iPXE boot menu for installer-${system} | 331 | menu iPXE boot menu for installer-${system} |
283 | item installer Boot installer-${system} | 332 | item installer ${with installerBuild'; "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} (Linux ${config.boot.kernelPackages.kernel.modDirVersion})"} |
333 | item memtest memtest86plus | ||
284 | item netboot netboot.xyz | 334 | item netboot netboot.xyz |
285 | item shell iPXE shell | 335 | item shell iPXE shell |
286 | choose --timeout 0 --default installer selected || goto shell | 336 | choose --timeout 0 --default installer selected || goto shell |
@@ -291,29 +341,40 @@ in { | |||
291 | goto start | 341 | goto start |
292 | 342 | ||
293 | :installer | 343 | :installer |
294 | chain ${nfsrootBaseUrl}/installer-${system}/netboot.ipxe | 344 | chain installer-${system}/netboot.ipxe |
295 | goto start | 345 | goto start |
296 | 346 | ||
297 | :netboot | 347 | :netboot |
298 | chain --autofree ${nfsrootBaseUrl}/netboot.xyz.efi | 348 | iseq ''${platform} efi && chain --autofree netboot.xyz.efi || chain --autofree netboot.xyz.lkrn |
299 | goto start | 349 | goto start |
300 | ''; | 350 | |
301 | }) | 351 | :memtest |
352 | iseq ''${platform} efi && chain --autofree memtest.efi || chain --autofree memtest.bin | ||
353 | goto start | ||
354 | ''} $out/installer-${system}.menu.ipxe | ||
355 | ''))) | ||
302 | ]; | 356 | ]; |
303 | }) ["x86_64-linux"] | 357 | }) ["x86_64-linux"] |
304 | ) ++ [ | 358 | ) ++ [ |
305 | (pkgs.linkFarm "netbootxyz-efi" [ | 359 | (pkgs.runCommandLocal "utils" {} '' |
306 | { name = "netboot.xyz.efi"; path = pkgs.netbootxyz-efi; } | 360 | mkdir $out |
307 | ]) | 361 | install -m 0444 -t $out \ |
308 | (let | 362 | ${ipxe}/{ipxe.efi,i386-ipxe.efi,ipxe.lkrn} \ |
309 | eostreBuild = (flake.nixosConfigurations.eostre.extendModules { | 363 | ${pkgs.memtest86plus}/{memtest.efi,memtest.bin} |
364 | install -m 0444 ${sources.netbootxyz-efi.src} $out/netboot.xyz.efi | ||
365 | install -m 0444 ${sources.netbootxyz-lkrn.src} $out/netboot.xyz.lkrn | ||
366 | '') | ||
367 | (builtins.addErrorContext "while evaluating eostre" (let | ||
368 | eostreBuild' = (flake.nixosConfigurations.eostre.extendModules { | ||
310 | modules = [ | 369 | modules = [ |
311 | ({ ... }: { | 370 | ({ ... }: { |
312 | config.nfsroot.storeDevice = "10.141.0.1:nix-store"; | 371 | config.nfsroot.storeDevice = "${nfsIp}:nix-store"; |
313 | config.nfsroot.registrationUrl = "${nfsrootBaseUrl}/eostre/registration"; | 372 | config.nfsroot.registrationUrl = "${nfsrootBaseUrl}/eostre/registration"; |
373 | config.system.nixos.label = "eostre"; | ||
314 | }) | 374 | }) |
315 | ]; | 375 | ]; |
316 | }).config.system.build; | 376 | }); |
377 | eostreBuild = eostreBuild'.config.system.build; | ||
317 | in builtins.toPath (pkgs.runCommandLocal "eostre" {} '' | 378 | in builtins.toPath (pkgs.runCommandLocal "eostre" {} '' |
318 | mkdir -p $out/eostre | 379 | mkdir -p $out/eostre |
319 | install -m 0444 -t $out/eostre \ | 380 | install -m 0444 -t $out/eostre \ |
@@ -321,35 +382,39 @@ in { | |||
321 | ${eostreBuild.kernel}/bzImage \ | 382 | ${eostreBuild.kernel}/bzImage \ |
322 | ${eostreBuild.netbootIpxeScript}/netboot.ipxe \ | 383 | ${eostreBuild.netbootIpxeScript}/netboot.ipxe \ |
323 | ${pkgs.closureInfo { rootPaths = eostreBuild.storeContents; }}/registration | 384 | ${pkgs.closureInfo { rootPaths = eostreBuild.storeContents; }}/registration |
324 | '')) | 385 | install -m 0444 ${pkgs.writeText "eostre.menu.ipxe" '' |
325 | (pkgs.writeTextFile { | 386 | #!ipxe |
326 | name = "eostre.menu.ipxe"; | ||
327 | destination = "/eostre.menu.ipxe"; | ||
328 | text = '' | ||
329 | set menu-timeout 5000 | ||
330 | 387 | ||
331 | :start | 388 | set menu-timeout 5000 |
332 | menu iPXE boot menu for eostre | ||
333 | item eostre Boot eostre | ||
334 | item netboot netboot.xyz | ||
335 | item shell iPXE shell | ||
336 | choose --timeout ''${menu-timeout} --default eostre selected || goto shell | ||
337 | goto ''${selected} | ||
338 | 389 | ||
339 | :shell | 390 | :start |
340 | shell | 391 | menu iPXE boot menu for eostre |
341 | set menu-timeout 0 | 392 | item eostre ${with eostreBuild'; "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} (Linux ${config.boot.kernelPackages.kernel.modDirVersion})"} |
342 | goto start | 393 | item memtest memtest86plus |
394 | item netboot netboot.xyz | ||
395 | item shell iPXE shell | ||
396 | choose --timeout ''${menu-timeout} --default eostre selected || goto shell | ||
397 | set menu-timeout 0 | ||
398 | goto ''${selected} | ||
343 | 399 | ||
344 | :eostre | 400 | :shell |
345 | chain ${nfsrootBaseUrl}/eostre/netboot.ipxe | 401 | set menu-timeout 0 |
346 | goto start | 402 | shell |
403 | goto start | ||
404 | |||
405 | :eostre | ||
406 | chain eostre/netboot.ipxe | ||
407 | goto start | ||
408 | |||
409 | :netboot | ||
410 | iseq ''${platform} efi && chain --autofree netboot.xyz.efi || chain --autofree netboot.xyz.lkrn | ||
411 | goto start | ||
347 | 412 | ||
348 | :netboot | 413 | :memtest |
349 | chain --autofree ${nfsrootBaseUrl}/netboot.xyz.efi | 414 | iseq ''${platform} efi && chain --autofree memtest.efi || chain --autofree memtest.bin |
350 | goto start | 415 | goto start |
351 | ''; | 416 | ''} $out/eostre.menu.ipxe |
352 | }) | 417 | ''))) |
353 | ]; | 418 | ]; |
354 | }; | 419 | }; |
355 | }; | 420 | }; |
@@ -360,20 +425,12 @@ in { | |||
360 | after = [ "network.target" ]; | 425 | after = [ "network.target" ]; |
361 | wantedBy = [ "multi-user.target" ]; | 426 | wantedBy = [ "multi-user.target" ]; |
362 | serviceConfig.ExecStart = let | 427 | serviceConfig.ExecStart = let |
363 | ipxe = pkgs.ipxe.override { | ||
364 | additionalTargets = { | ||
365 | "bin-i386-efi/ipxe.efi" = "i386-ipxe.efi"; | ||
366 | }; | ||
367 | additionalOptions = [ | ||
368 | "NSLOOKUP_CMD" | ||
369 | ]; | ||
370 | }; | ||
371 | tftpRoot = pkgs.runCommandLocal "netboot" {} '' | 428 | tftpRoot = pkgs.runCommandLocal "netboot" {} '' |
372 | mkdir -p $out | 429 | mkdir -p $out |
373 | install -m 0444 -t $out \ | 430 | install -m 0444 -t $out \ |
374 | ${ipxe}/ipxe.efi ${ipxe}/i386-ipxe.efi ${ipxe}/undionly.kpxe | 431 | ${ipxe}/{ipxe.efi,i386-ipxe.efi,ipxe.lkrn} |
375 | ''; | 432 | ''; |
376 | in "${pkgs.atftp}/sbin/atftpd --daemon --no-fork --bind-address=10.141.0.1 ${tftpRoot}"; | 433 | in "${pkgs.atftp}/sbin/atftpd --daemon --no-fork --bind-address=${tftpIp} ${tftpRoot}"; |
377 | }; | 434 | }; |
378 | }; | 435 | }; |
379 | } | 436 | } |
diff --git a/hosts/vidhar/network/ruleset.nft b/hosts/vidhar/network/ruleset.nft index 1edae167..7897fb3d 100644 --- a/hosts/vidhar/network/ruleset.nft +++ b/hosts/vidhar/network/ruleset.nft | |||
@@ -60,6 +60,7 @@ table inet filter { | |||
60 | counter fw-lo {} | 60 | counter fw-lo {} |
61 | counter fw-lan {} | 61 | counter fw-lan {} |
62 | counter fw-gpon {} | 62 | counter fw-gpon {} |
63 | counter fw-kimai {} | ||
63 | 64 | ||
64 | counter fw-cups {} | 65 | counter fw-cups {} |
65 | 66 | ||
@@ -94,6 +95,8 @@ table inet filter { | |||
94 | counter immich-rx {} | 95 | counter immich-rx {} |
95 | counter paperless-rx {} | 96 | counter paperless-rx {} |
96 | counter hledger-rx {} | 97 | counter hledger-rx {} |
98 | counter audiobookshelf-rx {} | ||
99 | counter kimai-rx {} | ||
97 | 100 | ||
98 | counter established-rx {} | 101 | counter established-rx {} |
99 | 102 | ||
@@ -125,6 +128,8 @@ table inet filter { | |||
125 | counter immich-tx {} | 128 | counter immich-tx {} |
126 | counter paperless-tx {} | 129 | counter paperless-tx {} |
127 | counter hledger-tx {} | 130 | counter hledger-tx {} |
131 | counter audiobookshelf-tx {} | ||
132 | counter kimai-tx {} | ||
128 | 133 | ||
129 | counter tx {} | 134 | counter tx {} |
130 | 135 | ||
@@ -148,8 +153,13 @@ table inet filter { | |||
148 | 153 | ||
149 | oifname { lan, gpon, bifrost } meta l4proto $icmp_protos jump forward_icmp_accept | 154 | oifname { lan, gpon, bifrost } meta l4proto $icmp_protos jump forward_icmp_accept |
150 | iifname lan oifname { gpon, bifrost } counter name fw-lan accept | 155 | iifname lan oifname { gpon, bifrost } counter name fw-lan accept |
156 | iifname ve-kimai oifname gpon counter name fw-kimai accept | ||
151 | 157 | ||
152 | iifname gpon oifname lan ct state { established, related } counter name fw-gpon accept | 158 | iifname gpon oifname lan ct state { established, related } counter name fw-gpon accept |
159 | iifname gpon oifname ve-kimai ct state { established, related } counter name fw-kimai accept | ||
160 | |||
161 | iifname bifrost oifname ve-kimai tcp dport 80 ip6 saddr $bifrost_surtr ip6 daddr 2a03:4000:52:ada:6::2 counter name kimai-rx accept | ||
162 | iifname ve-kimai oifname bifrost tcp sport 80 ip6 saddr 2a03:4000:52:ada:6::2 ip6 daddr $bifrost_surtr counter name kimai-tx accept | ||
153 | 163 | ||
154 | 164 | ||
155 | limit name lim_reject log level debug prefix "drop forward: " counter name reject-ratelimit-fw drop | 165 | limit name lim_reject log level debug prefix "drop forward: " counter name reject-ratelimit-fw drop |
@@ -203,6 +213,7 @@ table inet filter { | |||
203 | iifname bifrost tcp dport 2283 ip6 saddr $bifrost_surtr counter name immich-rx accept | 213 | iifname bifrost tcp dport 2283 ip6 saddr $bifrost_surtr counter name immich-rx accept |
204 | iifname bifrost tcp dport 28981 ip6 saddr $bifrost_surtr counter name paperless-rx accept | 214 | iifname bifrost tcp dport 28981 ip6 saddr $bifrost_surtr counter name paperless-rx accept |
205 | iifname bifrost tcp dport 5000 ip6 saddr $bifrost_surtr counter name hledger-rx accept | 215 | iifname bifrost tcp dport 5000 ip6 saddr $bifrost_surtr counter name hledger-rx accept |
216 | iifname bifrost tcp dport 28982 ip6 saddr $bifrost_surtr counter name audiobookshelf-rx accept | ||
206 | 217 | ||
207 | ct state { established, related } counter name established-rx accept | 218 | ct state { established, related } counter name established-rx accept |
208 | 219 | ||
@@ -254,6 +265,7 @@ table inet filter { | |||
254 | iifname bifrost tcp sport 2283 ip6 daddr $bifrost_surtr counter name immich-tx accept | 265 | iifname bifrost tcp sport 2283 ip6 daddr $bifrost_surtr counter name immich-tx accept |
255 | iifname bifrost tcp sport 28981 ip6 daddr $bifrost_surtr counter name paperless-tx accept | 266 | iifname bifrost tcp sport 28981 ip6 daddr $bifrost_surtr counter name paperless-tx accept |
256 | iifname bifrost tcp sport 5000 ip6 daddr $bifrost_surtr counter name hledger-tx accept | 267 | iifname bifrost tcp sport 5000 ip6 daddr $bifrost_surtr counter name hledger-tx accept |
268 | iifname bifrost tcp sport 28982 ip6 daddr $bifrost_surtr counter name audiobookshelf-tx accept | ||
257 | 269 | ||
258 | 270 | ||
259 | counter name tx | 271 | counter name tx |
@@ -262,7 +274,7 @@ table inet filter { | |||
262 | 274 | ||
263 | table inet nat { | 275 | table inet nat { |
264 | counter gpon-nat {} | 276 | counter gpon-nat {} |
265 | # counter container-nat {} | 277 | counter kimai-nat {} |
266 | 278 | ||
267 | chain postrouting { | 279 | chain postrouting { |
268 | type nat hook postrouting priority srcnat | 280 | type nat hook postrouting priority srcnat |
@@ -270,7 +282,7 @@ table inet nat { | |||
270 | 282 | ||
271 | 283 | ||
272 | meta nfproto ipv4 oifname gpon counter name gpon-nat masquerade | 284 | meta nfproto ipv4 oifname gpon counter name gpon-nat masquerade |
273 | # iifname ve-* oifname gpon counter name container-nat masquerade | 285 | iifname ve-kimai oifname gpon counter name kimai-nat masquerade |
274 | } | 286 | } |
275 | } | 287 | } |
276 | 288 | ||
diff --git a/hosts/vidhar/paperless/default.nix b/hosts/vidhar/paperless/default.nix index 34cd18c4..dd02da38 100644 --- a/hosts/vidhar/paperless/default.nix +++ b/hosts/vidhar/paperless/default.nix | |||
@@ -4,7 +4,7 @@ | |||
4 | config = { | 4 | config = { |
5 | services.paperless = { | 5 | services.paperless = { |
6 | enable = true; | 6 | enable = true; |
7 | address = "[2a03:4000:52:ada:4:1::]"; | 7 | address = "2a03:4000:52:ada:4:1::"; |
8 | passwordFile = config.sops.secrets."paperless-rootpw".path; | 8 | passwordFile = config.sops.secrets."paperless-rootpw".path; |
9 | settings = { | 9 | settings = { |
10 | PAPERLESS_OCR_LANGUAGE = "deu+eng"; | 10 | PAPERLESS_OCR_LANGUAGE = "deu+eng"; |
diff --git a/hosts/vidhar/prometheus/default.nix b/hosts/vidhar/prometheus/default.nix index d368ad52..094f9f7a 100644 --- a/hosts/vidhar/prometheus/default.nix +++ b/hosts/vidhar/prometheus/default.nix | |||
@@ -26,7 +26,8 @@ in { | |||
26 | enable = true; | 26 | enable = true; |
27 | 27 | ||
28 | extraFlags = [ | 28 | extraFlags = [ |
29 | "--enable-feature=remote-write-receiver" | 29 | "--web.enable-remote-write-receiver" |
30 | "--storage.tsdb.retention.size=35GB" | ||
30 | ]; | 31 | ]; |
31 | 32 | ||
32 | exporters = { | 33 | exporters = { |
diff --git a/installer-profiles/cd-dvd.nix b/installer-profiles/cd-dvd.nix index 45291bad..ac12d885 100644 --- a/installer-profiles/cd-dvd.nix +++ b/installer-profiles/cd-dvd.nix | |||
@@ -1,7 +1,13 @@ | |||
1 | { flakeInputs, ... }: | 1 | { flakeInputs, lib, ... }: |
2 | 2 | ||
3 | { | 3 | { |
4 | imports = [ | 4 | imports = [ |
5 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix" | 5 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix" |
6 | ]; | 6 | ]; |
7 | |||
8 | config = { | ||
9 | isoImage.squashfsCompression = "zstd -Xcompression-level 9"; | ||
10 | system.installer.channel.enable = false; | ||
11 | boot.loader.grub.memtest86.enable = lib.mkForce false; | ||
12 | }; | ||
7 | } | 13 | } |
diff --git a/installer-profiles/netboot.nix b/installer-profiles/netboot.nix index 28e8084d..6e39ebfb 100644 --- a/installer-profiles/netboot.nix +++ b/installer-profiles/netboot.nix | |||
@@ -4,4 +4,9 @@ | |||
4 | imports = [ | 4 | imports = [ |
5 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/installer/netboot/netboot-minimal.nix" | 5 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/installer/netboot/netboot-minimal.nix" |
6 | ]; | 6 | ]; |
7 | |||
8 | config = { | ||
9 | netboot.squashfsCompression = "zstd -Xcompression-level 9"; | ||
10 | system.installer.channel.enable = false; | ||
11 | }; | ||
7 | } | 12 | } |
diff --git a/installer-profiles/nfsroot.nix b/installer-profiles/nfsroot.nix index 6bd875b4..a8f6def6 100644 --- a/installer-profiles/nfsroot.nix +++ b/installer-profiles/nfsroot.nix | |||
@@ -8,4 +8,6 @@ | |||
8 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/profiles/base.nix" | 8 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/profiles/base.nix" |
9 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/profiles/installation-device.nix" | 9 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/profiles/installation-device.nix" |
10 | ]; | 10 | ]; |
11 | |||
12 | config.system.installer.channel.enable = false; | ||
11 | } | 13 | } |
diff --git a/installer/default.nix b/installer/default.nix index ec47832a..26f38572 100644 --- a/installer/default.nix +++ b/installer/default.nix | |||
@@ -8,7 +8,7 @@ with lib; | |||
8 | ]; | 8 | ]; |
9 | 9 | ||
10 | config = { | 10 | config = { |
11 | boot.initrd.availableKernelModules = [ "e1000e" ]; | 11 | boot.initrd.kernelModules = [ "e1000e" "virtio_net" ]; |
12 | 12 | ||
13 | hardware.cpu.amd.updateMicrocode = config.hardware.enableRedistributableFirmware; | 13 | hardware.cpu.amd.updateMicrocode = config.hardware.enableRedistributableFirmware; |
14 | 14 | ||
@@ -47,7 +47,7 @@ with lib; | |||
47 | services.xserver.videoDrivers = [ "nvidia" ]; | 47 | services.xserver.videoDrivers = [ "nvidia" ]; |
48 | systemd.services.nvidia-control-devices = { | 48 | systemd.services.nvidia-control-devices = { |
49 | wantedBy = [ "multi-user.target" ]; | 49 | wantedBy = [ "multi-user.target" ]; |
50 | serviceConfig.ExecStart = "${pkgs.linuxPackages.nvidia_x11.bin}/bin/nvidia-smi"; | 50 | serviceConfig.ExecStart = lib.getExe' pkgs.linuxPackages.nvidia_x11.bin "nvidia-smi"; |
51 | }; | 51 | }; |
52 | nixpkgs.externalConfig.allowUnfree = true; | 52 | nixpkgs.externalConfig.allowUnfree = true; |
53 | 53 | ||
diff --git a/lib/pythonSet.nix b/lib/pythonSet.nix new file mode 100644 index 00000000..9dfb25ff --- /dev/null +++ b/lib/pythonSet.nix | |||
@@ -0,0 +1,28 @@ | |||
1 | { uv2nix, pyproject-nix, pyproject-build-systems, ... }: | ||
2 | { pkgs, python, overlay, lib ? pkgs.lib }: | ||
3 | (pkgs.callPackage pyproject-nix.build.packages { | ||
4 | inherit python; | ||
5 | }).overrideScope | ||
6 | ( | ||
7 | lib.composeManyExtensions [ | ||
8 | pyproject-build-systems.overlays.default | ||
9 | overlay | ||
10 | (final: prev: { | ||
11 | sdnotify = (prev.sdnotify.override { | ||
12 | sourcePreference = "sdist"; | ||
13 | }).overrideAttrs (oldAttrs: { | ||
14 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ [ | ||
15 | (final.resolveBuildSystem { setuptools = []; }) | ||
16 | ]; | ||
17 | }); | ||
18 | systemd-python = (prev.systemd-python.override { | ||
19 | sourcePreference = "sdist"; | ||
20 | }).overrideAttrs (oldAttrs: { | ||
21 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ [ | ||
22 | pkgs.pkg-config pkgs.systemd.dev | ||
23 | (final.resolveBuildSystem { setuptools = []; }) | ||
24 | ]; | ||
25 | }); | ||
26 | }) | ||
27 | ] | ||
28 | ) | ||
diff --git a/modules/abs-podcast-autoplaylist.nix b/modules/abs-podcast-autoplaylist.nix new file mode 100644 index 00000000..f526a434 --- /dev/null +++ b/modules/abs-podcast-autoplaylist.nix | |||
@@ -0,0 +1,55 @@ | |||
1 | { config, pkgs, lib, utils, ... }: | ||
2 | |||
3 | let | ||
4 | cfg = config.services.abs-podcast-autoplaylist; | ||
5 | |||
6 | enabledAttrs = lib.filterAttrs (_name: { enable, ... }: enable) cfg; | ||
7 | in { | ||
8 | options = { | ||
9 | services.abs-podcast-autoplaylist = lib.mkOption { | ||
10 | default = {}; | ||
11 | type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: { | ||
12 | options = { | ||
13 | enable = lib.mkEnableOption "this instance of abs-podcast-autoplaylist" // { | ||
14 | default = true; | ||
15 | }; | ||
16 | cron = lib.mkOption { | ||
17 | type = lib.types.str; | ||
18 | default = "*-*-* *:00/30:00"; | ||
19 | }; | ||
20 | configSecret = lib.mkOption { | ||
21 | type = lib.types.str; | ||
22 | default = "abs-podcast-autoplaylist-${name}.toml"; | ||
23 | }; | ||
24 | }; | ||
25 | })); | ||
26 | }; | ||
27 | }; | ||
28 | |||
29 | config = lib.mkIf (enabledAttrs != {}) { | ||
30 | systemd.services = { | ||
31 | "abs-podcast-autoplaylist@" = { | ||
32 | serviceConfig = { | ||
33 | WorkingDirectory = "%d"; | ||
34 | DynamicUser = true; | ||
35 | ProtectHome = true; | ||
36 | PrivateTmp = true; | ||
37 | PrivateDevices = true; | ||
38 | Type = "oneshot"; | ||
39 | ExecStart = "${lib.getExe pkgs.abs-podcast-autoplaylist} %I.toml"; | ||
40 | TimeoutSec = "5min"; | ||
41 | }; | ||
42 | }; | ||
43 | } // lib.mapAttrs' (name: { configSecret, ... }: lib.nameValuePair "abs-podcast-autoplaylist@${utils.escapeSystemdPath name}" { | ||
44 | overrideStrategy = "asDropin"; | ||
45 | serviceConfig = { | ||
46 | LoadCredential = "${name}.toml:${config.sops.secrets.${configSecret}.path}"; | ||
47 | }; | ||
48 | }) enabledAttrs; | ||
49 | |||
50 | systemd.timers = lib.mapAttrs' (name: { cron, ... }: lib.nameValuePair "abs-podcast-autoplaylist@${utils.escapeSystemdPath name}" { | ||
51 | wantedBy = [ "timers.target" ]; | ||
52 | timerConfig.OnCalendar = cron; | ||
53 | }) enabledAttrs; | ||
54 | }; | ||
55 | } | ||
diff --git a/modules/i18n.nix b/modules/i18n.nix new file mode 100644 index 00000000..f84e8b64 --- /dev/null +++ b/modules/i18n.nix | |||
@@ -0,0 +1,156 @@ | |||
1 | { | ||
2 | config, | ||
3 | lib, | ||
4 | pkgs, | ||
5 | ... | ||
6 | }: | ||
7 | let | ||
8 | aggregatedLocales = | ||
9 | (builtins.map | ||
10 | (l: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8") | ||
11 | ( | ||
12 | [ config.i18n.defaultLocale ] | ||
13 | ++ (lib.optionals (builtins.isList config.i18n.extraLocales) config.i18n.extraLocales) | ||
14 | ++ (lib.attrValues (lib.filterAttrs (n: _v: lib.hasPrefix "LC_" n) config.i18n.extraLocaleSettings)) | ||
15 | ) | ||
16 | ) | ||
17 | ++ (lib.optional (builtins.isString config.i18n.extraLocales) config.i18n.extraLocales); | ||
18 | in | ||
19 | { | ||
20 | disabledModules = [ "config/i18n.nix" ]; | ||
21 | |||
22 | ###### interface | ||
23 | |||
24 | options = { | ||
25 | |||
26 | i18n = { | ||
27 | glibcLocales = lib.mkOption { | ||
28 | type = lib.types.path; | ||
29 | default = pkgs.glibcLocales.override { | ||
30 | allLocales = lib.any (x: x == "all") config.i18n.supportedLocales; | ||
31 | locales = config.i18n.supportedLocales; | ||
32 | }; | ||
33 | defaultText = lib.literalExpression '' | ||
34 | pkgs.glibcLocales.override { | ||
35 | allLocales = lib.any (x: x == "all") config.i18n.supportedLocales; | ||
36 | locales = config.i18n.supportedLocales; | ||
37 | } | ||
38 | ''; | ||
39 | example = lib.literalExpression "pkgs.glibcLocales"; | ||
40 | description = '' | ||
41 | Customized pkg.glibcLocales package. | ||
42 | |||
43 | Changing this option can disable handling of i18n.defaultLocale | ||
44 | and supportedLocale. | ||
45 | ''; | ||
46 | }; | ||
47 | |||
48 | defaultLocale = lib.mkOption { | ||
49 | type = lib.types.str; | ||
50 | default = "en_US.UTF-8"; | ||
51 | example = "nl_NL.UTF-8"; | ||
52 | description = '' | ||
53 | The default locale. It determines the language for program | ||
54 | messages, the format for dates and times, sort order, and so on. | ||
55 | It also determines the character set, such as UTF-8. | ||
56 | ''; | ||
57 | }; | ||
58 | |||
59 | extraLocales = lib.mkOption { | ||
60 | type = lib.types.either (lib.types.listOf lib.types.str) (lib.types.enum [ "all" ]); | ||
61 | default = [ ]; | ||
62 | example = [ "nl_NL.UTF-8" ]; | ||
63 | description = '' | ||
64 | Additional locales that the system should support, besides the ones | ||
65 | configured with {option}`i18n.defaultLocale` and | ||
66 | {option}`i18n.extraLocaleSettings`. | ||
67 | Set this to `"all"` to install all available locales. | ||
68 | ''; | ||
69 | }; | ||
70 | |||
71 | extraLocaleSettings = lib.mkOption { | ||
72 | type = lib.types.attrsOf lib.types.str; | ||
73 | default = { }; | ||
74 | example = { | ||
75 | LC_MESSAGES = "en_US.UTF-8"; | ||
76 | LC_TIME = "de_DE.UTF-8"; | ||
77 | }; | ||
78 | description = '' | ||
79 | A set of additional system-wide locale settings other than | ||
80 | `LANG` which can be configured with | ||
81 | {option}`i18n.defaultLocale`. | ||
82 | ''; | ||
83 | }; | ||
84 | |||
85 | supportedLocales = lib.mkOption { | ||
86 | type = lib.types.listOf lib.types.str; | ||
87 | visible = false; | ||
88 | default = lib.unique ( | ||
89 | [ | ||
90 | "C.UTF-8/UTF-8" | ||
91 | "en_US.UTF-8/UTF-8" | ||
92 | ] | ||
93 | ++ aggregatedLocales | ||
94 | ); | ||
95 | example = [ | ||
96 | "en_US.UTF-8/UTF-8" | ||
97 | "nl_NL.UTF-8/UTF-8" | ||
98 | "nl_NL/ISO-8859-1" | ||
99 | ]; | ||
100 | description = '' | ||
101 | List of locales that the system should support. The value | ||
102 | `"all"` means that all locales supported by | ||
103 | Glibc will be installed. A full list of supported locales | ||
104 | can be found at <https://sourceware.org/git/?p=glibc.git;a=blob;f=localedata/SUPPORTED>. | ||
105 | ''; | ||
106 | }; | ||
107 | |||
108 | }; | ||
109 | |||
110 | }; | ||
111 | |||
112 | ###### implementation | ||
113 | |||
114 | config = { | ||
115 | warnings = | ||
116 | lib.optional | ||
117 | ( | ||
118 | !( | ||
119 | (lib.subtractLists config.i18n.supportedLocales aggregatedLocales) == [ ] | ||
120 | || lib.any (x: x == "all") config.i18n.supportedLocales | ||
121 | ) | ||
122 | ) | ||
123 | '' | ||
124 | `i18n.supportedLocales` is deprecated in favor of `i18n.extraLocales`, | ||
125 | and it seems you are using `i18n.supportedLocales` and forgot to | ||
126 | include some locales specified in `i18n.defaultLocale`, | ||
127 | `i18n.extraLocales` or `i18n.extraLocaleSettings`. | ||
128 | |||
129 | If you're trying to install additional locales not specified in | ||
130 | `i18n.defaultLocale` or `i18n.extraLocaleSettings`, consider adding | ||
131 | only those locales to `i18n.extraLocales`. | ||
132 | ''; | ||
133 | |||
134 | environment.systemPackages = | ||
135 | # We increase the priority a little, so that plain glibc in systemPackages can't win. | ||
136 | lib.optional (config.i18n.supportedLocales != [ ]) (lib.setPrio (-1) config.i18n.glibcLocales); | ||
137 | |||
138 | environment.sessionVariables = { | ||
139 | LANG = config.i18n.defaultLocale; | ||
140 | LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive"; | ||
141 | } // config.i18n.extraLocaleSettings; | ||
142 | |||
143 | systemd.globalEnvironment = lib.mkIf (config.i18n.supportedLocales != [ ]) { | ||
144 | LOCALE_ARCHIVE = "${config.i18n.glibcLocales}/lib/locale/locale-archive"; | ||
145 | }; | ||
146 | |||
147 | # ‘/etc/locale.conf’ is used by systemd. | ||
148 | environment.etc."locale.conf".source = pkgs.writeText "locale.conf" '' | ||
149 | LANG=${config.i18n.defaultLocale} | ||
150 | ${lib.concatStringsSep "\n" ( | ||
151 | lib.mapAttrsToList (n: v: "${n}=${v}") config.i18n.extraLocaleSettings | ||
152 | )} | ||
153 | ''; | ||
154 | |||
155 | }; | ||
156 | } | ||
diff --git a/modules/installer.nix b/modules/installer.nix new file mode 100644 index 00000000..3e5c6d5b --- /dev/null +++ b/modules/installer.nix | |||
@@ -0,0 +1,56 @@ | |||
1 | { flake, config, lib, pkgs, ... }: | ||
2 | |||
3 | let | ||
4 | cfg = config.installer.links; | ||
5 | |||
6 | installerOutPath = { | ||
7 | "cd-dvd" = _: installerBuild: "${installerBuild.config.system.build.isoImage}/iso"; | ||
8 | "netboot" = {system, variant}: installerBuild: pkgs.runCommandLocal "${system}-${variant}" {} '' | ||
9 | mkdir $out | ||
10 | install -m 0444 -t $out \ | ||
11 | ${installerBuild.config.system.build.netbootRamdisk}/initrd \ | ||
12 | ${installerBuild.config.system.build.kernel}/${config.system.boot.loader.kernelFile} \ | ||
13 | ${installerBuild.config.system.build.netbootIpxeScript}/netboot.ipxe \ | ||
14 | ${pkgs.ipxe.override { | ||
15 | additionalTargets = { | ||
16 | "bin-i386-efi/ipxe.efi" = "i386-ipxe.efi"; | ||
17 | }; | ||
18 | additionalOptions = [ | ||
19 | "NSLOOKUP_CMD" | ||
20 | "PING_CMD" | ||
21 | "CONSOLE_CMD" | ||
22 | ]; | ||
23 | embedScript = pkgs.writeText "netboot.ipxe" '' | ||
24 | #!ipxe | ||
25 | |||
26 | chain netboot.ipxe | ||
27 | ''; | ||
28 | }}/{ipxe.efi,i386-ipxe.efi,ipxe.lkrn} | ||
29 | ''; | ||
30 | }; | ||
31 | in { | ||
32 | options = { | ||
33 | installer.links = lib.mkOption { | ||
34 | type = lib.types.listOf (lib.types.submodule { | ||
35 | options = { | ||
36 | system = lib.mkOption { | ||
37 | type = lib.types.str; | ||
38 | }; | ||
39 | variant = lib.mkOption { | ||
40 | type = lib.types.str; | ||
41 | }; | ||
42 | }; | ||
43 | }); | ||
44 | default = []; | ||
45 | }; | ||
46 | }; | ||
47 | |||
48 | config = lib.mkIf (cfg != []) { | ||
49 | systemd.tmpfiles.rules = map (installer'@{system, variant}: | ||
50 | let | ||
51 | installer = "${system}-${variant}"; | ||
52 | installerBuild = builtins.addErrorContext "while evaluating installer-${installer}" flake.nixosConfigurations.${"installer-${installer}"}; | ||
53 | in "L+ /run/installer-${installer} - - - - ${installerOutPath.${variant} installer' installerBuild}" | ||
54 | ) cfg; | ||
55 | }; | ||
56 | } | ||
diff --git a/modules/pgbackrest.nix b/modules/pgbackrest.nix index 81c74a8e..550e970b 100644 --- a/modules/pgbackrest.nix +++ b/modules/pgbackrest.nix | |||
@@ -43,6 +43,8 @@ let | |||
43 | loglevelType = types.enum ["off" "error" "warn" "info" "detail" "debug" "trace"]; | 43 | loglevelType = types.enum ["off" "error" "warn" "info" "detail" "debug" "trace"]; |
44 | inherit (utils.systemdUtils.unitOptions) unitOption; | 44 | inherit (utils.systemdUtils.unitOptions) unitOption; |
45 | in { | 45 | in { |
46 | disabledModules = ["services/backup/pgbackrest.nix"]; | ||
47 | |||
46 | options = { | 48 | options = { |
47 | services.pgbackrest = { | 49 | services.pgbackrest = { |
48 | enable = mkEnableOption "pgBackRest"; | 50 | enable = mkEnableOption "pgBackRest"; |
diff --git a/modules/postsrsd.nix b/modules/postsrsd.nix new file mode 100644 index 00000000..205e669d --- /dev/null +++ b/modules/postsrsd.nix | |||
@@ -0,0 +1,157 @@ | |||
1 | { | ||
2 | config, | ||
3 | lib, | ||
4 | pkgs, | ||
5 | ... | ||
6 | }: | ||
7 | let | ||
8 | |||
9 | cfg = config.services.postsrsd; | ||
10 | runtimeDirectoryName = "postsrsd"; | ||
11 | runtimeDirectory = "/run/${runtimeDirectoryName}"; | ||
12 | # TODO: follow RFC 42, but we need a libconfuse format first: | ||
13 | # https://github.com/NixOS/nixpkgs/issues/401565 | ||
14 | # Arrays in `libconfuse` look like this: {"Life", "Universe", "Everything"} | ||
15 | # See https://www.nongnu.org/confuse/tutorial-html/ar01s03.html. | ||
16 | # | ||
17 | # Note: We're using `builtins.toJSON` to escape strings, but JSON strings | ||
18 | # don't have exactly the same semantics as libconfuse strings. For example, | ||
19 | # "${F}" gets treated as an env var reference, see above issue for details. | ||
20 | libconfuseDomains = "{ " + lib.concatMapStringsSep ", " builtins.toJSON cfg.domains + " }"; | ||
21 | configFile = pkgs.writeText "postsrsd.conf" '' | ||
22 | secrets-file = "''${CREDENTIALS_DIRECTORY}/secrets-file" | ||
23 | domains = ${libconfuseDomains} | ||
24 | separator = "${cfg.separator}" | ||
25 | |||
26 | # Disable postsrsd's jailing in favor of confinement with systemd. | ||
27 | unprivileged-user = "" | ||
28 | chroot-dir = "" | ||
29 | |||
30 | ${cfg.extraConfig} | ||
31 | ''; | ||
32 | |||
33 | in | ||
34 | { | ||
35 | imports = | ||
36 | map | ||
37 | ( | ||
38 | name: | ||
39 | lib.mkRemovedOptionModule [ "services" "postsrsd" name ] '' | ||
40 | `postsrsd` was upgraded to `>= 2.0.0`, with some different behaviors and configuration settings: | ||
41 | - NixOS Release Notes: https://nixos.org/manual/nixos/unstable/release-notes#sec-nixpkgs-release-25.05-incompatibilities | ||
42 | - NixOS Options Reference: https://nixos.org/manual/nixos/unstable/options#opt-services.postsrsd.enable | ||
43 | - Migration instructions: https://github.com/roehling/postsrsd/blob/2.0.10/README.rst#migrating-from-version-1x | ||
44 | - Postfix Setup: https://github.com/roehling/postsrsd/blob/2.0.10/README.rst#postfix-setup | ||
45 | '' | ||
46 | ) | ||
47 | [ | ||
48 | "domain" | ||
49 | "forwardPort" | ||
50 | "reversePort" | ||
51 | "timeout" | ||
52 | "excludeDomains" | ||
53 | ]; | ||
54 | |||
55 | disabledModules = [ "services/mail/postsrsd.nix" ]; | ||
56 | |||
57 | options = { | ||
58 | services.postsrsd = { | ||
59 | enable = lib.mkOption { | ||
60 | type = lib.types.bool; | ||
61 | default = false; | ||
62 | description = "Whether to enable the postsrsd SRS server for Postfix."; | ||
63 | }; | ||
64 | |||
65 | secretsFile = lib.mkOption { | ||
66 | type = lib.types.path; | ||
67 | default = "/var/lib/postsrsd/postsrsd.secret"; | ||
68 | description = "Secret keys used for signing and verification"; | ||
69 | }; | ||
70 | |||
71 | domains = lib.mkOption { | ||
72 | type = lib.types.listOf lib.types.str; | ||
73 | description = "Domain names for rewrite"; | ||
74 | default = [ config.networking.hostName ]; | ||
75 | defaultText = lib.literalExpression "[ config.networking.hostName ]"; | ||
76 | }; | ||
77 | |||
78 | separator = lib.mkOption { | ||
79 | type = lib.types.enum [ | ||
80 | "-" | ||
81 | "=" | ||
82 | "+" | ||
83 | ]; | ||
84 | default = "="; | ||
85 | description = "First separator character in generated addresses"; | ||
86 | }; | ||
87 | |||
88 | user = lib.mkOption { | ||
89 | type = lib.types.str; | ||
90 | default = "postsrsd"; | ||
91 | description = "User for the daemon"; | ||
92 | }; | ||
93 | |||
94 | group = lib.mkOption { | ||
95 | type = lib.types.str; | ||
96 | default = "postsrsd"; | ||
97 | description = "Group for the daemon"; | ||
98 | }; | ||
99 | |||
100 | extraConfig = lib.mkOption { | ||
101 | type = lib.types.lines; | ||
102 | default = ""; | ||
103 | }; | ||
104 | }; | ||
105 | }; | ||
106 | |||
107 | config = lib.mkIf cfg.enable { | ||
108 | users.users = lib.optionalAttrs (cfg.user == "postsrsd") { | ||
109 | postsrsd = { | ||
110 | group = cfg.group; | ||
111 | uid = config.ids.uids.postsrsd; | ||
112 | }; | ||
113 | }; | ||
114 | |||
115 | users.groups = lib.optionalAttrs (cfg.group == "postsrsd") { | ||
116 | postsrsd.gid = config.ids.gids.postsrsd; | ||
117 | }; | ||
118 | |||
119 | systemd.services.postsrsd-generate-secrets = { | ||
120 | path = [ pkgs.coreutils ]; | ||
121 | script = '' | ||
122 | if [ -e "${cfg.secretsFile}" ]; then | ||
123 | echo "Secrets file exists. Nothing to do!" | ||
124 | else | ||
125 | echo "WARNING: secrets file not found, autogenerating!" | ||
126 | DIR="$(dirname "${cfg.secretsFile}")" | ||
127 | install -m 750 -o ${cfg.user} -g ${cfg.group} -d "$DIR" | ||
128 | install -m 600 -o ${cfg.user} -g ${cfg.group} <(dd if=/dev/random bs=18 count=1 | base64) "${cfg.secretsFile}" | ||
129 | fi | ||
130 | ''; | ||
131 | serviceConfig = { | ||
132 | Type = "oneshot"; | ||
133 | }; | ||
134 | }; | ||
135 | |||
136 | systemd.services.postsrsd = { | ||
137 | description = "PostSRSd SRS rewriting server"; | ||
138 | after = [ | ||
139 | "network.target" | ||
140 | "postsrsd-generate-secrets.service" | ||
141 | ]; | ||
142 | before = [ "postfix.service" ]; | ||
143 | wantedBy = [ "multi-user.target" ]; | ||
144 | requires = [ "postsrsd-generate-secrets.service" ]; | ||
145 | confinement.enable = true; | ||
146 | |||
147 | serviceConfig = { | ||
148 | ExecStart = "${lib.getExe pkgs.postsrsd} -C ${configFile}"; | ||
149 | User = cfg.user; | ||
150 | Group = cfg.group; | ||
151 | PermissionsStartOnly = true; | ||
152 | RuntimeDirectory = runtimeDirectoryName; | ||
153 | LoadCredential = "secrets-file:${cfg.secretsFile}"; | ||
154 | }; | ||
155 | }; | ||
156 | }; | ||
157 | } | ||
diff --git a/modules/systemd-run0.nix b/modules/systemd-run0.nix new file mode 100644 index 00000000..8575ec7c --- /dev/null +++ b/modules/systemd-run0.nix | |||
@@ -0,0 +1,4 @@ | |||
1 | { config, lib, ... }: | ||
2 | { | ||
3 | config.security.pam.services.systemd-run0 = lib.mkIf (lib.versionAtLeast config.systemd.package.version "256") {}; | ||
4 | } | ||
diff --git a/modules/uucp.nix b/modules/uucp.nix deleted file mode 100644 index 10f7297b..00000000 --- a/modules/uucp.nix +++ /dev/null | |||
@@ -1,373 +0,0 @@ | |||
1 | { flake, config, lib, pkgs, ... }: | ||
2 | |||
3 | with lib; | ||
4 | |||
5 | let | ||
6 | portSpec = name: node: concatStringsSep "\n" (map (port: '' | ||
7 | port ${name}.${port} | ||
8 | type pipe | ||
9 | protocol ${node.protocols} | ||
10 | reliable true | ||
11 | command ${pkgs.openssh}/bin/ssh -x -o batchmode=yes ${name}.${port} | ||
12 | '') node.hostnames); | ||
13 | sysSpec = name: node: '' | ||
14 | system ${name} | ||
15 | time any | ||
16 | chat-seven-bit false | ||
17 | chat . "" | ||
18 | protocol ${node.protocols} | ||
19 | command-path ${concatStringsSep " " cfg.commandPath} | ||
20 | commands ${concatStringsSep " " node.commands} | ||
21 | ${concatStringsSep "\nalternate\n" (map (port: '' | ||
22 | port ${name}.${port} | ||
23 | '') node.hostnames)} | ||
24 | ''; | ||
25 | sshConfig = name: node: concatStringsSep "\n" (map (port: '' | ||
26 | Host ${name}.${port} | ||
27 | Hostname ${port} | ||
28 | IdentitiesOnly Yes | ||
29 | IdentityFile ${cfg.sshKeyDir}/${name} | ||
30 | '') node.hostnames); | ||
31 | sshKeyGen = name: node: '' | ||
32 | if [[ ! -e ${cfg.sshKeyDir}/${name} ]]; then | ||
33 | ${pkgs.openssh}/bin/ssh-keygen ${escapeShellArgs node.generateKey} -f ${cfg.sshKeyDir}/${name} | ||
34 | fi | ||
35 | ''; | ||
36 | restrictKey = key: '' | ||
37 | restrict,command="${chat}" ${key} | ||
38 | ''; | ||
39 | chat = pkgs.writeScript "chat" '' | ||
40 | #!${pkgs.stdenv.shell} | ||
41 | |||
42 | echo . | ||
43 | exec ${config.security.wrapperDir}/uucico | ||
44 | ''; | ||
45 | |||
46 | nodeCfg = { | ||
47 | options = { | ||
48 | commands = mkOption { | ||
49 | type = types.listOf types.str; | ||
50 | default = cfg.defaultCommands; | ||
51 | defaultText = literalExpression "config.services.uucp.defaultCommands"; | ||
52 | description = "Commands to allow for this remote"; | ||
53 | }; | ||
54 | |||
55 | protocols = mkOption { | ||
56 | type = types.separatedString ""; | ||
57 | default = cfg.defaultProtocols; | ||
58 | defaultText = literalExpression "config.services.uucp.defaultProtocols"; | ||
59 | description = "UUCP protocols to use for this remote"; | ||
60 | }; | ||
61 | |||
62 | publicKeys = mkOption { | ||
63 | type = types.listOf types.str; | ||
64 | default = []; | ||
65 | description = "SSH client public keys for this node"; | ||
66 | }; | ||
67 | |||
68 | generateKey = mkOption { | ||
69 | type = types.listOf types.str; | ||
70 | default = [ "-t" "ed25519" "-N" "" ]; | ||
71 | description = "Arguments to pass to `ssh-keygen` to generate a keypair for communication with this host"; | ||
72 | }; | ||
73 | |||
74 | hostnames = mkOption { | ||
75 | type = types.listOf types.str; | ||
76 | default = []; | ||
77 | description = "Hostnames to try in order when connecting"; | ||
78 | }; | ||
79 | }; | ||
80 | }; | ||
81 | |||
82 | cfg = config.services.uucp; | ||
83 | in { | ||
84 | options = { | ||
85 | services.uucp = { | ||
86 | enable = mkOption { | ||
87 | type = types.bool; | ||
88 | default = false; | ||
89 | description = '' | ||
90 | If enabled we set up an account accesible via uucp over ssh | ||
91 | ''; | ||
92 | }; | ||
93 | |||
94 | nodeName = mkOption { | ||
95 | type = types.str; | ||
96 | default = "nixos"; | ||
97 | description = "uucp node name"; | ||
98 | }; | ||
99 | |||
100 | sshUser = mkOption { | ||
101 | type = types.attrs; | ||
102 | default = {}; | ||
103 | description = "Overrides for the local uucp linux-user"; | ||
104 | }; | ||
105 | |||
106 | extraSSHConfig = mkOption { | ||
107 | type = types.str; | ||
108 | default = ""; | ||
109 | description = "Extra SSH config"; | ||
110 | }; | ||
111 | |||
112 | remoteNodes = mkOption { | ||
113 | type = types.attrsOf (types.submodule nodeCfg); | ||
114 | default = {}; | ||
115 | description = '' | ||
116 | Ports to set up | ||
117 | Names will probably need to be configured in sshConfig | ||
118 | ''; | ||
119 | }; | ||
120 | |||
121 | commandPath = mkOption { | ||
122 | type = types.listOf types.path; | ||
123 | default = [ "${pkgs.rmail}/bin" ]; | ||
124 | defaultText = literalExpression ''[ "''${pkgs.rmail}/bin" ]''; | ||
125 | description = '' | ||
126 | Command search path for all systems | ||
127 | ''; | ||
128 | }; | ||
129 | |||
130 | defaultCommands = mkOption { | ||
131 | type = types.listOf types.str; | ||
132 | default = ["rmail"]; | ||
133 | description = "Commands allowed for remotes without explicit override"; | ||
134 | }; | ||
135 | |||
136 | defaultProtocols = mkOption { | ||
137 | type = types.separatedString ""; | ||
138 | default = "te"; | ||
139 | description = "UUCP protocol to use within ssh unless overriden"; | ||
140 | }; | ||
141 | |||
142 | incomingProtocols = mkOption { | ||
143 | type = types.separatedString ""; | ||
144 | default = "te"; | ||
145 | description = "UUCP protocols to use when called"; | ||
146 | }; | ||
147 | |||
148 | homeDir = mkOption { | ||
149 | type = types.path; | ||
150 | default = "/var/uucp"; | ||
151 | description = "Home of the uucp user"; | ||
152 | }; | ||
153 | |||
154 | sshKeyDir = mkOption { | ||
155 | type = types.path; | ||
156 | default = "${cfg.homeDir}/.ssh/"; | ||
157 | defaultText = literalExpression ''''${config.services.uucp.homeDir}/.ssh/''; | ||
158 | description = "Directory to store ssh keypairs"; | ||
159 | }; | ||
160 | |||
161 | spoolDir = mkOption { | ||
162 | type = types.path; | ||
163 | default = "/var/spool/uucp"; | ||
164 | description = "Spool directory"; | ||
165 | }; | ||
166 | |||
167 | lockDir = mkOption { | ||
168 | type = types.path; | ||
169 | default = "/var/spool/uucp"; | ||
170 | description = "Lock directory"; | ||
171 | }; | ||
172 | |||
173 | pubDir = mkOption { | ||
174 | type = types.path; | ||
175 | default = "/var/spool/uucppublic"; | ||
176 | description = "Public directory"; | ||
177 | }; | ||
178 | |||
179 | logFile = mkOption { | ||
180 | type = types.path; | ||
181 | default = "/var/log/uucp"; | ||
182 | description = "Log file"; | ||
183 | }; | ||
184 | |||
185 | statFile = mkOption { | ||
186 | type = types.path; | ||
187 | default = "/var/log/uucp.stat"; | ||
188 | description = "Statistics file"; | ||
189 | }; | ||
190 | |||
191 | debugFile = mkOption { | ||
192 | type = types.path; | ||
193 | default = "/var/log/uucp.debug"; | ||
194 | description = "Debug file"; | ||
195 | }; | ||
196 | |||
197 | interval = mkOption { | ||
198 | type = types.nullOr types.str; | ||
199 | default = "1h"; | ||
200 | description = '' | ||
201 | Specification of when to run `uucico' in format used by systemd timers | ||
202 | The default is to do so every hour | ||
203 | ''; | ||
204 | }; | ||
205 | |||
206 | nmDispatch = mkOption { | ||
207 | type = types.bool; | ||
208 | default = config.networking.networkmanager.enable; | ||
209 | defaultText = literalExpression "config.networking.networkmanager.enable"; | ||
210 | description = '' | ||
211 | Install a network-manager dispatcher script to automatically | ||
212 | call all remotes when networking is available | ||
213 | ''; | ||
214 | }; | ||
215 | |||
216 | extraConfig = mkOption { | ||
217 | type = types.lines; | ||
218 | default = '' | ||
219 | run-uuxqt 1 | ||
220 | ''; | ||
221 | description = "Extra configuration to append verbatim to `/etc/uucp/config'"; | ||
222 | }; | ||
223 | |||
224 | extraSys = mkOption { | ||
225 | type = types.lines; | ||
226 | default = '' | ||
227 | protocol-parameter g packet-size 4096 | ||
228 | ''; | ||
229 | description = "Extra configuration to prepend verbatim to `/etc/uucp/sys`"; | ||
230 | }; | ||
231 | }; | ||
232 | }; | ||
233 | |||
234 | config = mkIf cfg.enable { | ||
235 | environment.etc."uucp/config" = { | ||
236 | text = '' | ||
237 | hostname ${cfg.nodeName} | ||
238 | |||
239 | spool ${cfg.spoolDir} | ||
240 | lockdir ${cfg.lockDir} | ||
241 | pubdir ${cfg.pubDir} | ||
242 | logfile ${cfg.logFile} | ||
243 | statfile ${cfg.statFile} | ||
244 | debugfile ${cfg.debugFile} | ||
245 | |||
246 | ${cfg.extraConfig} | ||
247 | ''; | ||
248 | }; | ||
249 | |||
250 | users.groups."uucp" = {}; | ||
251 | users.users."uucp" = { | ||
252 | name = "uucp"; | ||
253 | group = "uucp"; | ||
254 | isSystemUser = true; | ||
255 | isNormalUser = false; | ||
256 | createHome = true; | ||
257 | home = cfg.homeDir; | ||
258 | description = "User for uucp over ssh"; | ||
259 | useDefaultShell = true; | ||
260 | openssh.authorizedKeys.keys = map restrictKey (concatLists (mapAttrsToList (name: node: node.publicKeys) cfg.remoteNodes)); | ||
261 | } // cfg.sshUser; | ||
262 | |||
263 | system.activationScripts."uucp-sshconfig" = '' | ||
264 | mkdir -p ${config.users.users."uucp".home}/.ssh | ||
265 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${config.users.users."uucp".home}/.ssh | ||
266 | chmod 700 ${config.users.users."uucp".home}/.ssh | ||
267 | ln -fs ${builtins.toFile "ssh-config" '' | ||
268 | ${concatStringsSep "\n" (mapAttrsToList sshConfig cfg.remoteNodes)} | ||
269 | |||
270 | ${cfg.extraSSHConfig} | ||
271 | ''} ${config.users.users."uucp".home}/.ssh/config | ||
272 | |||
273 | mkdir -p ${cfg.sshKeyDir} | ||
274 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.sshKeyDir} | ||
275 | chmod 700 ${cfg.sshKeyDir} | ||
276 | |||
277 | ${concatStringsSep "\n" (mapAttrsToList sshKeyGen cfg.remoteNodes)} | ||
278 | ''; | ||
279 | |||
280 | system.activationScripts."uucp-logs" = '' | ||
281 | touch ${cfg.logFile} | ||
282 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.logFile} | ||
283 | chmod 644 ${cfg.logFile} | ||
284 | touch ${cfg.statFile} | ||
285 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.statFile} | ||
286 | chmod 644 ${cfg.statFile} | ||
287 | touch ${cfg.debugFile} | ||
288 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.debugFile} | ||
289 | chmod 644 ${cfg.debugFile} | ||
290 | ''; | ||
291 | |||
292 | environment.etc."uucp/port" = { | ||
293 | text = '' | ||
294 | port ssh | ||
295 | type stdin | ||
296 | protocol ${cfg.incomingProtocols} | ||
297 | '' + concatStringsSep "\n" (mapAttrsToList portSpec cfg.remoteNodes); | ||
298 | }; | ||
299 | environment.etc."uucp/sys" = { | ||
300 | text = cfg.extraSys + "\n" + concatStringsSep "\n" (mapAttrsToList sysSpec cfg.remoteNodes); | ||
301 | }; | ||
302 | |||
303 | security.wrappers = let | ||
304 | wrapper = p: { | ||
305 | name = p; | ||
306 | value = { | ||
307 | source = "${pkgs.uucp}/bin/${p}"; | ||
308 | owner = "root"; | ||
309 | group = "root"; | ||
310 | setuid = true; | ||
311 | setgid = false; | ||
312 | }; | ||
313 | }; | ||
314 | in listToAttrs (map wrapper ["uucico" "cu" "uucp" "uuname" "uustat" "uux" "uuxqt"]); | ||
315 | |||
316 | nixpkgs.overlays = [(self: super: { | ||
317 | rmail = super.writeShellScriptBin "rmail" '' | ||
318 | # Dummy UUCP rmail command for postfix/qmail systems | ||
319 | |||
320 | IFS=" " read junk from junk junk junk junk junk junk junk relay | ||
321 | |||
322 | case "$from" in | ||
323 | *[@!]*) ;; | ||
324 | *) from="$from@$relay";; | ||
325 | esac | ||
326 | |||
327 | exec ${config.security.wrapperDir}/sendmail -G -i -f "$from" -- "$@" | ||
328 | ''; | ||
329 | })]; | ||
330 | |||
331 | environment.systemPackages = with pkgs; [ | ||
332 | uucp | ||
333 | ]; | ||
334 | |||
335 | systemd.services."uucico@" = { | ||
336 | serviceConfig = { | ||
337 | User = "uucp"; | ||
338 | Type = "oneshot"; | ||
339 | ExecStart = "${config.security.wrapperDir}/uucico -D -S %i"; | ||
340 | }; | ||
341 | }; | ||
342 | |||
343 | systemd.timers."uucico@" = { | ||
344 | timerConfig.OnActiveSec = cfg.interval; | ||
345 | timerConfig.OnUnitActiveSec = cfg.interval; | ||
346 | }; | ||
347 | |||
348 | systemd.targets."multi-user" = { | ||
349 | wants = mapAttrsToList (name: node: "uucico@${name}.timer") cfg.remoteNodes; | ||
350 | }; | ||
351 | |||
352 | systemd.kill-user.enable = true; | ||
353 | systemd.targets."sleep" = { | ||
354 | after = [ "kill-user@uucp.service" ]; | ||
355 | wants = [ "kill-user@uucp.service" ]; | ||
356 | }; | ||
357 | |||
358 | networking.networkmanager.dispatcherScripts = optional cfg.nmDispatch { | ||
359 | type = "basic"; | ||
360 | source = pkgs.writeScript "callRemotes.sh" '' | ||
361 | #!${pkgs.stdenv.shell} | ||
362 | |||
363 | shopt -s extglob | ||
364 | |||
365 | case "''${2}" in | ||
366 | (?(vpn-)up) | ||
367 | ${concatStringsSep "\n " (mapAttrsToList (name: node: "${pkgs.systemd}/bin/systemctl start uucico@${name}.service") cfg.remoteNodes)} | ||
368 | ;; | ||
369 | esac | ||
370 | ''; | ||
371 | }; | ||
372 | }; | ||
373 | } | ||
diff --git a/nvfetcher.toml b/nvfetcher.toml index ecaebba0..72c0d99d 100644 --- a/nvfetcher.toml +++ b/nvfetcher.toml | |||
@@ -115,3 +115,11 @@ fetch.git = "https://github.com/emersion/mako" | |||
115 | [swayosd] | 115 | [swayosd] |
116 | src.git = "https://github.com/ErikReider/SwayOSD" | 116 | src.git = "https://github.com/ErikReider/SwayOSD" |
117 | fetch.git = "https://github.com/ErikReider/SwayOSD" | 117 | fetch.git = "https://github.com/ErikReider/SwayOSD" |
118 | |||
119 | [netbootxyz-efi] | ||
120 | src.github = "netbootxyz/netboot.xyz" | ||
121 | fetch.url = "https://github.com/netbootxyz/netboot.xyz/releases/download/$ver/netboot.xyz.efi" | ||
122 | |||
123 | [netbootxyz-lkrn] | ||
124 | src.github = "netbootxyz/netboot.xyz" | ||
125 | fetch.url = "https://github.com/netbootxyz/netboot.xyz/releases/download/$ver/netboot.xyz.lkrn" | ||
diff --git a/overlays/abs-podcast-autoplaylist/.envrc b/overlays/abs-podcast-autoplaylist/.envrc new file mode 100644 index 00000000..2c909235 --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/.envrc | |||
@@ -0,0 +1,4 @@ | |||
1 | use flake | ||
2 | |||
3 | [[ -d ".venv" ]] || ( uv venv && uv sync ) | ||
4 | . .venv/bin/activate | ||
diff --git a/overlays/abs-podcast-autoplaylist/.gitignore b/overlays/abs-podcast-autoplaylist/.gitignore new file mode 100644 index 00000000..4ccfae70 --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | .venv | ||
2 | **/__pycache__ | ||
diff --git a/overlays/abs-podcast-autoplaylist/abs_podcast_autoplaylist/__init__.py b/overlays/abs-podcast-autoplaylist/abs_podcast_autoplaylist/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/abs_podcast_autoplaylist/__init__.py | |||
diff --git a/overlays/abs-podcast-autoplaylist/abs_podcast_autoplaylist/__main__.py b/overlays/abs-podcast-autoplaylist/abs_podcast_autoplaylist/__main__.py new file mode 100644 index 00000000..fd739805 --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/abs_podcast_autoplaylist/__main__.py | |||
@@ -0,0 +1,107 @@ | |||
1 | import click | ||
2 | from pathlib import Path | ||
3 | import tomllib | ||
4 | import requests | ||
5 | from urllib.parse import urljoin | ||
6 | from operator import itemgetter | ||
7 | import re | ||
8 | from frozendict import frozendict | ||
9 | |||
10 | class BearerAuth(requests.auth.AuthBase): | ||
11 | def __init__(self, token): | ||
12 | self.token = token | ||
13 | def __call__(self, r): | ||
14 | r.headers["authorization"] = "Bearer " + self.token | ||
15 | return r | ||
16 | |||
17 | class ABSSession(requests.Session): | ||
18 | def __init__(self, config): | ||
19 | super().__init__() | ||
20 | self.base_url = config['instance'] | ||
21 | self.auth = BearerAuth(config['api_token']) | ||
22 | |||
23 | def request(self, method, url, *args, **kwargs): | ||
24 | joined_url = urljoin(self.base_url, url) | ||
25 | return super().request(method, joined_url, *args, **kwargs) | ||
26 | |||
27 | @click.command() | ||
28 | @click.argument('config_file', type=click.Path(dir_okay=False, path_type=Path)) | ||
29 | def main(config_file: Path): | ||
30 | with config_file.open('rb') as fh: | ||
31 | config = tomllib.load(fh) | ||
32 | |||
33 | with ABSSession(config) as s: | ||
34 | libraries = s.get('/api/libraries').json()['libraries'] | ||
35 | playlists = s.get('/api/playlists').json()['playlists'] | ||
36 | |||
37 | for library_config in config['libraries']: | ||
38 | [library] = filter(lambda l: l['name'] == library_config['name'], libraries) | ||
39 | filtered_playlists = list(filter(lambda p: p['name'] == library_config['playlist'] and p['libraryId'] == library['id'], playlists)) | ||
40 | def get_playlist(): | ||
41 | playlist = None | ||
42 | if filtered_playlists: | ||
43 | [playlist] = filtered_playlists | ||
44 | if not playlist: | ||
45 | playlist = s.post('/api/playlists', json={ | ||
46 | 'libraryId': library['id'], | ||
47 | 'name': library_config['playlist'], | ||
48 | }).json() | ||
49 | return playlist | ||
50 | |||
51 | podcasts = dict() | ||
52 | items = s.get('/api/libraries/{}/items'.format(library['id'])).json()['results'] | ||
53 | for item in items: | ||
54 | item = s.get('/api/items/{}'.format(item['id']), json={'expanded': True}).json() | ||
55 | episodes = list() | ||
56 | for episode in sorted(item['media']['episodes'], key = itemgetter('publishedAt')): | ||
57 | progress = s.get('/api/me/progress/{}/{}'.format(episode['libraryItemId'], episode['id'])) | ||
58 | if progress.ok and progress.json()["isFinished"]: | ||
59 | continue | ||
60 | episodes.append(episode) | ||
61 | podcasts[item['media']['metadata']['title']] = list(map(lambda x: frozendict({ 'libraryItemId': x['libraryItemId'], 'episodeId': x['id']}), episodes)) | ||
62 | def lookup_podcast(expr): | ||
63 | expr = re.compile(expr, flags=re.I) | ||
64 | matches = filter(lambda t: expr.search(t), podcasts.keys()) | ||
65 | match list(matches): | ||
66 | case [x]: | ||
67 | return (x,) | ||
68 | case _: | ||
69 | raise RuntimeError("No unique match for ‘{}’".format(expr)) | ||
70 | |||
71 | priorities = [ | ||
72 | [ | ||
73 | k | ||
74 | for item in (section if type(section) is list else [section]) | ||
75 | for k in lookup_podcast(item) | ||
76 | ] | ||
77 | for section in library_config['priorities'] | ||
78 | ] | ||
79 | |||
80 | playlist_items = list() | ||
81 | for section in priorities: | ||
82 | while any(map(lambda item: item in podcasts, section)): | ||
83 | for item in section: | ||
84 | if not item in podcasts: | ||
85 | continue | ||
86 | |||
87 | if not podcasts[item]: | ||
88 | del podcasts[item] | ||
89 | continue | ||
90 | |||
91 | playlist_items.append(podcasts[item].pop(0)) | ||
92 | |||
93 | playlist = get_playlist() | ||
94 | current_playlist_items = map(lambda item: frozendict({ k: v for k, v in item.items() if k in {'libraryItemId', 'episodeId'}}), playlist['items']) | ||
95 | |||
96 | if current_playlist_items == playlist_items: | ||
97 | continue | ||
98 | |||
99 | to_remove = set(current_playlist_items) - set(playlist_items) | ||
100 | if to_remove: | ||
101 | s.post('/api/playlists/{}/batch/remove'.format(playlist['id']), json={'items': list(to_remove)}).raise_for_status() | ||
102 | playlist = get_playlist() | ||
103 | to_add = set(playlist_items) - set(current_playlist_items) | ||
104 | if to_add: | ||
105 | s.post('/api/playlists/{}/batch/add'.format(playlist['id']), json={'items': list(to_add)}).raise_for_status() | ||
106 | |||
107 | r = s.patch('/api/playlists/{}'.format(playlist['id']), json={'items': playlist_items}).raise_for_status() | ||
diff --git a/overlays/abs-podcast-autoplaylist/default.nix b/overlays/abs-podcast-autoplaylist/default.nix new file mode 100644 index 00000000..843f1b65 --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/default.nix | |||
@@ -0,0 +1,19 @@ | |||
1 | { prev, final, flake, flakeInputs, ... }: | ||
2 | |||
3 | let | ||
4 | workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; | ||
5 | pythonSet = flake.lib.pythonSet { | ||
6 | pkgs = final; | ||
7 | python = final.python312; | ||
8 | overlay = workspace.mkPyprojectOverlay { | ||
9 | sourcePreference = "wheel"; | ||
10 | }; | ||
11 | }; | ||
12 | virtualEnv = pythonSet.mkVirtualEnv "abs-podcast-autoplaylist-env" workspace.deps.default; | ||
13 | in { | ||
14 | abs-podcast-autoplaylist = virtualEnv.overrideAttrs (oldAttrs: { | ||
15 | meta = (oldAttrs.meta or {}) // { | ||
16 | mainProgram = "abs-podcast-autoplaylist"; | ||
17 | }; | ||
18 | }); | ||
19 | } | ||
diff --git a/overlays/abs-podcast-autoplaylist/pyproject.toml b/overlays/abs-podcast-autoplaylist/pyproject.toml new file mode 100644 index 00000000..f52a84bc --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/pyproject.toml | |||
@@ -0,0 +1,16 @@ | |||
1 | [project] | ||
2 | name = "abs-podcast-autoplaylist" | ||
3 | version = "0.1.0" | ||
4 | requires-python = ">=3.12" | ||
5 | dependencies = [ | ||
6 | "click>=8.1.8", | ||
7 | "frozendict>=2.4.6", | ||
8 | "requests>=2.32.3", | ||
9 | ] | ||
10 | |||
11 | [project.scripts] | ||
12 | abs-podcast-autoplaylist = "abs_podcast_autoplaylist.__main__:main" | ||
13 | |||
14 | [build-system] | ||
15 | requires = ["hatchling"] | ||
16 | build-backend = "hatchling.build" | ||
diff --git a/overlays/abs-podcast-autoplaylist/uv.lock b/overlays/abs-podcast-autoplaylist/uv.lock new file mode 100644 index 00000000..17de5f0e --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/uv.lock | |||
@@ -0,0 +1,129 @@ | |||
1 | version = 1 | ||
2 | revision = 2 | ||
3 | requires-python = ">=3.12" | ||
4 | |||
5 | [[package]] | ||
6 | name = "abs-podcast-autoplaylist" | ||
7 | version = "0.1.0" | ||
8 | source = { editable = "." } | ||
9 | dependencies = [ | ||
10 | { name = "click" }, | ||
11 | { name = "frozendict" }, | ||
12 | { name = "requests" }, | ||
13 | ] | ||
14 | |||
15 | [package.metadata] | ||
16 | requires-dist = [ | ||
17 | { name = "click", specifier = ">=8.1.8" }, | ||
18 | { name = "frozendict", specifier = ">=2.4.6" }, | ||
19 | { name = "requests", specifier = ">=2.32.3" }, | ||
20 | ] | ||
21 | |||
22 | [[package]] | ||
23 | name = "certifi" | ||
24 | version = "2025.4.26" | ||
25 | source = { registry = "https://pypi.org/simple" } | ||
26 | sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload_time = "2025-04-26T02:12:29.51Z" } | ||
27 | wheels = [ | ||
28 | { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload_time = "2025-04-26T02:12:27.662Z" }, | ||
29 | ] | ||
30 | |||
31 | [[package]] | ||
32 | name = "charset-normalizer" | ||
33 | version = "3.4.2" | ||
34 | source = { registry = "https://pypi.org/simple" } | ||
35 | sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload_time = "2025-05-02T08:34:42.01Z" } | ||
36 | wheels = [ | ||
37 | { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload_time = "2025-05-02T08:32:33.712Z" }, | ||
38 | { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload_time = "2025-05-02T08:32:35.768Z" }, | ||
39 | { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload_time = "2025-05-02T08:32:37.284Z" }, | ||
40 | { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload_time = "2025-05-02T08:32:38.803Z" }, | ||
41 | { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload_time = "2025-05-02T08:32:40.251Z" }, | ||
42 | { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload_time = "2025-05-02T08:32:41.705Z" }, | ||
43 | { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload_time = "2025-05-02T08:32:43.709Z" }, | ||
44 | { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload_time = "2025-05-02T08:32:46.197Z" }, | ||
45 | { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload_time = "2025-05-02T08:32:48.105Z" }, | ||
46 | { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload_time = "2025-05-02T08:32:49.719Z" }, | ||
47 | { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload_time = "2025-05-02T08:32:51.404Z" }, | ||
48 | { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload_time = "2025-05-02T08:32:53.079Z" }, | ||
49 | { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload_time = "2025-05-02T08:32:54.573Z" }, | ||
50 | { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload_time = "2025-05-02T08:32:56.363Z" }, | ||
51 | { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload_time = "2025-05-02T08:32:58.551Z" }, | ||
52 | { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload_time = "2025-05-02T08:33:00.342Z" }, | ||
53 | { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload_time = "2025-05-02T08:33:02.081Z" }, | ||
54 | { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload_time = "2025-05-02T08:33:04.063Z" }, | ||
55 | { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload_time = "2025-05-02T08:33:06.418Z" }, | ||
56 | { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload_time = "2025-05-02T08:33:08.183Z" }, | ||
57 | { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload_time = "2025-05-02T08:33:09.986Z" }, | ||
58 | { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload_time = "2025-05-02T08:33:11.814Z" }, | ||
59 | { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload_time = "2025-05-02T08:33:13.707Z" }, | ||
60 | { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload_time = "2025-05-02T08:33:15.458Z" }, | ||
61 | { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload_time = "2025-05-02T08:33:17.06Z" }, | ||
62 | { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload_time = "2025-05-02T08:33:18.753Z" }, | ||
63 | { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload_time = "2025-05-02T08:34:40.053Z" }, | ||
64 | ] | ||
65 | |||
66 | [[package]] | ||
67 | name = "click" | ||
68 | version = "8.1.8" | ||
69 | source = { registry = "https://pypi.org/simple" } | ||
70 | dependencies = [ | ||
71 | { name = "colorama", marker = "sys_platform == 'win32'" }, | ||
72 | ] | ||
73 | sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload_time = "2024-12-21T18:38:44.339Z" } | ||
74 | wheels = [ | ||
75 | { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload_time = "2024-12-21T18:38:41.666Z" }, | ||
76 | ] | ||
77 | |||
78 | [[package]] | ||
79 | name = "colorama" | ||
80 | version = "0.4.6" | ||
81 | source = { registry = "https://pypi.org/simple" } | ||
82 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload_time = "2022-10-25T02:36:22.414Z" } | ||
83 | wheels = [ | ||
84 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload_time = "2022-10-25T02:36:20.889Z" }, | ||
85 | ] | ||
86 | |||
87 | [[package]] | ||
88 | name = "frozendict" | ||
89 | version = "2.4.6" | ||
90 | source = { registry = "https://pypi.org/simple" } | ||
91 | sdist = { url = "https://files.pythonhosted.org/packages/bb/59/19eb300ba28e7547538bdf603f1c6c34793240a90e1a7b61b65d8517e35e/frozendict-2.4.6.tar.gz", hash = "sha256:df7cd16470fbd26fc4969a208efadc46319334eb97def1ddf48919b351192b8e", size = 316416, upload_time = "2024-10-13T12:15:32.449Z" } | ||
92 | wheels = [ | ||
93 | { url = "https://files.pythonhosted.org/packages/04/13/d9839089b900fa7b479cce495d62110cddc4bd5630a04d8469916c0e79c5/frozendict-2.4.6-py311-none-any.whl", hash = "sha256:d065db6a44db2e2375c23eac816f1a022feb2fa98cbb50df44a9e83700accbea", size = 16148, upload_time = "2024-10-13T12:15:26.839Z" }, | ||
94 | { url = "https://files.pythonhosted.org/packages/ba/d0/d482c39cee2ab2978a892558cf130681d4574ea208e162da8958b31e9250/frozendict-2.4.6-py312-none-any.whl", hash = "sha256:49344abe90fb75f0f9fdefe6d4ef6d4894e640fadab71f11009d52ad97f370b9", size = 16146, upload_time = "2024-10-13T12:15:28.16Z" }, | ||
95 | { url = "https://files.pythonhosted.org/packages/a5/8e/b6bf6a0de482d7d7d7a2aaac8fdc4a4d0bb24a809f5ddd422aa7060eb3d2/frozendict-2.4.6-py313-none-any.whl", hash = "sha256:7134a2bb95d4a16556bb5f2b9736dceb6ea848fa5b6f3f6c2d6dba93b44b4757", size = 16146, upload_time = "2024-10-13T12:15:29.495Z" }, | ||
96 | ] | ||
97 | |||
98 | [[package]] | ||
99 | name = "idna" | ||
100 | version = "3.10" | ||
101 | source = { registry = "https://pypi.org/simple" } | ||
102 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload_time = "2024-09-15T18:07:39.745Z" } | ||
103 | wheels = [ | ||
104 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload_time = "2024-09-15T18:07:37.964Z" }, | ||
105 | ] | ||
106 | |||
107 | [[package]] | ||
108 | name = "requests" | ||
109 | version = "2.32.3" | ||
110 | source = { registry = "https://pypi.org/simple" } | ||
111 | dependencies = [ | ||
112 | { name = "certifi" }, | ||
113 | { name = "charset-normalizer" }, | ||
114 | { name = "idna" }, | ||
115 | { name = "urllib3" }, | ||
116 | ] | ||
117 | sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload_time = "2024-05-29T15:37:49.536Z" } | ||
118 | wheels = [ | ||
119 | { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload_time = "2024-05-29T15:37:47.027Z" }, | ||
120 | ] | ||
121 | |||
122 | [[package]] | ||
123 | name = "urllib3" | ||
124 | version = "2.4.0" | ||
125 | source = { registry = "https://pypi.org/simple" } | ||
126 | sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload_time = "2025-04-10T15:23:39.232Z" } | ||
127 | wheels = [ | ||
128 | { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload_time = "2025-04-10T15:23:37.377Z" }, | ||
129 | ] | ||
diff --git a/overlays/cake-prometheus-exporter/default.nix b/overlays/cake-prometheus-exporter/default.nix index 3d0acc2d..69a5008c 100644 --- a/overlays/cake-prometheus-exporter/default.nix +++ b/overlays/cake-prometheus-exporter/default.nix | |||
@@ -1,19 +1,18 @@ | |||
1 | { final, prev, ... }: | 1 | { final, prev, ... }: |
2 | let | 2 | let |
3 | inpPython = final.python310.override {}; | 3 | inpPython = final.python310.override {}; |
4 | python = inpPython.withPackages (ps: with ps; []); | ||
4 | in { | 5 | in { |
5 | cake-prometheus-exporter = prev.stdenv.mkDerivation rec { | 6 | cake-prometheus-exporter = prev.stdenv.mkDerivation rec { |
6 | pname = "cake-prometheus-exporter"; | 7 | pname = "cake-prometheus-exporter"; |
7 | version = "0.0.0"; | 8 | version = "0.0.0"; |
8 | 9 | ||
9 | src = ./cake-prometheus-exporter.py; | 10 | src = prev.replaceVars ./cake-prometheus-exporter.py { inherit python; }; |
10 | 11 | ||
11 | phases = [ "buildPhase" "checkPhase" "installPhase" ]; | 12 | phases = [ "unpackPhase" "checkPhase" "installPhase" ]; |
12 | 13 | ||
13 | python = inpPython.withPackages (ps: with ps; []); | 14 | unpackPhase = '' |
14 | 15 | cp $src cake-prometheus-exporter | |
15 | buildPhase = '' | ||
16 | substituteAll $src cake-prometheus-exporter | ||
17 | ''; | 16 | ''; |
18 | 17 | ||
19 | doCheck = true; | 18 | doCheck = true; |
diff --git a/overlays/deploy-rs.nix b/overlays/deploy-rs.nix index 0bf1c3b2..678c6f5f 100644 --- a/overlays/deploy-rs.nix +++ b/overlays/deploy-rs.nix | |||
@@ -2,13 +2,15 @@ | |||
2 | flakeInputs.deploy-rs.overlays.default | 2 | flakeInputs.deploy-rs.overlays.default |
3 | (final: prev: { | 3 | (final: prev: { |
4 | deploy-rs = prev.deploy-rs // { | 4 | deploy-rs = prev.deploy-rs // { |
5 | deploy-rs = prev.deploy-rs.deploy-rs.overrideAttrs (oldAttrs: { | 5 | deploy-rs = prev.symlinkJoin { |
6 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ [final.makeWrapper]; | 6 | name = "${prev.deploy-rs.deploy-rs.name}-wrapped"; |
7 | preFixup = '' | 7 | paths = [ prev.deploy-rs.deploy-rs ]; |
8 | buildInputs = [ prev.makeWrapper ]; | ||
9 | postBuild = '' | ||
8 | wrapProgram $out/bin/deploy \ | 10 | wrapProgram $out/bin/deploy \ |
9 | --prefix PATH : ${prev.lib.makeBinPath (with final; [ nix-monitored ])} | 11 | --prefix PATH : ${prev.lib.makeBinPath (with final; [ nix-monitored ])} |
10 | ''; | 12 | ''; |
11 | }); | 13 | }; |
12 | }; | 14 | }; |
13 | }) | 15 | }) |
14 | final prev | 16 | final prev |
diff --git a/overlays/inwx-cdnskey/default.nix b/overlays/inwx-cdnskey/default.nix index cd564f24..e1bee0f2 100644 --- a/overlays/inwx-cdnskey/default.nix +++ b/overlays/inwx-cdnskey/default.nix | |||
@@ -2,17 +2,16 @@ | |||
2 | let | 2 | let |
3 | packageOverrides = final.callPackage ./python-packages.nix {}; | 3 | packageOverrides = final.callPackage ./python-packages.nix {}; |
4 | inpPython = final.python39.override { inherit packageOverrides; }; | 4 | inpPython = final.python39.override { inherit packageOverrides; }; |
5 | python = inpPython.withPackages (ps: with ps; [pyxdg inwx-domrobot configparser dnspython]); | ||
5 | in { | 6 | in { |
6 | inwx-cdnskey = prev.stdenv.mkDerivation rec { | 7 | inwx-cdnskey = prev.stdenv.mkDerivation rec { |
7 | name = "inwx-cdnskey"; | 8 | name = "inwx-cdnskey"; |
8 | src = ./inwx-cdnskey.py; | 9 | src = prev.replaceVars ./inwx-cdnskey.py { inherit python; }; |
9 | 10 | ||
10 | phases = [ "buildPhase" "checkPhase" "installPhase" ]; | 11 | phases = [ "unpackPhase" "checkPhase" "installPhase" ]; |
11 | 12 | ||
12 | python = inpPython.withPackages (ps: with ps; [pyxdg inwx-domrobot configparser dnspython]); | 13 | unpackPhase = '' |
13 | 14 | cp $src inwx-cdnskey | |
14 | buildPhase = '' | ||
15 | substituteAll $src inwx-cdnskey | ||
16 | ''; | 15 | ''; |
17 | 16 | ||
18 | doCheck = true; | 17 | doCheck = true; |
diff --git a/overlays/lesspipe.nix b/overlays/lesspipe.nix index 3258eb70..b791f6e5 100644 --- a/overlays/lesspipe.nix +++ b/overlays/lesspipe.nix | |||
@@ -17,7 +17,7 @@ | |||
17 | 17 | ||
18 | preFixup = '' | 18 | preFixup = '' |
19 | wrapProgram $out/bin/lesspipe.sh \ | 19 | wrapProgram $out/bin/lesspipe.sh \ |
20 | --prefix PATH : ${final.python3.pkgs.pygments}/bin:${final.file}/bin:${final.ncurses}/bin | 20 | --prefix PATH : ${prev.lib.makeBinPath (with final; [ file ncurses binutils ])} |
21 | ''; | 21 | ''; |
22 | }; | 22 | }; |
23 | } | 23 | } |
diff --git a/overlays/nftables-prometheus-exporter/default.nix b/overlays/nftables-prometheus-exporter/default.nix index aab0c8e9..48f668c4 100644 --- a/overlays/nftables-prometheus-exporter/default.nix +++ b/overlays/nftables-prometheus-exporter/default.nix | |||
@@ -1,17 +1,16 @@ | |||
1 | { final, prev, ... }: | 1 | { final, prev, ... }: |
2 | let | 2 | let |
3 | inpPython = final.python310; | 3 | inpPython = final.python310; |
4 | python = inpPython.withPackages (ps: with ps; []); | ||
4 | in { | 5 | in { |
5 | nftables-prometheus-exporter = prev.stdenv.mkDerivation rec { | 6 | nftables-prometheus-exporter = prev.stdenv.mkDerivation rec { |
6 | name = "nftables-prometheus-exporter"; | 7 | name = "nftables-prometheus-exporter"; |
7 | src = ./nftables-prometheus-exporter.py; | 8 | src = prev.replaceVars ./nftables-prometheus-exporter.py { inherit python; }; |
8 | 9 | ||
9 | phases = [ "buildPhase" "checkPhase" "installPhase" ]; | 10 | phases = [ "unpackPhase" "checkPhase" "installPhase" ]; |
10 | 11 | ||
11 | python = inpPython.withPackages (ps: with ps; []); | 12 | unpackPhase = '' |
12 | 13 | cp $src nftables-prometheus-exporter | |
13 | buildPhase = '' | ||
14 | substituteAll $src nftables-prometheus-exporter | ||
15 | ''; | 14 | ''; |
16 | 15 | ||
17 | doCheck = true; | 16 | doCheck = true; |
diff --git a/overlays/persistent-nix-shell/default.nix b/overlays/persistent-nix-shell/default.nix index c36b9e86..6067cade 100644 --- a/overlays/persistent-nix-shell/default.nix +++ b/overlays/persistent-nix-shell/default.nix | |||
@@ -5,10 +5,9 @@ | |||
5 | 5 | ||
6 | phases = [ "buildPhase" "installPhase" ]; | 6 | phases = [ "buildPhase" "installPhase" ]; |
7 | 7 | ||
8 | inherit (final) zsh; | ||
9 | |||
10 | buildPhase = '' | 8 | buildPhase = '' |
11 | substituteAll $src persistent-nix-shell | 9 | substitute $src persistent-nix-shell \ |
10 | --subst-var-by zsh ${final.zsh} | ||
12 | ''; | 11 | ''; |
13 | 12 | ||
14 | installPhase = '' | 13 | installPhase = '' |
diff --git a/overlays/postsrsd.nix b/overlays/postsrsd.nix new file mode 100644 index 00000000..cb1ccf30 --- /dev/null +++ b/overlays/postsrsd.nix | |||
@@ -0,0 +1,11 @@ | |||
1 | { final, prev, ... }: | ||
2 | { | ||
3 | postsrsd = prev.postsrsd.overrideAttrs (oldAttrs: { | ||
4 | cmakeFlags = (oldAttrs.cmakeFlags or []) ++ [ | ||
5 | "-DWITH_MILTER=ON" | ||
6 | ]; | ||
7 | buildInputs = (oldAttrs.buildInputs or []) ++ [ | ||
8 | final.libmilter | ||
9 | ]; | ||
10 | }); | ||
11 | } | ||
diff --git a/overlays/uucp/default.nix b/overlays/uucp/default.nix deleted file mode 100644 index 4189dbcc..00000000 --- a/overlays/uucp/default.nix +++ /dev/null | |||
@@ -1,9 +0,0 @@ | |||
1 | { final, prev, ... }: { | ||
2 | uucp = prev.uucp.overrideAttrs (oldAttrs: { | ||
3 | configureFlags = (oldAttrs.configureFlags or []) ++ ["--with-newconfigdir=/etc/uucp"]; | ||
4 | patches = (oldAttrs.patches or []) ++ [ | ||
5 | ./mailprogram.patch | ||
6 | ]; | ||
7 | NIX_CFLAGS_COMPILE = "${oldAttrs.NIX_CFLAGS_COMPILE or ""} -Wno-error=incompatible-pointer-types"; | ||
8 | }); | ||
9 | } | ||
diff --git a/overlays/uucp/mailprogram.patch b/overlays/uucp/mailprogram.patch deleted file mode 100644 index 89ac8f31..00000000 --- a/overlays/uucp/mailprogram.patch +++ /dev/null | |||
@@ -1,16 +0,0 @@ | |||
1 | policy.h | 2 +- | ||
2 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
3 | |||
4 | diff --git a/policy.h b/policy.h | ||
5 | index 5afe34b..8e92c8b 100644 | ||
6 | --- a/policy.h | ||
7 | +++ b/policy.h | ||
8 | @@ -240,7 +240,7 @@ | ||
9 | the sendmail choice below. Otherwise, select one of the other | ||
10 | choices as appropriate. */ | ||
11 | #if 1 | ||
12 | -#define MAIL_PROGRAM "/usr/lib/sendmail -t" | ||
13 | +#define MAIL_PROGRAM "${config.security.wrapperDir}/sendmail -t" | ||
14 | /* #define MAIL_PROGRAM "/usr/sbin/sendmail -t" */ | ||
15 | #define MAIL_PROGRAM_TO_BODY 1 | ||
16 | #define MAIL_PROGRAM_SUBJECT_BODY 1 | ||
diff --git a/overlays/waybar-systemd-inhibit/.envrc b/overlays/waybar-systemd-inhibit/.envrc new file mode 100644 index 00000000..2c909235 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/.envrc | |||
@@ -0,0 +1,4 @@ | |||
1 | use flake | ||
2 | |||
3 | [[ -d ".venv" ]] || ( uv venv && uv sync ) | ||
4 | . .venv/bin/activate | ||
diff --git a/overlays/waybar-systemd-inhibit/.gitignore b/overlays/waybar-systemd-inhibit/.gitignore new file mode 100644 index 00000000..4ccfae70 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | .venv | ||
2 | **/__pycache__ | ||
diff --git a/overlays/waybar-systemd-inhibit/default.nix b/overlays/waybar-systemd-inhibit/default.nix new file mode 100644 index 00000000..ae6b8c75 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/default.nix | |||
@@ -0,0 +1,20 @@ | |||
1 | { prev, final, flake, flakeInputs, ... }: | ||
2 | |||
3 | let | ||
4 | workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; | ||
5 | pythonSet = flake.lib.pythonSet { | ||
6 | pkgs = final; | ||
7 | python = final.python312; | ||
8 | overlay = workspace.mkPyprojectOverlay { | ||
9 | sourcePreference = "wheel"; | ||
10 | }; | ||
11 | }; | ||
12 | virtualEnv = pythonSet.mkVirtualEnv "waybar-systemd-inhibit-env" workspace.deps.default; | ||
13 | in { | ||
14 | waybar-systemd-inhibit = virtualEnv.overrideAttrs (oldAttrs: { | ||
15 | meta = (oldAttrs.meta or {}) // { | ||
16 | mainProgram = "waybar-systemd-inhibit"; | ||
17 | }; | ||
18 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ [ final.gobject-introspection final.wrapGAppsHook ]; | ||
19 | }); | ||
20 | } | ||
diff --git a/overlays/waybar-systemd-inhibit/pyproject.toml b/overlays/waybar-systemd-inhibit/pyproject.toml new file mode 100644 index 00000000..6c6240b8 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/pyproject.toml | |||
@@ -0,0 +1,17 @@ | |||
1 | [project] | ||
2 | name = "waybar-systemd-inhibit" | ||
3 | version = "0.1.0" | ||
4 | requires-python = ">=3.12" | ||
5 | dependencies = [ | ||
6 | "asyncclick>=8.1.8", | ||
7 | "asyncio>=3.4.3", | ||
8 | "dbus-next>=0.2.3", | ||
9 | ] | ||
10 | |||
11 | [project.scripts] | ||
12 | waybar-systemd-inhibit = "waybar_systemd_inhibit.__main__:main" | ||
13 | waybar-systemd-inhibit-toggle = "waybar_systemd_inhibit.__main__:toggle" | ||
14 | |||
15 | [build-system] | ||
16 | requires = ["hatchling"] | ||
17 | build-backend = "hatchling.build" | ||
diff --git a/overlays/waybar-systemd-inhibit/uv.lock b/overlays/waybar-systemd-inhibit/uv.lock new file mode 100644 index 00000000..4e10d145 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/uv.lock | |||
@@ -0,0 +1,102 @@ | |||
1 | version = 1 | ||
2 | revision = 2 | ||
3 | requires-python = ">=3.12" | ||
4 | |||
5 | [[package]] | ||
6 | name = "anyio" | ||
7 | version = "4.9.0" | ||
8 | source = { registry = "https://pypi.org/simple" } | ||
9 | dependencies = [ | ||
10 | { name = "idna" }, | ||
11 | { name = "sniffio" }, | ||
12 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, | ||
13 | ] | ||
14 | sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } | ||
15 | wheels = [ | ||
16 | { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, | ||
17 | ] | ||
18 | |||
19 | [[package]] | ||
20 | name = "asyncclick" | ||
21 | version = "8.1.8" | ||
22 | source = { registry = "https://pypi.org/simple" } | ||
23 | dependencies = [ | ||
24 | { name = "anyio" }, | ||
25 | { name = "colorama", marker = "sys_platform == 'win32'" }, | ||
26 | ] | ||
27 | sdist = { url = "https://files.pythonhosted.org/packages/cb/b5/e1e5fdf1c1bb7e6e614987c120a98d9324bf8edfaa5f5cd16a6235c9d91b/asyncclick-8.1.8.tar.gz", hash = "sha256:0f0eb0f280e04919d67cf71b9fcdfb4db2d9ff7203669c40284485c149578e4c", size = 232900, upload-time = "2025-01-06T09:46:52.694Z" } | ||
28 | wheels = [ | ||
29 | { url = "https://files.pythonhosted.org/packages/14/cc/a436f0fc2d04e57a0697e0f87a03b9eaed03ad043d2d5f887f8eebcec95f/asyncclick-8.1.8-py3-none-any.whl", hash = "sha256:eb1ccb44bc767f8f0695d592c7806fdf5bd575605b4ee246ffd5fadbcfdbd7c6", size = 99093, upload-time = "2025-01-06T09:46:51.046Z" }, | ||
30 | { url = "https://files.pythonhosted.org/packages/92/c4/ae9e9d25522c6dc96ff167903880a0fe94d7bd31ed999198ee5017d977ed/asyncclick-8.1.8.0-py3-none-any.whl", hash = "sha256:be146a2d8075d4fe372ff4e877f23c8b5af269d16705c1948123b9415f6fd678", size = 99115, upload-time = "2025-01-06T09:50:52.72Z" }, | ||
31 | ] | ||
32 | |||
33 | [[package]] | ||
34 | name = "asyncio" | ||
35 | version = "3.4.3" | ||
36 | source = { registry = "https://pypi.org/simple" } | ||
37 | sdist = { url = "https://files.pythonhosted.org/packages/da/54/054bafaf2c0fb8473d423743e191fcdf49b2c1fd5e9af3524efbe097bafd/asyncio-3.4.3.tar.gz", hash = "sha256:83360ff8bc97980e4ff25c964c7bd3923d333d177aa4f7fb736b019f26c7cb41", size = 204411, upload-time = "2015-03-10T14:11:26.494Z" } | ||
38 | wheels = [ | ||
39 | { url = "https://files.pythonhosted.org/packages/22/74/07679c5b9f98a7cb0fc147b1ef1cc1853bc07a4eb9cb5731e24732c5f773/asyncio-3.4.3-py3-none-any.whl", hash = "sha256:c4d18b22701821de07bd6aea8b53d21449ec0ec5680645e5317062ea21817d2d", size = 101767, upload-time = "2015-03-10T14:05:10.959Z" }, | ||
40 | ] | ||
41 | |||
42 | [[package]] | ||
43 | name = "colorama" | ||
44 | version = "0.4.6" | ||
45 | source = { registry = "https://pypi.org/simple" } | ||
46 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } | ||
47 | wheels = [ | ||
48 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, | ||
49 | ] | ||
50 | |||
51 | [[package]] | ||
52 | name = "dbus-next" | ||
53 | version = "0.2.3" | ||
54 | source = { registry = "https://pypi.org/simple" } | ||
55 | sdist = { url = "https://files.pythonhosted.org/packages/ce/45/6a40fbe886d60a8c26f480e7d12535502b5ba123814b3b9a0b002ebca198/dbus_next-0.2.3.tar.gz", hash = "sha256:f4eae26909332ada528c0a3549dda8d4f088f9b365153952a408e28023a626a5", size = 71112, upload-time = "2021-07-25T22:11:28.398Z" } | ||
56 | wheels = [ | ||
57 | { url = "https://files.pythonhosted.org/packages/d2/fc/c0a3f4c4eaa5a22fbef91713474666e13d0ea2a69c84532579490a9f2cc8/dbus_next-0.2.3-py3-none-any.whl", hash = "sha256:58948f9aff9db08316734c0be2a120f6dc502124d9642f55e90ac82ffb16a18b", size = 57885, upload-time = "2021-07-25T22:11:25.466Z" }, | ||
58 | ] | ||
59 | |||
60 | [[package]] | ||
61 | name = "idna" | ||
62 | version = "3.10" | ||
63 | source = { registry = "https://pypi.org/simple" } | ||
64 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } | ||
65 | wheels = [ | ||
66 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, | ||
67 | ] | ||
68 | |||
69 | [[package]] | ||
70 | name = "sniffio" | ||
71 | version = "1.3.1" | ||
72 | source = { registry = "https://pypi.org/simple" } | ||
73 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } | ||
74 | wheels = [ | ||
75 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, | ||
76 | ] | ||
77 | |||
78 | [[package]] | ||
79 | name = "typing-extensions" | ||
80 | version = "4.13.2" | ||
81 | source = { registry = "https://pypi.org/simple" } | ||
82 | sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } | ||
83 | wheels = [ | ||
84 | { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, | ||
85 | ] | ||
86 | |||
87 | [[package]] | ||
88 | name = "waybar-systemd-inhibit" | ||
89 | version = "0.1.0" | ||
90 | source = { editable = "." } | ||
91 | dependencies = [ | ||
92 | { name = "asyncclick" }, | ||
93 | { name = "asyncio" }, | ||
94 | { name = "dbus-next" }, | ||
95 | ] | ||
96 | |||
97 | [package.metadata] | ||
98 | requires-dist = [ | ||
99 | { name = "asyncclick", specifier = ">=8.1.8" }, | ||
100 | { name = "asyncio", specifier = ">=3.4.3" }, | ||
101 | { name = "dbus-next", specifier = ">=0.2.3" }, | ||
102 | ] | ||
diff --git a/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__init__.py b/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__init__.py | |||
diff --git a/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__main__.py b/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__main__.py new file mode 100644 index 00000000..35cc7fd1 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__main__.py | |||
@@ -0,0 +1,117 @@ | |||
1 | import asyncclick as click | ||
2 | from dbus_next.aio import MessageBus | ||
3 | from dbus_next import BusType, Message, PropertyAccess | ||
4 | import asyncio | ||
5 | from functools import update_wrapper | ||
6 | from dbus_next.service import ServiceInterface, method, dbus_property | ||
7 | from dbus_next import Variant, DBusError | ||
8 | import os | ||
9 | import json | ||
10 | |||
11 | class BlockInterface(ServiceInterface): | ||
12 | def __init__(self, system_bus, logind): | ||
13 | super().__init__('li.yggdrasil.WaybarSystemdInhibit') | ||
14 | self.system_bus = system_bus | ||
15 | self.logind = logind | ||
16 | self.fd = None | ||
17 | |||
18 | def Release(self): | ||
19 | if not self.fd: | ||
20 | return | ||
21 | |||
22 | os.close(self.fd) | ||
23 | self.fd = None | ||
24 | self.emit_properties_changed({'IsAcquired': False}) | ||
25 | |||
26 | async def Acquire(self): | ||
27 | if self.fd: | ||
28 | return | ||
29 | |||
30 | res = await self.system_bus.call(Message( | ||
31 | destination='org.freedesktop.login1', | ||
32 | path='/org/freedesktop/login1', | ||
33 | interface='org.freedesktop.login1.Manager', | ||
34 | member='Inhibit', | ||
35 | signature='ssss', | ||
36 | body=[ | ||
37 | "handle-lid-switch", | ||
38 | "waybar-systemd-inhibit", | ||
39 | "User request", | ||
40 | "block", | ||
41 | ], | ||
42 | )) | ||
43 | self.fd = res.unix_fds[res.body[0]] | ||
44 | self.emit_properties_changed({'IsAcquired': True}) | ||
45 | |||
46 | @method() | ||
47 | async def ToggleBlock(self): | ||
48 | if self.fd: | ||
49 | self.Release() | ||
50 | else: | ||
51 | await self.Acquire() | ||
52 | |||
53 | @dbus_property(access=PropertyAccess.READ) | ||
54 | def IsAcquired(self) -> 'b': | ||
55 | return self.fd is not None | ||
56 | |||
57 | |||
58 | @click.command() | ||
59 | async def main(): | ||
60 | system_bus = await MessageBus(bus_type=BusType.SYSTEM, negotiate_unix_fd=True).connect() | ||
61 | session_bus = await MessageBus(bus_type=BusType.SESSION).connect() | ||
62 | |||
63 | introspection = await system_bus.introspect('org.freedesktop.login1', '/org/freedesktop/login1') | ||
64 | obj = system_bus.get_proxy_object('org.freedesktop.login1', '/org/freedesktop/login1', introspection) | ||
65 | logind = obj.get_interface('org.freedesktop.login1.Manager') | ||
66 | properties = obj.get_interface('org.freedesktop.DBus.Properties') | ||
67 | |||
68 | def is_blocked_logind(what: str): | ||
69 | return "handle-lid-switch" in what.split(':') | ||
70 | |||
71 | def print_state(is_blocked: bool, is_acquired: bool = False): | ||
72 | icon = "󰌢" if is_blocked else "󰛧" | ||
73 | text = f"<span font=\"Symbols Nerd Font Mono\">{icon}</span>" | ||
74 | if is_acquired: | ||
75 | text = f"<span color=\"#f28a21\">{text}</span>" | ||
76 | elif is_blocked: | ||
77 | text = f"<span color=\"#ffffff\">{text}</span>" | ||
78 | print(json.dumps({'text': text, 'tooltip': ("Manually inhibited" if is_acquired else None)}, separators=(',', ':')), flush=True) | ||
79 | |||
80 | print_state(is_blocked_logind(await logind.get_block_inhibited())) | ||
81 | |||
82 | async def get_inhibit(): | ||
83 | introspection = await session_bus.introspect('li.yggdrasil.WaybarSystemdInhibit', '/li/yggdrasil/WaybarSystemdInhibit') | ||
84 | return session_bus.get_proxy_object('li.yggdrasil.WaybarSystemdInhibit', '/li/yggdrasil/WaybarSystemdInhibit', introspection) | ||
85 | |||
86 | async def on_logind_properties_changed(interface_name, changed_properties, invalidated_properties): | ||
87 | if 'BlockInhibited' not in changed_properties: | ||
88 | return | ||
89 | |||
90 | properties = (await get_inhibit()).get_interface('li.yggdrasil.WaybarSystemdInhibit') | ||
91 | |||
92 | print_state(is_blocked_logind(changed_properties['BlockInhibited'].value), await properties.get_is_acquired()) | ||
93 | |||
94 | properties.on_properties_changed(on_logind_properties_changed) | ||
95 | |||
96 | session_bus.export('/li/yggdrasil/WaybarSystemdInhibit', BlockInterface(system_bus, logind)) | ||
97 | await session_bus.request_name('li.yggdrasil.WaybarSystemdInhibit') | ||
98 | |||
99 | properties = (await get_inhibit()).get_interface('org.freedesktop.DBus.Properties') | ||
100 | |||
101 | async def on_inhibit_properties_changed(interface_name, changed_properties, invalidated_properties): | ||
102 | if 'IsAcquired' not in changed_properties: | ||
103 | return | ||
104 | |||
105 | print_state(is_blocked_logind(await logind.get_block_inhibited()), changed_properties['IsAcquired'].value) | ||
106 | |||
107 | properties.on_properties_changed(on_inhibit_properties_changed) | ||
108 | |||
109 | await session_bus.wait_for_disconnect() | ||
110 | |||
111 | @click.command() | ||
112 | async def toggle(): | ||
113 | session_bus = await MessageBus(bus_type=BusType.SESSION).connect() | ||
114 | introspection = await session_bus.introspect('li.yggdrasil.WaybarSystemdInhibit', '/li/yggdrasil/WaybarSystemdInhibit') | ||
115 | obj = session_bus.get_proxy_object('li.yggdrasil.WaybarSystemdInhibit', '/li/yggdrasil/WaybarSystemdInhibit', introspection) | ||
116 | interface = obj.get_interface('li.yggdrasil.WaybarSystemdInhibit') | ||
117 | await interface.call_toggle_block() | ||
diff --git a/overlays/worktime/.envrc b/overlays/worktime/.envrc new file mode 100644 index 00000000..2c909235 --- /dev/null +++ b/overlays/worktime/.envrc | |||
@@ -0,0 +1,4 @@ | |||
1 | use flake | ||
2 | |||
3 | [[ -d ".venv" ]] || ( uv venv && uv sync ) | ||
4 | . .venv/bin/activate | ||
diff --git a/overlays/worktime/.gitignore b/overlays/worktime/.gitignore new file mode 100644 index 00000000..4ccfae70 --- /dev/null +++ b/overlays/worktime/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | .venv | ||
2 | **/__pycache__ | ||
diff --git a/overlays/worktime/default.nix b/overlays/worktime/default.nix index 1d8433af..579cf7ad 100644 --- a/overlays/worktime/default.nix +++ b/overlays/worktime/default.nix | |||
@@ -1,13 +1,19 @@ | |||
1 | { prev, ... }: | 1 | { prev, final, flake, flakeInputs, ... }: |
2 | 2 | ||
3 | with prev.poetry2nix; | 3 | let |
4 | 4 | workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; | |
5 | { | 5 | pythonSet = flake.lib.pythonSet { |
6 | worktime = mkPoetryApplication { | 6 | pkgs = final; |
7 | python = prev.python312; | 7 | python = final.python312; |
8 | 8 | overlay = workspace.mkPyprojectOverlay { | |
9 | projectDir = cleanPythonSources { src = ./.; }; | 9 | sourcePreference = "wheel"; |
10 | 10 | }; | |
11 | meta.mainProgram = "worktime"; | ||
12 | }; | 11 | }; |
12 | virtualEnv = pythonSet.mkVirtualEnv "worktime" workspace.deps.default; | ||
13 | in { | ||
14 | worktime = virtualEnv.overrideAttrs (oldAttrs: { | ||
15 | meta = (oldAttrs.meta or {}) // { | ||
16 | mainProgram = "worktime"; | ||
17 | }; | ||
18 | }); | ||
13 | } | 19 | } |
diff --git a/overlays/worktime/poetry.lock b/overlays/worktime/poetry.lock deleted file mode 100644 index 7c1ca91d..00000000 --- a/overlays/worktime/poetry.lock +++ /dev/null | |||
@@ -1,284 +0,0 @@ | |||
1 | # This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. | ||
2 | |||
3 | [[package]] | ||
4 | name = "backoff" | ||
5 | version = "2.2.1" | ||
6 | description = "Function decoration for backoff and retry" | ||
7 | optional = false | ||
8 | python-versions = ">=3.7,<4.0" | ||
9 | groups = ["main"] | ||
10 | files = [ | ||
11 | {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, | ||
12 | {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, | ||
13 | ] | ||
14 | |||
15 | [[package]] | ||
16 | name = "certifi" | ||
17 | version = "2025.1.31" | ||
18 | description = "Python package for providing Mozilla's CA Bundle." | ||
19 | optional = false | ||
20 | python-versions = ">=3.6" | ||
21 | groups = ["main"] | ||
22 | files = [ | ||
23 | {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, | ||
24 | {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, | ||
25 | ] | ||
26 | |||
27 | [[package]] | ||
28 | name = "charset-normalizer" | ||
29 | version = "3.4.1" | ||
30 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." | ||
31 | optional = false | ||
32 | python-versions = ">=3.7" | ||
33 | groups = ["main"] | ||
34 | files = [ | ||
35 | {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, | ||
36 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, | ||
37 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, | ||
38 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, | ||
39 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, | ||
40 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, | ||
41 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, | ||
42 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, | ||
43 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, | ||
44 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, | ||
45 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, | ||
46 | {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, | ||
47 | {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, | ||
48 | {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, | ||
49 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, | ||
50 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, | ||
51 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, | ||
52 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, | ||
53 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, | ||
54 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, | ||
55 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, | ||
56 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, | ||
57 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, | ||
58 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, | ||
59 | {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, | ||
60 | {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, | ||
61 | {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, | ||
62 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, | ||
63 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, | ||
64 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, | ||
65 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, | ||
66 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, | ||
67 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, | ||
68 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, | ||
69 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, | ||
70 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, | ||
71 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, | ||
72 | {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, | ||
73 | {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, | ||
74 | {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, | ||
75 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, | ||
76 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, | ||
77 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, | ||
78 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, | ||
79 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, | ||
80 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, | ||
81 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, | ||
82 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, | ||
83 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, | ||
84 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, | ||
85 | {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, | ||
86 | {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, | ||
87 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, | ||
88 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, | ||
89 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, | ||
90 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, | ||
91 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, | ||
92 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, | ||
93 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, | ||
94 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, | ||
95 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, | ||
96 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, | ||
97 | {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, | ||
98 | {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, | ||
99 | {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, | ||
100 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, | ||
101 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, | ||
102 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, | ||
103 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, | ||
104 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, | ||
105 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, | ||
106 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, | ||
107 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, | ||
108 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, | ||
109 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, | ||
110 | {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, | ||
111 | {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, | ||
112 | {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, | ||
113 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, | ||
114 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, | ||
115 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, | ||
116 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, | ||
117 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, | ||
118 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, | ||
119 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, | ||
120 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, | ||
121 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, | ||
122 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, | ||
123 | {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, | ||
124 | {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, | ||
125 | {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, | ||
126 | {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, | ||
127 | ] | ||
128 | |||
129 | [[package]] | ||
130 | name = "idna" | ||
131 | version = "3.10" | ||
132 | description = "Internationalized Domain Names in Applications (IDNA)" | ||
133 | optional = false | ||
134 | python-versions = ">=3.6" | ||
135 | groups = ["main"] | ||
136 | files = [ | ||
137 | {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, | ||
138 | {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, | ||
139 | ] | ||
140 | |||
141 | [package.extras] | ||
142 | all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] | ||
143 | |||
144 | [[package]] | ||
145 | name = "jsonpickle" | ||
146 | version = "4.0.5" | ||
147 | description = "jsonpickle encodes/decodes any Python object to/from JSON" | ||
148 | optional = false | ||
149 | python-versions = ">=3.8" | ||
150 | groups = ["main"] | ||
151 | files = [ | ||
152 | {file = "jsonpickle-4.0.5-py3-none-any.whl", hash = "sha256:b4ac7d0a75ddcdfd93445737f1d36ff28768690d43e54bf5d0ddb1d915e580df"}, | ||
153 | {file = "jsonpickle-4.0.5.tar.gz", hash = "sha256:f299818b39367c361b3f26bdba827d4249ab5d383cd93144d0f94b5417aacb35"}, | ||
154 | ] | ||
155 | |||
156 | [package.extras] | ||
157 | cov = ["pytest-cov"] | ||
158 | dev = ["black", "pyupgrade"] | ||
159 | docs = ["furo", "rst.linker (>=1.9)", "sphinx (>=3.5)"] | ||
160 | packaging = ["build", "setuptools (>=61.2)", "setuptools-scm[toml] (>=6.0)", "twine"] | ||
161 | testing = ["PyYAML", "atheris (>=2.3.0,<2.4.0) ; python_version < \"3.12\"", "bson", "ecdsa", "feedparser", "gmpy2", "numpy", "pandas", "pymongo", "pytest (>=6.0,!=8.1.*)", "pytest-benchmark", "pytest-benchmark[histogram]", "pytest-checkdocs (>=1.2.3)", "pytest-enabler (>=1.0.1)", "pytest-ruff (>=0.2.1)", "scikit-learn", "scipy (>=1.9.3) ; python_version > \"3.10\"", "scipy ; python_version <= \"3.10\"", "simplejson", "sqlalchemy", "ujson"] | ||
162 | |||
163 | [[package]] | ||
164 | name = "python-dateutil" | ||
165 | version = "2.9.0.post0" | ||
166 | description = "Extensions to the standard Python datetime module" | ||
167 | optional = false | ||
168 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" | ||
169 | groups = ["main"] | ||
170 | files = [ | ||
171 | {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, | ||
172 | {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, | ||
173 | ] | ||
174 | |||
175 | [package.dependencies] | ||
176 | six = ">=1.5" | ||
177 | |||
178 | [[package]] | ||
179 | name = "pyxdg" | ||
180 | version = "0.28" | ||
181 | description = "PyXDG contains implementations of freedesktop.org standards in python." | ||
182 | optional = false | ||
183 | python-versions = "*" | ||
184 | groups = ["main"] | ||
185 | files = [ | ||
186 | {file = "pyxdg-0.28-py2.py3-none-any.whl", hash = "sha256:bdaf595999a0178ecea4052b7f4195569c1ff4d344567bccdc12dfdf02d545ab"}, | ||
187 | {file = "pyxdg-0.28.tar.gz", hash = "sha256:3267bb3074e934df202af2ee0868575484108581e6f3cb006af1da35395e88b4"}, | ||
188 | ] | ||
189 | |||
190 | [[package]] | ||
191 | name = "requests" | ||
192 | version = "2.32.3" | ||
193 | description = "Python HTTP for Humans." | ||
194 | optional = false | ||
195 | python-versions = ">=3.8" | ||
196 | groups = ["main"] | ||
197 | files = [ | ||
198 | {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, | ||
199 | {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, | ||
200 | ] | ||
201 | |||
202 | [package.dependencies] | ||
203 | certifi = ">=2017.4.17" | ||
204 | charset-normalizer = ">=2,<4" | ||
205 | idna = ">=2.5,<4" | ||
206 | urllib3 = ">=1.21.1,<3" | ||
207 | |||
208 | [package.extras] | ||
209 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] | ||
210 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] | ||
211 | |||
212 | [[package]] | ||
213 | name = "six" | ||
214 | version = "1.17.0" | ||
215 | description = "Python 2 and 3 compatibility utilities" | ||
216 | optional = false | ||
217 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" | ||
218 | groups = ["main"] | ||
219 | files = [ | ||
220 | {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, | ||
221 | {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, | ||
222 | ] | ||
223 | |||
224 | [[package]] | ||
225 | name = "tabulate" | ||
226 | version = "0.9.0" | ||
227 | description = "Pretty-print tabular data" | ||
228 | optional = false | ||
229 | python-versions = ">=3.7" | ||
230 | groups = ["main"] | ||
231 | files = [ | ||
232 | {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, | ||
233 | {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, | ||
234 | ] | ||
235 | |||
236 | [package.extras] | ||
237 | widechars = ["wcwidth"] | ||
238 | |||
239 | [[package]] | ||
240 | name = "toml" | ||
241 | version = "0.10.2" | ||
242 | description = "Python Library for Tom's Obvious, Minimal Language" | ||
243 | optional = false | ||
244 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" | ||
245 | groups = ["main"] | ||
246 | files = [ | ||
247 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, | ||
248 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, | ||
249 | ] | ||
250 | |||
251 | [[package]] | ||
252 | name = "uritools" | ||
253 | version = "4.0.3" | ||
254 | description = "URI parsing, classification and composition" | ||
255 | optional = false | ||
256 | python-versions = ">=3.7" | ||
257 | groups = ["main"] | ||
258 | files = [ | ||
259 | {file = "uritools-4.0.3-py3-none-any.whl", hash = "sha256:bae297d090e69a0451130ffba6f2f1c9477244aa0a5543d66aed2d9f77d0dd9c"}, | ||
260 | {file = "uritools-4.0.3.tar.gz", hash = "sha256:ee06a182a9c849464ce9d5fa917539aacc8edd2a4924d1b7aabeeecabcae3bc2"}, | ||
261 | ] | ||
262 | |||
263 | [[package]] | ||
264 | name = "urllib3" | ||
265 | version = "2.3.0" | ||
266 | description = "HTTP library with thread-safe connection pooling, file post, and more." | ||
267 | optional = false | ||
268 | python-versions = ">=3.9" | ||
269 | groups = ["main"] | ||
270 | files = [ | ||
271 | {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, | ||
272 | {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, | ||
273 | ] | ||
274 | |||
275 | [package.extras] | ||
276 | brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] | ||
277 | h2 = ["h2 (>=4,<5)"] | ||
278 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] | ||
279 | zstd = ["zstandard (>=0.18.0)"] | ||
280 | |||
281 | [metadata] | ||
282 | lock-version = "2.1" | ||
283 | python-versions = "^3.12" | ||
284 | content-hash = "2b335da94bf3e2d2bee7d8ca6e84cdb56e97ac29d1224d8c8dca98d93bbdcea2" | ||
diff --git a/overlays/worktime/pyproject.toml b/overlays/worktime/pyproject.toml index de4b9fd4..42da51f5 100644 --- a/overlays/worktime/pyproject.toml +++ b/overlays/worktime/pyproject.toml | |||
@@ -1,23 +1,28 @@ | |||
1 | [tool.poetry] | 1 | [project] |
2 | name = "worktime" | 2 | name = "worktime" |
3 | version = "0.1.0" | 3 | version = "1.0.0" |
4 | description = "" | 4 | requires-python = "~=3.12" |
5 | authors = ["Gregor Kleen <gkleen@yggdrasil.li>"] | 5 | dependencies = [ |
6 | "pyxdg>=0.28,<0.29", | ||
7 | "python-dateutil>=2.9.0.post0,<3", | ||
8 | "uritools>=4.0.3,<5", | ||
9 | "requests>=2.32.3,<3", | ||
10 | "tabulate>=0.9.0,<0.10", | ||
11 | "toml>=0.10.2,<0.11", | ||
12 | "jsonpickle>=4.0.5,<5", | ||
13 | "frozendict>=2.4.6", | ||
14 | "atomicwriter>=0.2.5", | ||
15 | "desktop-notify>=1.3.3", | ||
16 | ] | ||
6 | 17 | ||
7 | [tool.poetry.dependencies] | 18 | [project.scripts] |
8 | python = "^3.12" | ||
9 | pyxdg = "^0.28" | ||
10 | python-dateutil = "^2.9.0.post0" | ||
11 | uritools = "^4.0.3" | ||
12 | requests = "^2.32.3" | ||
13 | tabulate = "^0.9.0" | ||
14 | backoff = "^2.2.1" | ||
15 | toml = "^0.10.2" | ||
16 | jsonpickle = "^4.0.5" | ||
17 | |||
18 | [tool.poetry.scripts] | ||
19 | worktime = "worktime.__main__:main" | 19 | worktime = "worktime.__main__:main" |
20 | worktime-ui = "worktime.__main__:ui" | ||
21 | worktime-stop = "worktime.__main__:stop" | ||
20 | 22 | ||
21 | [build-system] | 23 | [build-system] |
22 | requires = ["poetry-core"] | 24 | requires = ["hatchling"] |
23 | build-backend = "poetry.core.masonry.api" \ No newline at end of file | 25 | build-backend = "hatchling.build" |
26 | |||
27 | [dependency-groups] | ||
28 | dev = [] | ||
diff --git a/overlays/worktime/uv.lock b/overlays/worktime/uv.lock new file mode 100644 index 00000000..39de4ccf --- /dev/null +++ b/overlays/worktime/uv.lock | |||
@@ -0,0 +1,248 @@ | |||
1 | version = 1 | ||
2 | revision = 2 | ||
3 | requires-python = ">=3.12, <4" | ||
4 | |||
5 | [[package]] | ||
6 | name = "atomicwriter" | ||
7 | version = "0.2.5" | ||
8 | source = { registry = "https://pypi.org/simple" } | ||
9 | sdist = { url = "https://files.pythonhosted.org/packages/50/b4/dd04e186eb244d1ed84b1d0ebfba19ddc7f8886b98e345aaca4208b031d2/atomicwriter-0.2.5.tar.gz", hash = "sha256:5ced6afb0579377a13e191b17a16115e14c30ec00e6c38b60403f58235a867af", size = 64990, upload-time = "2025-05-24T20:35:42.538Z" } | ||
10 | wheels = [ | ||
11 | { url = "https://files.pythonhosted.org/packages/99/7c/672a0de09b0b355a2ffa521ef25cf106f1984823379dee37f7305fdc1774/atomicwriter-0.2.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c1fab874e62ebe96f1af0e965dc1e92c4c1ef2e2e9612a444371b8fc751ec43", size = 234141, upload-time = "2025-05-24T20:34:32.74Z" }, | ||
12 | { url = "https://files.pythonhosted.org/packages/b9/0c/e1c5bad033284c212c0a77121b48dd4147f80e9a7cd82a9d2ce0a2160901/atomicwriter-0.2.5-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:8dbb67cc730be7d6bdfd5e991271bc17052be8fb2e4fa27854b47d8a76d36349", size = 245788, upload-time = "2025-05-24T20:34:33.897Z" }, | ||
13 | { url = "https://files.pythonhosted.org/packages/f4/d3/7036e203cc5fc4c49bf916b4ba158e0d2779de127afad5963edd7e3b9400/atomicwriter-0.2.5-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:a4e7f81932839c738425dc96ad98e4a7511b740cd3d75f480bfabbcf8e6f7eae", size = 260428, upload-time = "2025-05-24T20:34:35.533Z" }, | ||
14 | { url = "https://files.pythonhosted.org/packages/e5/b9/9a4d235a8d67fb442302dc0f3ea2394b7bd994bfc99b1dc0f744c7852418/atomicwriter-0.2.5-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:de37a3a5d1b57b719cfb0b81a11cab2114acfdc2c36051bf0af72d05eb644411", size = 263648, upload-time = "2025-05-24T20:34:36.72Z" }, | ||
15 | { url = "https://files.pythonhosted.org/packages/71/7c/32d4ddad53375de42f3e972bb0633ec76f2c31772f2e508479d4788651d9/atomicwriter-0.2.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b925e55750092fd482565b6068b8c8366fd79de526681af9e58eb209f0deeca", size = 323775, upload-time = "2025-05-24T20:34:37.968Z" }, | ||
16 | { url = "https://files.pythonhosted.org/packages/06/fe/6a226368a3f7ea30001fbd165f6a97f28c8f1a884896357b3d694983f5d2/atomicwriter-0.2.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:538f78f25e01584535782397211c66b8b3c9de90c2d1fc01a668ddce73dd0cb2", size = 340819, upload-time = "2025-05-24T20:34:39.63Z" }, | ||
17 | { url = "https://files.pythonhosted.org/packages/92/95/b035b2296c483fde5392c629e0b6e3844eba6e54ea965c4b8827379b0893/atomicwriter-0.2.5-cp312-cp312-win_amd64.whl", hash = "sha256:1d2d49a1b94ea7b289be9f7134d756bfb0bbf53eb0e58411334ed1b9958abe5e", size = 152789, upload-time = "2025-05-24T20:34:40.905Z" }, | ||
18 | { url = "https://files.pythonhosted.org/packages/da/25/caa0959ae8ce24763e24e1f45be6cb897414545d224a155f929d496d6812/atomicwriter-0.2.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f5490fd5bec378509521f7c2a19a64031a0de07d368d76733c3f76a0b9f026b", size = 233830, upload-time = "2025-05-24T20:34:42.532Z" }, | ||
19 | { url = "https://files.pythonhosted.org/packages/d2/76/3c41bfd4fd74bc63bec29f05a806a767258eea7cf151496b4ab015cb5323/atomicwriter-0.2.5-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:a4dada83ff1255c7e640363cc2a4399ab9a822d4dbc9c18f55bbf0c8b12ce056", size = 245461, upload-time = "2025-05-24T20:34:44.454Z" }, | ||
20 | { url = "https://files.pythonhosted.org/packages/c3/1e/5512dbdfdc3f4ab12f5923c50ae4765cc2fc65a9f112bb9dccbcbe60b395/atomicwriter-0.2.5-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:ef2cf15e67513f05ad37d4cec48e403982c6b3c07f491472effd76d2157de7e2", size = 259892, upload-time = "2025-05-24T20:34:45.688Z" }, | ||
21 | { url = "https://files.pythonhosted.org/packages/e5/1d/2382b6cacb119115828eb519697a555900bcfdb062efeb0f82603295402d/atomicwriter-0.2.5-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:73618f74c3c5f5401d3da0a3cd3043f23de5b6bb4a3d85bc580940a441355d25", size = 263125, upload-time = "2025-05-24T20:34:47.205Z" }, | ||
22 | { url = "https://files.pythonhosted.org/packages/07/d7/c4d68386161870db4a8d0452f0655a19902fa435b749c12e6ef800e89b19/atomicwriter-0.2.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbd5eda80710ddac7aefb421c79cef6b905852a827e764f0f12fcbaa88919f7a", size = 323503, upload-time = "2025-05-24T20:34:48.417Z" }, | ||
23 | { url = "https://files.pythonhosted.org/packages/b7/08/0fc03c0736ab8466e1b47a3ee17a528da18019cff93b7c4c2b33df82c19e/atomicwriter-0.2.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4776aaca40bc3040c3716c2adad74625c42285083ff31e8bf24a95315225c7b", size = 340156, upload-time = "2025-05-24T20:34:50.389Z" }, | ||
24 | { url = "https://files.pythonhosted.org/packages/fa/09/7ba888cf4d90bcabd9e82db3bdb9de50e4ef072e0ea0d375cd1931b79349/atomicwriter-0.2.5-cp313-cp313-win_amd64.whl", hash = "sha256:225ed1fbfa1996d9b0b2252f8a5d81263e51cbc797086d830f488c35b1d2ab42", size = 152274, upload-time = "2025-05-24T20:34:51.785Z" }, | ||
25 | { url = "https://files.pythonhosted.org/packages/2a/70/07d2ba2e0a126cfecfbfed46baf599c9e2155f4c8338fed4d3ae0041b133/atomicwriter-0.2.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:63b55982cfa47232f179689933bf003eefb2bd33464235883ed3ce7322cf38f3", size = 232879, upload-time = "2025-05-24T20:34:53.195Z" }, | ||
26 | { url = "https://files.pythonhosted.org/packages/f6/4d/397eb5435917135df93b339d849884bb1125896b1e15163c5244aa590336/atomicwriter-0.2.5-cp313-cp313t-macosx_11_0_x86_64.whl", hash = "sha256:e33f40b2a27f8831beeabb485923acb6dd067cc70bba1a63096749b3dc4747ff", size = 244386, upload-time = "2025-05-24T20:34:54.852Z" }, | ||
27 | { url = "https://files.pythonhosted.org/packages/8b/01/73f0b683fa55e61dd29d30e48e9a75ddb049e6dad0ac4ae1a29dbc05f21e/atomicwriter-0.2.5-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c646e115e88147d71f845a005fc53910f22c4dc65bd634768cb90b7f34259359", size = 258255, upload-time = "2025-05-24T20:34:56.046Z" }, | ||
28 | { url = "https://files.pythonhosted.org/packages/4b/19/692387c1fb1b8714a9b2fab99a58850fd4136bed988814c8ff74d0c8de02/atomicwriter-0.2.5-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:47f974e986ff6514351c3ea75041009a514be0c34c225c062b0ad8a28ec9c0a3", size = 261768, upload-time = "2025-05-24T20:34:57.795Z" }, | ||
29 | { url = "https://files.pythonhosted.org/packages/3e/f2/4d466f52ee635cc54011713272f302584c6d1ce612c331d9989fa6fa672f/atomicwriter-0.2.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e1db8b9004cd3f628166e83b25eb814b82345f9d6bc15e99b6d201c355455b45", size = 321975, upload-time = "2025-05-24T20:34:59.45Z" }, | ||
30 | { url = "https://files.pythonhosted.org/packages/84/ad/0189ad9783ca6609df47e06cc0cd22866a8073d46478f59c6ab3ec13e0fb/atomicwriter-0.2.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a7da4a114121ab865663578b801a0520b2b518d4591af0bd294f6aac0dad243b", size = 338946, upload-time = "2025-05-24T20:35:01.501Z" }, | ||
31 | { url = "https://files.pythonhosted.org/packages/94/79/2c4d8f75eeb09192cf572957f031271998f3c985fabd79d513fff66ac715/atomicwriter-0.2.5-cp313-cp313t-win_amd64.whl", hash = "sha256:7aab4b3956cc17219e7e4da76e8a1bceb3d3aeaf03234f89b90e234a2adcf27b", size = 151571, upload-time = "2025-05-24T20:35:02.747Z" }, | ||
32 | { url = "https://files.pythonhosted.org/packages/32/19/d6a686d189c3577e7f08b33df398b959c24bf74b3cec34359104db1a24ff/atomicwriter-0.2.5-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8d0fccac2dfe5d884d97edbda28be9c16d55faee9bdf66f53a99384ac387cc43", size = 239320, upload-time = "2025-05-24T20:35:04.028Z" }, | ||
33 | { url = "https://files.pythonhosted.org/packages/8e/35/35571a4eed57816c3b5fdbefcb15f38563fbe4f3a4a7d1588c8ef899afaf/atomicwriter-0.2.5-cp39-abi3-macosx_11_0_x86_64.whl", hash = "sha256:6583c24333508839db2156d895cbbb5cd3ff20d4f9c698e341435e5b35990eaa", size = 250818, upload-time = "2025-05-24T20:35:05.21Z" }, | ||
34 | { url = "https://files.pythonhosted.org/packages/81/d9/145093630bc25f115a49d32d9ef66745f5cdef787492d77fd27e74d20389/atomicwriter-0.2.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:136a9902ae3f1c0cb262a07dd3ac85069d71f8b11347cd740030567e67d611aa", size = 265796, upload-time = "2025-05-24T20:35:06.388Z" }, | ||
35 | { url = "https://files.pythonhosted.org/packages/58/32/d1881adade2ebc70aa9dbb61cadabc2c00cfa99a7a5d6ba48f44e279056f/atomicwriter-0.2.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0b6830434b6a49c19473c3f3975dfa0a87dec95bee81297f7393e378f9a0b82f", size = 269378, upload-time = "2025-05-24T20:35:07.578Z" }, | ||
36 | { url = "https://files.pythonhosted.org/packages/93/f5/2661ea763784a4991c4c7be5c932a468937bd1d4618b833a63ec638a3b76/atomicwriter-0.2.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53095a01891a2901aa04c10c8de52c0ba41e0d8a4a1893318cf34ccbdbde00b7", size = 328167, upload-time = "2025-05-24T20:35:08.764Z" }, | ||
37 | { url = "https://files.pythonhosted.org/packages/ec/bc/e3aa521671a589bee9662d3e2108e4835a5d80e6da76e4d05d98d1c78005/atomicwriter-0.2.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ecf4dc3983bb1f28b21cb09c2d96b6936d8864c559dcf151b57813cb1eae998b", size = 347153, upload-time = "2025-05-24T20:35:10.507Z" }, | ||
38 | { url = "https://files.pythonhosted.org/packages/59/b7/e190383e7240b1f247c6df9bc6667db8df10190cd0bb2dba8ea6bd704ea4/atomicwriter-0.2.5-cp39-abi3-win_amd64.whl", hash = "sha256:92cff264a20364301ab341b332fd0112866870b8cb35caf99a3f3fee0e6c19e8", size = 156374, upload-time = "2025-05-24T20:35:11.716Z" }, | ||
39 | ] | ||
40 | |||
41 | [[package]] | ||
42 | name = "certifi" | ||
43 | version = "2025.1.31" | ||
44 | source = { registry = "https://pypi.org/simple" } | ||
45 | sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577, upload-time = "2025-01-31T02:16:47.166Z" } | ||
46 | wheels = [ | ||
47 | { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393, upload-time = "2025-01-31T02:16:45.015Z" }, | ||
48 | ] | ||
49 | |||
50 | [[package]] | ||
51 | name = "charset-normalizer" | ||
52 | version = "3.4.1" | ||
53 | source = { registry = "https://pypi.org/simple" } | ||
54 | sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188, upload-time = "2024-12-24T18:12:35.43Z" } | ||
55 | wheels = [ | ||
56 | { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105, upload-time = "2024-12-24T18:10:38.83Z" }, | ||
57 | { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404, upload-time = "2024-12-24T18:10:44.272Z" }, | ||
58 | { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423, upload-time = "2024-12-24T18:10:45.492Z" }, | ||
59 | { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184, upload-time = "2024-12-24T18:10:47.898Z" }, | ||
60 | { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268, upload-time = "2024-12-24T18:10:50.589Z" }, | ||
61 | { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601, upload-time = "2024-12-24T18:10:52.541Z" }, | ||
62 | { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098, upload-time = "2024-12-24T18:10:53.789Z" }, | ||
63 | { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520, upload-time = "2024-12-24T18:10:55.048Z" }, | ||
64 | { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852, upload-time = "2024-12-24T18:10:57.647Z" }, | ||
65 | { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488, upload-time = "2024-12-24T18:10:59.43Z" }, | ||
66 | { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192, upload-time = "2024-12-24T18:11:00.676Z" }, | ||
67 | { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550, upload-time = "2024-12-24T18:11:01.952Z" }, | ||
68 | { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785, upload-time = "2024-12-24T18:11:03.142Z" }, | ||
69 | { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698, upload-time = "2024-12-24T18:11:05.834Z" }, | ||
70 | { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162, upload-time = "2024-12-24T18:11:07.064Z" }, | ||
71 | { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263, upload-time = "2024-12-24T18:11:08.374Z" }, | ||
72 | { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966, upload-time = "2024-12-24T18:11:09.831Z" }, | ||
73 | { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992, upload-time = "2024-12-24T18:11:12.03Z" }, | ||
74 | { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162, upload-time = "2024-12-24T18:11:13.372Z" }, | ||
75 | { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972, upload-time = "2024-12-24T18:11:14.628Z" }, | ||
76 | { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095, upload-time = "2024-12-24T18:11:17.672Z" }, | ||
77 | { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668, upload-time = "2024-12-24T18:11:18.989Z" }, | ||
78 | { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073, upload-time = "2024-12-24T18:11:21.507Z" }, | ||
79 | { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732, upload-time = "2024-12-24T18:11:22.774Z" }, | ||
80 | { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391, upload-time = "2024-12-24T18:11:24.139Z" }, | ||
81 | { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702, upload-time = "2024-12-24T18:11:26.535Z" }, | ||
82 | { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767, upload-time = "2024-12-24T18:12:32.852Z" }, | ||
83 | ] | ||
84 | |||
85 | [[package]] | ||
86 | name = "dbus-next" | ||
87 | version = "0.2.3" | ||
88 | source = { registry = "https://pypi.org/simple" } | ||
89 | sdist = { url = "https://files.pythonhosted.org/packages/ce/45/6a40fbe886d60a8c26f480e7d12535502b5ba123814b3b9a0b002ebca198/dbus_next-0.2.3.tar.gz", hash = "sha256:f4eae26909332ada528c0a3549dda8d4f088f9b365153952a408e28023a626a5", size = 71112, upload-time = "2021-07-25T22:11:28.398Z" } | ||
90 | wheels = [ | ||
91 | { url = "https://files.pythonhosted.org/packages/d2/fc/c0a3f4c4eaa5a22fbef91713474666e13d0ea2a69c84532579490a9f2cc8/dbus_next-0.2.3-py3-none-any.whl", hash = "sha256:58948f9aff9db08316734c0be2a120f6dc502124d9642f55e90ac82ffb16a18b", size = 57885, upload-time = "2021-07-25T22:11:25.466Z" }, | ||
92 | ] | ||
93 | |||
94 | [[package]] | ||
95 | name = "desktop-notify" | ||
96 | version = "1.3.3" | ||
97 | source = { registry = "https://pypi.org/simple" } | ||
98 | dependencies = [ | ||
99 | { name = "dbus-next" }, | ||
100 | ] | ||
101 | sdist = { url = "https://files.pythonhosted.org/packages/7a/d8/7ae5779257f5f1aa0a2d50c02d70b29522bd414692f3d3bd18ef119fe82d/desktop-notify-1.3.3.tar.gz", hash = "sha256:62934ad1f72f292f9a3af5ffe45af32814af18c396c00369385540c72bf08077", size = 7828, upload-time = "2021-01-03T16:46:36.483Z" } | ||
102 | wheels = [ | ||
103 | { url = "https://files.pythonhosted.org/packages/0a/cd/a7e3bd0262f3e8a9272fd24d0193e24dad7cb4e4edd27da48e74b5523e59/desktop_notify-1.3.3-py3-none-any.whl", hash = "sha256:8ad7ecc3a9a603dd5fa3cdc11cc6265cfbc7f6df9d8ed240f4663f43ef0de37a", size = 9937, upload-time = "2021-01-03T16:46:35.157Z" }, | ||
104 | ] | ||
105 | |||
106 | [[package]] | ||
107 | name = "frozendict" | ||
108 | version = "2.4.6" | ||
109 | source = { registry = "https://pypi.org/simple" } | ||
110 | sdist = { url = "https://files.pythonhosted.org/packages/bb/59/19eb300ba28e7547538bdf603f1c6c34793240a90e1a7b61b65d8517e35e/frozendict-2.4.6.tar.gz", hash = "sha256:df7cd16470fbd26fc4969a208efadc46319334eb97def1ddf48919b351192b8e", size = 316416, upload-time = "2024-10-13T12:15:32.449Z" } | ||
111 | wheels = [ | ||
112 | { url = "https://files.pythonhosted.org/packages/04/13/d9839089b900fa7b479cce495d62110cddc4bd5630a04d8469916c0e79c5/frozendict-2.4.6-py311-none-any.whl", hash = "sha256:d065db6a44db2e2375c23eac816f1a022feb2fa98cbb50df44a9e83700accbea", size = 16148, upload-time = "2024-10-13T12:15:26.839Z" }, | ||
113 | { url = "https://files.pythonhosted.org/packages/ba/d0/d482c39cee2ab2978a892558cf130681d4574ea208e162da8958b31e9250/frozendict-2.4.6-py312-none-any.whl", hash = "sha256:49344abe90fb75f0f9fdefe6d4ef6d4894e640fadab71f11009d52ad97f370b9", size = 16146, upload-time = "2024-10-13T12:15:28.16Z" }, | ||
114 | { url = "https://files.pythonhosted.org/packages/a5/8e/b6bf6a0de482d7d7d7a2aaac8fdc4a4d0bb24a809f5ddd422aa7060eb3d2/frozendict-2.4.6-py313-none-any.whl", hash = "sha256:7134a2bb95d4a16556bb5f2b9736dceb6ea848fa5b6f3f6c2d6dba93b44b4757", size = 16146, upload-time = "2024-10-13T12:15:29.495Z" }, | ||
115 | ] | ||
116 | |||
117 | [[package]] | ||
118 | name = "idna" | ||
119 | version = "3.10" | ||
120 | source = { registry = "https://pypi.org/simple" } | ||
121 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } | ||
122 | wheels = [ | ||
123 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, | ||
124 | ] | ||
125 | |||
126 | [[package]] | ||
127 | name = "jsonpickle" | ||
128 | version = "4.0.5" | ||
129 | source = { registry = "https://pypi.org/simple" } | ||
130 | sdist = { url = "https://files.pythonhosted.org/packages/d6/33/4bda317ab294722fcdfff8f63aab74af9fda3675a4652d984a101aa7587e/jsonpickle-4.0.5.tar.gz", hash = "sha256:f299818b39367c361b3f26bdba827d4249ab5d383cd93144d0f94b5417aacb35", size = 315661, upload-time = "2025-03-29T19:22:56.92Z" } | ||
131 | wheels = [ | ||
132 | { url = "https://files.pythonhosted.org/packages/dc/1b/0e79cf115e0f54f1e8f56effb6ffd2ef8f92e9c324d692ede660067f1bfe/jsonpickle-4.0.5-py3-none-any.whl", hash = "sha256:b4ac7d0a75ddcdfd93445737f1d36ff28768690d43e54bf5d0ddb1d915e580df", size = 46382, upload-time = "2025-03-29T19:22:54.252Z" }, | ||
133 | ] | ||
134 | |||
135 | [[package]] | ||
136 | name = "python-dateutil" | ||
137 | version = "2.9.0.post0" | ||
138 | source = { registry = "https://pypi.org/simple" } | ||
139 | dependencies = [ | ||
140 | { name = "six" }, | ||
141 | ] | ||
142 | sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } | ||
143 | wheels = [ | ||
144 | { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, | ||
145 | ] | ||
146 | |||
147 | [[package]] | ||
148 | name = "pyxdg" | ||
149 | version = "0.28" | ||
150 | source = { registry = "https://pypi.org/simple" } | ||
151 | sdist = { url = "https://files.pythonhosted.org/packages/b0/25/7998cd2dec731acbd438fbf91bc619603fc5188de0a9a17699a781840452/pyxdg-0.28.tar.gz", hash = "sha256:3267bb3074e934df202af2ee0868575484108581e6f3cb006af1da35395e88b4", size = 77776, upload-time = "2022-06-05T11:35:01Z" } | ||
152 | wheels = [ | ||
153 | { url = "https://files.pythonhosted.org/packages/e5/8d/cf41b66a8110670e3ad03dab9b759704eeed07fa96e90fdc0357b2ba70e2/pyxdg-0.28-py2.py3-none-any.whl", hash = "sha256:bdaf595999a0178ecea4052b7f4195569c1ff4d344567bccdc12dfdf02d545ab", size = 49520, upload-time = "2022-06-05T11:34:58.832Z" }, | ||
154 | ] | ||
155 | |||
156 | [[package]] | ||
157 | name = "requests" | ||
158 | version = "2.32.3" | ||
159 | source = { registry = "https://pypi.org/simple" } | ||
160 | dependencies = [ | ||
161 | { name = "certifi" }, | ||
162 | { name = "charset-normalizer" }, | ||
163 | { name = "idna" }, | ||
164 | { name = "urllib3" }, | ||
165 | ] | ||
166 | sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" } | ||
167 | wheels = [ | ||
168 | { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" }, | ||
169 | ] | ||
170 | |||
171 | [[package]] | ||
172 | name = "six" | ||
173 | version = "1.17.0" | ||
174 | source = { registry = "https://pypi.org/simple" } | ||
175 | sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } | ||
176 | wheels = [ | ||
177 | { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, | ||
178 | ] | ||
179 | |||
180 | [[package]] | ||
181 | name = "tabulate" | ||
182 | version = "0.9.0" | ||
183 | source = { registry = "https://pypi.org/simple" } | ||
184 | sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090, upload-time = "2022-10-06T17:21:48.54Z" } | ||
185 | wheels = [ | ||
186 | { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload-time = "2022-10-06T17:21:44.262Z" }, | ||
187 | ] | ||
188 | |||
189 | [[package]] | ||
190 | name = "toml" | ||
191 | version = "0.10.2" | ||
192 | source = { registry = "https://pypi.org/simple" } | ||
193 | sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } | ||
194 | wheels = [ | ||
195 | { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, | ||
196 | ] | ||
197 | |||
198 | [[package]] | ||
199 | name = "uritools" | ||
200 | version = "4.0.3" | ||
201 | source = { registry = "https://pypi.org/simple" } | ||
202 | sdist = { url = "https://files.pythonhosted.org/packages/d3/43/4182fb2a03145e6d38698e38b49114ce59bc8c79063452eb585a58f8ce78/uritools-4.0.3.tar.gz", hash = "sha256:ee06a182a9c849464ce9d5fa917539aacc8edd2a4924d1b7aabeeecabcae3bc2", size = 24184, upload-time = "2024-05-28T18:07:45.194Z" } | ||
203 | wheels = [ | ||
204 | { url = "https://files.pythonhosted.org/packages/e6/17/5a4510d9ca9cc8be217ce359eb54e693dca81cf4d442308b282d5131b17d/uritools-4.0.3-py3-none-any.whl", hash = "sha256:bae297d090e69a0451130ffba6f2f1c9477244aa0a5543d66aed2d9f77d0dd9c", size = 10304, upload-time = "2024-05-28T18:07:42.731Z" }, | ||
205 | ] | ||
206 | |||
207 | [[package]] | ||
208 | name = "urllib3" | ||
209 | version = "2.3.0" | ||
210 | source = { registry = "https://pypi.org/simple" } | ||
211 | sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268, upload-time = "2024-12-22T07:47:30.032Z" } | ||
212 | wheels = [ | ||
213 | { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369, upload-time = "2024-12-22T07:47:28.074Z" }, | ||
214 | ] | ||
215 | |||
216 | [[package]] | ||
217 | name = "worktime" | ||
218 | version = "1.0.0" | ||
219 | source = { editable = "." } | ||
220 | dependencies = [ | ||
221 | { name = "atomicwriter" }, | ||
222 | { name = "desktop-notify" }, | ||
223 | { name = "frozendict" }, | ||
224 | { name = "jsonpickle" }, | ||
225 | { name = "python-dateutil" }, | ||
226 | { name = "pyxdg" }, | ||
227 | { name = "requests" }, | ||
228 | { name = "tabulate" }, | ||
229 | { name = "toml" }, | ||
230 | { name = "uritools" }, | ||
231 | ] | ||
232 | |||
233 | [package.metadata] | ||
234 | requires-dist = [ | ||
235 | { name = "atomicwriter", specifier = ">=0.2.5" }, | ||
236 | { name = "desktop-notify", specifier = ">=1.3.3" }, | ||
237 | { name = "frozendict", specifier = ">=2.4.6" }, | ||
238 | { name = "jsonpickle", specifier = ">=4.0.5,<5" }, | ||
239 | { name = "python-dateutil", specifier = ">=2.9.0.post0,<3" }, | ||
240 | { name = "pyxdg", specifier = ">=0.28,<0.29" }, | ||
241 | { name = "requests", specifier = ">=2.32.3,<3" }, | ||
242 | { name = "tabulate", specifier = ">=0.9.0,<0.10" }, | ||
243 | { name = "toml", specifier = ">=0.10.2,<0.11" }, | ||
244 | { name = "uritools", specifier = ">=4.0.3,<5" }, | ||
245 | ] | ||
246 | |||
247 | [package.metadata.requires-dev] | ||
248 | dev = [] | ||
diff --git a/overlays/worktime/worktime/__main__.py b/overlays/worktime/worktime/__main__.py index 4eee5dc2..bf24bbec 100755 --- a/overlays/worktime/worktime/__main__.py +++ b/overlays/worktime/worktime/__main__.py | |||
@@ -1,10 +1,12 @@ | |||
1 | import requests | 1 | import requests |
2 | from requests.exceptions import HTTPError | 2 | from requests.exceptions import HTTPError |
3 | from requests.auth import HTTPBasicAuth | 3 | from requests.auth import HTTPBasicAuth |
4 | from requests.adapters import HTTPAdapter, Retry | ||
4 | from datetime import * | 5 | from datetime import * |
5 | from xdg import BaseDirectory | 6 | from xdg import BaseDirectory |
6 | import toml | 7 | import toml |
7 | from uritools import (uricompose) | 8 | from uritools import uricompose |
9 | from urllib.parse import urljoin | ||
8 | 10 | ||
9 | from inspect import signature | 11 | from inspect import signature |
10 | 12 | ||
@@ -27,77 +29,76 @@ from sys import stderr, stdout | |||
27 | 29 | ||
28 | from tabulate import tabulate | 30 | from tabulate import tabulate |
29 | 31 | ||
30 | from itertools import groupby, count | 32 | from itertools import groupby, count, islice |
31 | from functools import cache, partial | 33 | from functools import cache, partial |
32 | 34 | ||
33 | import backoff | ||
34 | |||
35 | from pathlib import Path | 35 | from pathlib import Path |
36 | 36 | ||
37 | from collections import defaultdict | 37 | from collections import defaultdict |
38 | from collections.abc import Iterable, Generator | ||
39 | from typing import Any | ||
38 | 40 | ||
39 | import jsonpickle | 41 | import jsonpickle |
40 | from hashlib import blake2s | 42 | from hashlib import blake2s |
41 | import json | 43 | import json |
42 | 44 | ||
43 | class TogglAPISection(Enum): | 45 | import asyncio |
44 | TOGGL = '/api/v9' | 46 | |
45 | REPORTS = '/reports/api/v2' | 47 | from frozendict import frozendict |
46 | 48 | from contextlib import closing | |
47 | class TogglAPIError(Exception): | 49 | import os |
48 | def __init__(self, response, *, http_error=None): | 50 | from time import clock_gettime_ns, CLOCK_MONOTONIC |
49 | self.http_error = http_error | 51 | from atomicwriter import AtomicWriter |
50 | self.response = response | 52 | import desktop_notify.aio as notify |
51 | 53 | ||
52 | def __str__(self): | 54 | class BearerAuth(requests.auth.AuthBase): |
53 | if not self.http_error is None: | 55 | def __init__(self, token): |
54 | return str(self.http_error) | 56 | self.token = token |
55 | else: | 57 | def __call__(self, r): |
56 | return self.response.text | 58 | r.headers["authorization"] = "Bearer " + self.token |
57 | 59 | return r | |
58 | class TogglAPI(object): | 60 | |
59 | def __init__(self, api_token, workspace_id, client_ids): | 61 | class KimaiSession(requests.Session): |
60 | self._api_token = api_token | 62 | def __init__(self, base_url: str, api_token: str): |
61 | self._workspace_id = workspace_id | 63 | super().__init__() |
62 | self._client_ids = set(map(int, client_ids.split(','))) if client_ids else None | 64 | self.base_url = base_url |
63 | 65 | self.auth = BearerAuth(api_token) | |
64 | def _make_url(self, api=TogglAPISection.TOGGL, section=['me', 'time_entries', 'current'], params={}): | 66 | retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[500, 502, 503, 504]) |
65 | if api is TogglAPISection.REPORTS: | 67 | super().mount(base_url, HTTPAdapter(max_retries=retries)) |
66 | params.update({'user_agent': 'worktime', 'workspace_id': self._workspace_id}) | 68 | |
67 | 69 | def request(self, method, url, *args, **kwargs): | |
68 | api_path = api.value | 70 | joined_url = urljoin(self.base_url, url) |
69 | section_path = '/'.join(section) | 71 | return super().request(method, joined_url, *args, headers = {'Accept': 'application/json'} | (kwargs['headers'] if 'headers' in kwargs else {}), **{k: v for k, v in kwargs.items() if k not in ['headers']}) |
70 | uri = uricompose(scheme='https', host='api.track.toggl.com', path=f"{api_path}/{section_path}", query=params) | 72 | |
71 | 73 | class KimaiAPI(object): | |
72 | return uri | 74 | def __init__(self, base_url: str, api_token: str, clients: Iterable[str]): |
73 | 75 | self._session = KimaiSession(base_url, api_token) | |
74 | def _query(self, url, method): | 76 | self._kimai_clients = self._session.get('/api/customers').json() |
75 | response = self._raw_query(url, method) | 77 | self._client_ids = self.resolve_clients(clients) |
76 | response.raise_for_status() | 78 | kimai_user = self._session.get('/api/users/me').json() |
77 | return response | 79 | self._tz = gettz(kimai_user['timezone']) |
78 | 80 | ||
79 | @backoff.on_predicate( | 81 | def resolve_clients(self, clients: Iterable[str]) -> frozenset[int]: |
80 | backoff.expo, | 82 | return frozenset({ client['id'] for client in self._kimai_clients if client['name'] in clients }) |
81 | factor=0.1, max_value=2, | 83 | |
82 | predicate=lambda r: r.status_code == 429, | 84 | def render_datetime(self, datetime: datetime) -> str: |
83 | max_time=10, | 85 | return datetime.astimezone(self._tz).strftime('%Y-%m-%dT%H:%M:%S') |
84 | ) | 86 | |
85 | def _raw_query(self, url, method): | 87 | def get_timesheets(self, params: dict[str, Any] = {}) -> Generator[Any]: |
86 | headers = {'content-type': 'application/json', 'accept': 'application/json'} | 88 | for page in count(start=1): |
87 | response = None | 89 | resp = self._session.get('/api/timesheets', params=params | {'size': 100, 'page': page}) |
88 | 90 | if resp.status_code == 404: | |
89 | if method == 'GET': | 91 | break |
90 | response = requests.get(url, headers=headers, auth=HTTPBasicAuth(self._api_token, 'api_token')) | 92 | yield from resp.json() |
91 | elif method == 'POST': | ||
92 | response = requests.post(url, headers=headers, auth=HTTPBasicAuth(self._api_token, 'api_token')) | ||
93 | else: | ||
94 | raise ValueError(f"Undefined HTTP method “{method}”") | ||
95 | |||
96 | return response | ||
97 | 93 | ||
98 | def entry_durations(self, start_date, *, end_date, rounding=False, client_ids): | 94 | def entry_durations(self, start_date: datetime, *, end_date: datetime, clients: Iterable[str] | None = None) -> Generator[timedelta]: |
99 | if client_ids is not None and not client_ids: | 95 | client_ids = None |
96 | if clients is not None and not clients: | ||
100 | return | 97 | return |
98 | elif clients is None: | ||
99 | client_ids = self._client_ids | ||
100 | else: | ||
101 | client_ids = self.resolve_clients(clients) | ||
101 | 102 | ||
102 | cache_dir = Path(BaseDirectory.save_cache_path('worktime')) / 'entry_durations' | 103 | cache_dir = Path(BaseDirectory.save_cache_path('worktime')) / 'entry_durations' |
103 | step = timedelta(days = 120) | 104 | step = timedelta(days = 120) |
@@ -116,11 +117,8 @@ class TogglAPI(object): | |||
116 | cache_key = blake2s(jsonpickle.encode({ | 117 | cache_key = blake2s(jsonpickle.encode({ |
117 | 'start': req_start, | 118 | 'start': req_start, |
118 | 'end': req_end, | 119 | 'end': req_end, |
119 | 'rounding': rounding, | 120 | 'client_ids': client_ids, |
120 | 'clients': client_ids, | 121 | }).encode('utf-8'), key = self._session.auth.token.encode('utf-8')).hexdigest() |
121 | 'workspace': self._workspace_id, | ||
122 | 'workspace_clients': self._client_ids | ||
123 | }).encode('utf-8'), key = self._api_token.encode('utf-8')).hexdigest() | ||
124 | cache_path = cache_dir / cache_key[:2] / cache_key[2:4] / f'{cache_key[4:]}.json' | 122 | cache_path = cache_dir / cache_key[:2] / cache_key[2:4] / f'{cache_key[4:]}.json' |
125 | try: | 123 | try: |
126 | with cache_path.open('r', encoding='utf-8') as ch: | 124 | with cache_path.open('r', encoding='utf-8') as ch: |
@@ -130,85 +128,83 @@ class TogglAPI(object): | |||
130 | pass | 128 | pass |
131 | 129 | ||
132 | entries = list() | 130 | entries = list() |
133 | params = { 'since': (req_start - timedelta(days=1)).date().isoformat(), | 131 | params = { |
134 | 'until': (req_end + timedelta(days=1)).date().isoformat(), | 132 | 'begin': self.render_datetime(req_start), |
135 | 'rounding': 'yes' if rounding else 'no', | 133 | 'end': self.render_datetime(req_end), |
136 | 'billable': 'yes' | 134 | 'customers[]': list(client_ids), |
137 | } | 135 | 'billable': 1, |
138 | if client_ids is not None: | 136 | } |
139 | params |= { 'client_ids': ','.join(map(str, client_ids)) } | 137 | |
140 | for page in count(start = 1): | 138 | for entry in self.get_timesheets(params): |
141 | url = self._make_url(api = TogglAPISection.REPORTS, section = ['details'], params = params | { 'page': page }) | 139 | if entry['end'] is None: |
142 | r = self._query(url = url, method='GET') | 140 | continue |
143 | if not r or not r.json(): | 141 | |
144 | raise TogglAPIError(r) | 142 | start = isoparse(entry['begin']) |
145 | report = r.json() | 143 | end = isoparse(entry['end']) |
146 | for entry in report['data']: | 144 | |
147 | start = isoparse(entry['start']) | 145 | if start > req_end or end < req_start: |
148 | end = isoparse(entry['end']) | 146 | continue |
149 | |||
150 | if start > req_end or end < req_start: | ||
151 | continue | ||
152 | 147 | ||
153 | x = min(end, req_end) - max(start, req_start) | 148 | x = min(end, req_end) - max(start, req_start) |
154 | if cache_key: | 149 | if cache_key: |
155 | entries.append(x) | 150 | entries.append(x) |
156 | yield x | 151 | yield x |
157 | if not report['data']: | ||
158 | break | ||
159 | 152 | ||
160 | if cache_path: | 153 | if cache_path: |
161 | cache_path.parent.mkdir(parents=True, exist_ok=True) | 154 | cache_path.parent.mkdir(parents=True, exist_ok=True) |
162 | with cache_path.open('w', encoding='utf-8') as ch: | 155 | with cache_path.open('w', encoding='utf-8') as ch: |
163 | ch.write(jsonpickle.encode(entries)) | 156 | ch.write(jsonpickle.encode(entries)) |
164 | # res = timedelta(milliseconds=report['total_billable']) if report['total_billable'] else timedelta(milliseconds=0) | ||
165 | # return res | ||
166 | 157 | ||
167 | def get_billable_hours(self, start_date, end_date=datetime.now(timezone.utc), rounding=False): | 158 | def get_billable_hours(self, start_date: datetime, end_date: datetime = datetime.now(timezone.utc)) -> timedelta: |
168 | billable_acc = timedelta(milliseconds = 0) | 159 | return sum(self.entry_durations(start_date, end_date=end_date), start=timedelta(milliseconds=0)) |
169 | if 0 in self._client_ids: | ||
170 | url = self._make_url(api = TogglAPISection.TOGGL, section = ['workspaces', self._workspace_id, 'clients']) | ||
171 | r = self._query(url = url, method = 'GET') | ||
172 | if not r or not r.json(): | ||
173 | raise TogglAPIError(r) | ||
174 | 160 | ||
175 | billable_acc += sum(self.entry_durations(start_date, end_date=end_date, rounding=rounding, client_ids=None), start=timedelta(milliseconds=0)) - sum(self.entry_durations(start_date, end_date=end_date, rounding=rounding, client_ids=frozenset(map(lambda c: c['id'], r.json()))), start=timedelta(milliseconds=0)) | 161 | def get_running_entry(self) -> Any | None: |
176 | 162 | kimai_entries = self._session.get('/api/timesheets/active').json() | |
177 | billable_acc += sum(self.entry_durations(start_date, end_date=end_date, rounding=rounding, client_ids=frozenset(*(self._client_ids - {0}))), start=timedelta(milliseconds=0)) | 163 | if not kimai_entries: |
178 | 164 | return None | |
179 | return billable_acc | 165 | entry = kimai_entries[0] |
180 | 166 | ||
181 | def get_running_clock(self, now=datetime.now(timezone.utc)): | 167 | if entry['project']['customer']['id'] not in self._client_ids: |
182 | url = self._make_url(api = TogglAPISection.TOGGL, section = ['me', 'time_entries', 'current']) | 168 | return None |
183 | r = self._query(url = url, method='GET') | ||
184 | 169 | ||
185 | if not r or (not r.json() and r.json() is not None): | 170 | return entry |
186 | raise TogglAPIError(r) | ||
187 | 171 | ||
188 | if not r.json() or not r.json()['billable']: | 172 | def get_running_clock(self, now: datetime = datetime.now(timezone.utc)) -> timedelta | None: |
173 | entry = self.get_running_entry() | ||
174 | if not entry: | ||
189 | return None | 175 | return None |
176 | start = isoparse(entry['begin']) | ||
177 | return now - start if start <= now else None | ||
190 | 178 | ||
191 | if self._client_ids is not None: | 179 | def get_recent_entries(self) -> Generator[Any]: |
192 | if 'pid' in r.json() and r.json()['pid']: | 180 | step = timedelta(days = 7) |
193 | url = self._make_url(api = TogglAPISection.TOGGL, section = ['projects', str(r.json()['pid'])]) | 181 | now = datetime.now().astimezone(timezone.utc) |
194 | pr = self._query(url = url, method = 'GET') | 182 | ids = set() |
195 | if not pr or not pr.json(): | 183 | for req_end in (now - step * i for i in count()): |
196 | raise TogglAPIError(pr) | 184 | params = { |
185 | 'begin': self.render_datetime(req_end - step), | ||
186 | 'end': self.render_datetime(req_end), | ||
187 | 'full': 'true', | ||
188 | } | ||
189 | for entry in self.get_timesheets(params): | ||
190 | if entry['id'] in ids: | ||
191 | continue | ||
192 | ids.add(entry['id']) | ||
193 | yield entry | ||
197 | 194 | ||
198 | if not pr.json(): | 195 | def start_clock(self, project_id: int, activity_id: int, description: str | None = None, tags: Iterable[str] | None = None, billable: bool = True): |
199 | return None | 196 | self._session.post('/api/timesheets', json={ |
197 | 'begin': self.render_datetime(datetime.now()), | ||
198 | 'project': project_id, | ||
199 | 'activity': activity_id, | ||
200 | 'description': description if description else '', | ||
201 | 'tags': (','.join(tags)) if tags else '', | ||
202 | 'billable': billable, | ||
203 | }).raise_for_status() | ||
200 | 204 | ||
201 | if 'cid' in pr.json() and pr.json()['cid']: | 205 | def stop_clock(self, running_id: int): |
202 | if pr.json()['cid'] not in self._client_ids: | 206 | self._session.patch(f'/api/timesheets/{running_id}/stop').raise_for_status() |
203 | return None | ||
204 | elif 0 not in self._client_ids: | ||
205 | return None | ||
206 | elif 0 not in self._client_ids: | ||
207 | return None | ||
208 | 207 | ||
209 | start = isoparse(r.json()['start']) | ||
210 | |||
211 | return now - start if start <= now else None | ||
212 | 208 | ||
213 | class Worktime(object): | 209 | class Worktime(object): |
214 | time_worked = timedelta() | 210 | time_worked = timedelta() |
@@ -281,10 +277,10 @@ class Worktime(object): | |||
281 | 277 | ||
282 | config = Worktime.config() | 278 | config = Worktime.config() |
283 | config_dir = BaseDirectory.load_first_config('worktime') | 279 | config_dir = BaseDirectory.load_first_config('worktime') |
284 | api = TogglAPI( | 280 | api = KimaiAPI( |
285 | api_token=config.get("TOGGL", {}).get("ApiToken", None), | 281 | base_url=config.get("KIMAI", {}).get("BaseUrl", None), |
286 | workspace_id=config.get("TOGGL", {}).get("Workspace", None), | 282 | api_token=config.get("KIMAI", {}).get("ApiToken", None), |
287 | client_ids=config.get("TOGGL", {}).get("ClientIds", None) | 283 | clients=config.get("KIMAI", {}).get("Clients", None) |
288 | ) | 284 | ) |
289 | date_format = config.get("WORKTIME", {}).get("DateFormat", '%Y-%m-%d') | 285 | date_format = config.get("WORKTIME", {}).get("DateFormat", '%Y-%m-%d') |
290 | 286 | ||
@@ -496,7 +492,7 @@ class Worktime(object): | |||
496 | 492 | ||
497 | self.time_to_work += self.time_pulled_forward | 493 | self.time_to_work += self.time_pulled_forward |
498 | 494 | ||
499 | self.time_worked += api.get_billable_hours(self.start_date, self.now, rounding = config.get("WORKTIME", {}).get("rounding", True)) | 495 | self.time_worked += api.get_billable_hours(self.start_date, self.now) |
500 | 496 | ||
501 | def format_days(worktime, days, date_format=None): | 497 | def format_days(worktime, days, date_format=None): |
502 | if not date_format: | 498 | if not date_format: |
@@ -886,7 +882,7 @@ def main(): | |||
886 | 882 | ||
887 | config = Worktime.config() | 883 | config = Worktime.config() |
888 | 884 | ||
889 | parser = argparse.ArgumentParser(prog = "worktime", description = 'Track worktime using toggl API') | 885 | parser = argparse.ArgumentParser(prog = "worktime", description = 'Track worktime using Kimai API') |
890 | parser.add_argument('--time', dest = 'now', metavar = 'TIME', type = isotime, help = 'Time to calculate status for (default: current time)', default = datetime.now(tzlocal())) | 886 | parser.add_argument('--time', dest = 'now', metavar = 'TIME', type = isotime, help = 'Time to calculate status for (default: current time)', default = datetime.now(tzlocal())) |
891 | parser.add_argument('--start', dest = 'start_datetime', metavar = 'TIME', type = isotime, help = 'Time to calculate status from (default: None)', default = None) | 887 | parser.add_argument('--start', dest = 'start_datetime', metavar = 'TIME', type = isotime, help = 'Time to calculate status from (default: None)', default = None) |
892 | parser.add_argument('--no-running', dest = 'include_running', action = 'store_false') | 888 | parser.add_argument('--no-running', dest = 'include_running', action = 'store_false') |
@@ -923,5 +919,139 @@ def main(): | |||
923 | 919 | ||
924 | args.cmd(**vars(args)) | 920 | args.cmd(**vars(args)) |
925 | 921 | ||
922 | async def ui_update_options(api, cache_path): | ||
923 | options = set() | ||
924 | sort_order = dict() | ||
925 | entry_iter = enumerate(api.get_recent_entries()) | ||
926 | loop = asyncio.get_event_loop() | ||
927 | start = clock_gettime_ns(CLOCK_MONOTONIC) | ||
928 | while item := await loop.run_in_executor(None, next, entry_iter): | ||
929 | ix, entry = item | ||
930 | if len(options) >= 20 or ix >= 1000: | ||
931 | break | ||
932 | elif len(options) >= 3: | ||
933 | now = clock_gettime_ns(CLOCK_MONOTONIC) | ||
934 | if now - start >= 4000000000: | ||
935 | break | ||
936 | |||
937 | option = frozendict({ | ||
938 | 'tags': frozenset(entry['tags']), | ||
939 | 'activity': frozendict({'id': entry['activity']['id'], 'name': entry['activity']['name']}), | ||
940 | 'project': frozendict({'id': entry['project']['id'], 'customer': entry['project']['customer']['name'], 'name': entry['project']['name']}), | ||
941 | 'description': entry['description'] if entry['description'] else None, | ||
942 | 'billable': entry['billable'], | ||
943 | }) | ||
944 | sort_value = isoparse(entry['begin']) | ||
945 | if option in sort_order: | ||
946 | sort_value = max(sort_value, sort_order[option]) | ||
947 | sort_order[option] = sort_value | ||
948 | options.add(option) | ||
949 | |||
950 | options = list(sorted(options, key = lambda o: sort_order[o], reverse = True)) | ||
951 | |||
952 | with AtomicWriter(cache_path, overwrite=True) as ch: | ||
953 | ch.write_text(jsonpickle.encode(options)) | ||
954 | |||
955 | return options | ||
956 | |||
957 | def ui_render_option(option): | ||
958 | res = '' | ||
959 | if option['description']: | ||
960 | res += '„{}“, '.format(option['description']) | ||
961 | res += option['activity']['name'] + ', ' | ||
962 | res += option['project']['name'] | ||
963 | if option['project']['customer'] not in option['project']['name']: | ||
964 | res += ' ({})'.format(option['project']['customer']) | ||
965 | if option['tags']: | ||
966 | res += ', {}'.format(' '.join(map(lambda t: '#{}'.format(t), option['tags']))) | ||
967 | if not option['billable']: | ||
968 | res += ', not billable' | ||
969 | return res | ||
970 | |||
971 | async def ui_main(): | ||
972 | cache_path = Path(BaseDirectory.save_cache_path('worktime-ui')) / 'options.json' | ||
973 | options = None | ||
974 | try: | ||
975 | with cache_path.open('r', encoding='utf-8') as ch: | ||
976 | options = jsonpickle.decode(ch.read()) | ||
977 | except FileNotFoundError: | ||
978 | pass | ||
979 | |||
980 | config = Worktime.config() | ||
981 | api = KimaiAPI( | ||
982 | base_url=config.get("KIMAI", {}).get("BaseUrl", None), | ||
983 | api_token=config.get("KIMAI", {}).get("ApiToken", None), | ||
984 | clients=config.get("KIMAI", {}).get("Clients", None) | ||
985 | ) | ||
986 | running_entry = api.get_running_entry() | ||
987 | |||
988 | async with asyncio.TaskGroup() as tg: | ||
989 | update_options = tg.create_task(ui_update_options(api, cache_path)) | ||
990 | if not options: | ||
991 | options = await update_options | ||
992 | |||
993 | read_fd, write_fd = os.pipe() | ||
994 | w_pipe = open(write_fd, 'wb', 0) | ||
995 | loop = asyncio.get_event_loop() | ||
996 | w_transport, _ = await loop.connect_write_pipe( | ||
997 | asyncio.Protocol, | ||
998 | w_pipe, | ||
999 | ) | ||
1000 | r_pipe = open(read_fd, 'rb', 0) | ||
1001 | |||
1002 | proc = await asyncio.create_subprocess_exec( | ||
1003 | "fuzzel", "--dmenu", "--index", "--width=60", | ||
1004 | stdout = asyncio.subprocess.PIPE, | ||
1005 | stdin = r_pipe, | ||
1006 | ) | ||
1007 | |||
1008 | with closing(w_transport) as t: | ||
1009 | if running_entry: | ||
1010 | t.write(b'Stop running timesheet\n') | ||
1011 | for option in options: | ||
1012 | t.write(ui_render_option(option).encode('utf-8') + b'\n') | ||
1013 | |||
1014 | stdout, _ = await proc.communicate() | ||
1015 | if proc.returncode != 0: | ||
1016 | return | ||
1017 | fuzzel_out = int(stdout.decode('utf-8')) | ||
1018 | if fuzzel_out < 0 or fuzzel_out >= len(options): | ||
1019 | return | ||
1020 | elif running_entry and fuzzel_out == 0: | ||
1021 | api.stop_clock(running_entry['id']) | ||
1022 | await notify.Server('worktime').Notify("Stopped running timesheet").set_timeout(65000).show() | ||
1023 | else: | ||
1024 | if running_entry: | ||
1025 | fuzzel_out -= 1 | ||
1026 | option = options[fuzzel_out] | ||
1027 | api.start_clock( | ||
1028 | project_id = option['project']['id'], | ||
1029 | activity_id = option['activity']['id'], | ||
1030 | description = option['description'], | ||
1031 | tags = option['tags'], | ||
1032 | billable = option['billable'], | ||
1033 | ) | ||
1034 | await notify.Server('worktime').Notify("Timesheet started…").set_timeout(65000).show() | ||
1035 | |||
1036 | |||
1037 | def ui(): | ||
1038 | asyncio.run(ui_main()) | ||
1039 | |||
1040 | async def stop_main(): | ||
1041 | config = Worktime.config() | ||
1042 | api = KimaiAPI( | ||
1043 | base_url=config.get("KIMAI", {}).get("BaseUrl", None), | ||
1044 | api_token=config.get("KIMAI", {}).get("ApiToken", None), | ||
1045 | clients=config.get("KIMAI", {}).get("Clients", None) | ||
1046 | ) | ||
1047 | if running_entry := api.get_running_entry(): | ||
1048 | api.stop_clock(running_entry['id']) | ||
1049 | await notify.Server('worktime').Notify("Stopped running timesheet").set_timeout(65000).show() | ||
1050 | else: | ||
1051 | await notify.Server('worktime').Notify("No timesheet currently running").set_timeout(65000).show() | ||
1052 | |||
1053 | def stop(): | ||
1054 | asyncio.run(stop_main()) | ||
1055 | |||
926 | if __name__ == "__main__": | 1056 | if __name__ == "__main__": |
927 | sys.exit(main()) | 1057 | sys.exit(main()) |
diff --git a/overlays/zte-prometheus-exporter/default.nix b/overlays/zte-prometheus-exporter/default.nix index 2188e7b3..cd4207cd 100644 --- a/overlays/zte-prometheus-exporter/default.nix +++ b/overlays/zte-prometheus-exporter/default.nix | |||
@@ -2,17 +2,16 @@ | |||
2 | let | 2 | let |
3 | packageOverrides = final.callPackage ./python-packages.nix {}; | 3 | packageOverrides = final.callPackage ./python-packages.nix {}; |
4 | inpPython = final.python310.override { inherit packageOverrides; }; | 4 | inpPython = final.python310.override { inherit packageOverrides; }; |
5 | python = inpPython.withPackages (ps: with ps; [pytimeparse requests]); | ||
5 | in { | 6 | in { |
6 | zte-prometheus-exporter = prev.stdenv.mkDerivation rec { | 7 | zte-prometheus-exporter = prev.stdenv.mkDerivation rec { |
7 | name = "zte-prometheus-exporter"; | 8 | name = "zte-prometheus-exporter"; |
8 | src = ./zte-prometheus-exporter.py; | 9 | src = prev.replaceVars ./zte-prometheus-exporter.py { inherit python; }; |
9 | 10 | ||
10 | phases = [ "buildPhase" "checkPhase" "installPhase" ]; | 11 | phases = [ "unpackPhase" "checkPhase" "installPhase" ]; |
11 | 12 | ||
12 | python = inpPython.withPackages (ps: with ps; [pytimeparse requests]); | 13 | unpackPhase = '' |
13 | 14 | cp $src zte-prometheus-exporter | |
14 | buildPhase = '' | ||
15 | substituteAll $src zte-prometheus-exporter | ||
16 | ''; | 15 | ''; |
17 | 16 | ||
18 | doCheck = true; | 17 | doCheck = true; |
@@ -3,6 +3,12 @@ let | |||
3 | pkgs = self.legacyPackages.${system}; | 3 | pkgs = self.legacyPackages.${system}; |
4 | utils = import ./utils { inherit (nixpkgs) lib; }; | 4 | utils = import ./utils { inherit (nixpkgs) lib; }; |
5 | inherit (utils) nixImport; | 5 | inherit (utils) nixImport; |
6 | uv-links = pkgs.symlinkJoin { | ||
7 | name = "uv-links"; | ||
8 | paths = [ | ||
9 | pkgs.python312.pkgs.pygobject3 | ||
10 | ]; | ||
11 | }; | ||
6 | in pkgs.mkShell { | 12 | in pkgs.mkShell { |
7 | nativeBuildInputs = builtins.attrValues self.packages.${system} ++ (with pkgs; [ | 13 | nativeBuildInputs = builtins.attrValues self.packages.${system} ++ (with pkgs; [ |
8 | sops | 14 | sops |
@@ -14,6 +20,10 @@ in pkgs.mkShell { | |||
14 | yq | 20 | yq |
15 | nvfetcher.packages.${system}.default | 21 | nvfetcher.packages.${system}.default |
16 | ca-util.packages.${system}.ca | 22 | ca-util.packages.${system}.ca |
17 | poetry | 23 | poetry uv |
24 | ninja pkg-config cairo.dev systemd.dev | ||
18 | ]); | 25 | ]); |
26 | shellHook = '' | ||
27 | export UV_FIND_LINKS=${uv-links}/lib/python3.12/site-packages | ||
28 | ''; | ||
19 | } | 29 | } |
diff --git a/system-profiles/core/default.nix b/system-profiles/core/default.nix index b85aea4e..229a007e 100644 --- a/system-profiles/core/default.nix +++ b/system-profiles/core/default.nix | |||
@@ -127,36 +127,16 @@ in { | |||
127 | 127 | ||
128 | flake-registry = "${flakeInputs.flake-registry}/flake-registry.json"; | 128 | flake-registry = "${flakeInputs.flake-registry}/flake-registry.json"; |
129 | }; | 129 | }; |
130 | nixPath = [ | 130 | nixPath = map (flake: "${flake}=flake:${flake}") (attrNames config.nix.registry); |
131 | "nixpkgs=${pkgs.runCommand "nixpkgs" {} '' | ||
132 | mkdir $out | ||
133 | ln -s ${./nixpkgs.nix} $out/default.nix | ||
134 | ln -s /run/nixpkgs/lib $out/lib | ||
135 | ''}" | ||
136 | ]; | ||
137 | registry = | 131 | registry = |
138 | let override = { self = "nixos"; }; | 132 | let override = { self = "nixos"; }; |
139 | in mapAttrs' (inpName: inpFlake: nameValuePair | 133 | in mapAttrs' (inpName: inpFlake: nameValuePair |
140 | (override.${inpName} or inpName) | 134 | (override.${inpName} or inpName) |
141 | { flake = inpFlake; } ) flakeInputs; | 135 | { to = { type = "path"; path = inpFlake; }; } ) flakeInputs; |
142 | }; | 136 | }; |
143 | 137 | ||
144 | systemd.tmpfiles.rules = [ | 138 | systemd.tmpfiles.rules = [ |
145 | "L+ /run/nixpkgs - - - - ${flakeInputs.${config.nixpkgs.flakeInput}.outPath}" | 139 | "L+ /run/nixpkgs - - - - ${flakeInputs.${config.nixpkgs.flakeInput}.outPath}" |
146 | "L+ /run/nixpkgs-overlays.nix - - - - ${pkgs.writeText "overlays.nix" '' | ||
147 | with builtins; | ||
148 | |||
149 | attrValues (import | ||
150 | ( | ||
151 | let lock = fromJSON (readFile ${flake + "/flake.lock"}); in | ||
152 | fetchTarball { | ||
153 | url = "https://github.com/edolstra/flake-compat/archive/''${lock.nodes.flake-compat.locked.rev}.tar.gz"; | ||
154 | sha256 = lock.nodes.flake-compat.locked.narHash; | ||
155 | } | ||
156 | ) | ||
157 | { src = ${flake}; } | ||
158 | ).defaultNix.overlays | ||
159 | ''}" | ||
160 | "L+ /etc/nixos - - - - ${flake}" | 140 | "L+ /etc/nixos - - - - ${flake}" |
161 | ] ++ map (input: "L+ /run/flake-inputs/${input} - - - - ${flakeInputs.${input}.outPath}") (attrNames flakeInputs); | 141 | ] ++ map (input: "L+ /run/flake-inputs/${input} - - - - ${flakeInputs.${input}.outPath}") (attrNames flakeInputs); |
162 | 142 | ||
@@ -177,8 +157,6 @@ in { | |||
177 | { | 157 | { |
178 | manual.manpages.enable = true; | 158 | manual.manpages.enable = true; |
179 | systemd.user.startServices = "sd-switch"; | 159 | systemd.user.startServices = "sd-switch"; |
180 | |||
181 | programs.ssh.internallyManaged = mkForce true; | ||
182 | } | 160 | } |
183 | ]; | 161 | ]; |
184 | extraSpecialArgs = { inherit flake flakeInputs path; hostConfig = config; }; | 162 | extraSpecialArgs = { inherit flake flakeInputs path; hostConfig = config; }; |
diff --git a/system-profiles/default-locale.nix b/system-profiles/default-locale.nix index 2d483f04..60d338cb 100644 --- a/system-profiles/default-locale.nix +++ b/system-profiles/default-locale.nix | |||
@@ -1,16 +1,23 @@ | |||
1 | { lib, ... }: | 1 | { lib, options, ... }: |
2 | 2 | ||
3 | with lib; | 3 | with lib; |
4 | 4 | ||
5 | { | 5 | { |
6 | i18n = { | 6 | config = foldr recursiveUpdate {} ([ |
7 | defaultLocale = "en_DK.UTF-8"; | 7 | { |
8 | extraLocaleSettings = { | 8 | i18n = { |
9 | "TIME_STYLE" = "long-iso"; | 9 | defaultLocale = "en_DK.UTF-8"; |
10 | }; | 10 | extraLocaleSettings = { |
11 | supportedLocales = [ "C.UTF-8/UTF-8" "en_US.UTF-8/UTF-8" "en_DK.UTF-8/UTF-8" ]; | 11 | "TIME_STYLE" = "long-iso"; |
12 | }; | 12 | }; |
13 | console.keyMap = mkDefault "dvorak-programmer"; | 13 | }; |
14 | console.keyMap = mkDefault "dvorak-programmer"; | ||
14 | 15 | ||
15 | time.timeZone = mkDefault "Europe/Berlin"; | 16 | time.timeZone = mkDefault "Europe/Berlin"; |
17 | } | ||
18 | ] ++ (optional (options ? i18n.extraLocales) { | ||
19 | i18n.extraLocales = [ "C.UTF-8" "en_US.UTF-8" "en_DK.UTF-8" ]; | ||
20 | }) ++ (optional (!(options ? i18n.extraLocales)) { | ||
21 | i18n.supportedLocales = [ "C.UTF-8/UTF-8" "en_US.UTF-8/UTF-8" "en_DK.UTF-8/UTF-8" ]; | ||
22 | })); | ||
16 | } | 23 | } |
diff --git a/system-profiles/nfsroot.nix b/system-profiles/nfsroot.nix index 1cd930d9..b0116d61 100644 --- a/system-profiles/nfsroot.nix +++ b/system-profiles/nfsroot.nix | |||
@@ -48,7 +48,7 @@ in { | |||
48 | fileSystems."/nix/.ro-store" = mkImageMediaOverride | 48 | fileSystems."/nix/.ro-store" = mkImageMediaOverride |
49 | { fsType = "nfs4"; | 49 | { fsType = "nfs4"; |
50 | device = cfg.storeDevice; | 50 | device = cfg.storeDevice; |
51 | options = [ "ro" ]; | 51 | options = [ "ro" "nfsvers=4.2" ]; |
52 | neededForBoot = true; | 52 | neededForBoot = true; |
53 | }; | 53 | }; |
54 | 54 | ||
diff --git a/system-profiles/rebuild-machines/default.nix b/system-profiles/rebuild-machines/default.nix index 544f47e1..de86cd74 100644 --- a/system-profiles/rebuild-machines/default.nix +++ b/system-profiles/rebuild-machines/default.nix | |||
@@ -25,16 +25,18 @@ let | |||
25 | 25 | ||
26 | phases = [ "buildPhase" "installPhase" ]; | 26 | phases = [ "buildPhase" "installPhase" ]; |
27 | 27 | ||
28 | inherit (pkgs) zsh coreutils openssh; | ||
29 | inherit (cfg) scriptName; | ||
30 | inherit (cfg.flake) flakeOutput; | ||
31 | flake = cfg.flake.name; | ||
32 | nixosRebuild = config.system.build.nixos-rebuild; | ||
33 | inherit (config.security) wrapperDir; | ||
34 | inherit sshConfig; | ||
35 | |||
36 | buildPhase = '' | 28 | buildPhase = '' |
37 | substituteAll $src rebuild-machine.zsh | 29 | substitute $src rebuild-machine.zsh \ |
30 | --subst-var-by zsh ${pkgs.zsh} \ | ||
31 | --subst-var-by coreutils ${pkgs.coreutils} \ | ||
32 | --subst-var-by openssh ${pkgs.openssh} \ | ||
33 | --subst-var-by wrapperDir ${config.security.wrapperDir} \ | ||
34 | --subst-var-by sshConfig ${sshConfig} \ | ||
35 | --subst-var-by out "$out" \ | ||
36 | --subst-var-by nixosRebuild ${config.system.build.nixos-rebuild} \ | ||
37 | --subst-var-by flake ${cfg.flake.name} \ | ||
38 | --subst-var-by scriptName ${cfg.scriptName} \ | ||
39 | --subst-var-by flakeOutput ${cfg.flake.flakeOutput} | ||
38 | ''; | 40 | ''; |
39 | 41 | ||
40 | installPhase = '' | 42 | installPhase = '' |
diff --git a/user-profiles/core.nix b/user-profiles/core.nix index a8af48b3..57fb7628 100644 --- a/user-profiles/core.nix +++ b/user-profiles/core.nix | |||
@@ -5,7 +5,11 @@ with lib; | |||
5 | { | 5 | { |
6 | config = { | 6 | config = { |
7 | users.users.${userName} = {}; # Just make sure the user is created | 7 | users.users.${userName} = {}; # Just make sure the user is created |
8 | home-manager.users.${userName} = {}; | 8 | home-manager.users.${userName} = let sysConfig = config; in { config, ... }: { |
9 | config.nix.settings = { | ||
10 | inherit (sysConfig.nix.settings) use-xdg-base-directories; | ||
11 | }; | ||
12 | }; | ||
9 | 13 | ||
10 | systemd.services."home-manager-${utils.escapeSystemdPath userName}" = lib.mkIf (!config.home-manager.enableSystemd) { | 14 | systemd.services."home-manager-${utils.escapeSystemdPath userName}" = lib.mkIf (!config.home-manager.enableSystemd) { |
11 | restartIfChanged = false; # only run once on startup, deploy to running system with deploy-rs | 15 | restartIfChanged = false; # only run once on startup, deploy to running system with deploy-rs |
diff --git a/user-profiles/feeds/alot.config b/user-profiles/feeds/alot.config deleted file mode 100644 index a14d4539..00000000 --- a/user-profiles/feeds/alot.config +++ /dev/null | |||
@@ -1,50 +0,0 @@ | |||
1 | attachment_prefix="~/Downloads" | ||
2 | bug_on_exit=true | ||
3 | editor_cmd="false" | ||
4 | tabwidth=2 | ||
5 | timestamp_format="%a %d %b %H:%M:%S %Y UTC%z" | ||
6 | auto_remove_unread=True | ||
7 | #initial_command="search ( tag:inbox ) AND NOT ( tag:killed )" | ||
8 | initial_command="search ( tag:inbox ) AND NOT ( is:link OR is:media OR is:killed )" | ||
9 | |||
10 | [accounts] | ||
11 | [[private]] | ||
12 | realname = @realname@ | ||
13 | address = @address@ | ||
14 | |||
15 | [bindings] | ||
16 | j = | ||
17 | k = | ||
18 | 'g g' = | ||
19 | G = | ||
20 | I = search ( tag:inbox ) AND NOT ( is:killed ) | ||
21 | U = search ( tag:inbox ) AND NOT ( is:link OR is:media OR is:killed ) | ||
22 | V = search ( tag:inbox AND is:media OR ( is:live AND date:12h.. AND NOT is:unread ) ) AND NOT ( is:killed ) | ||
23 | W = search ( is:media ) AND NOT ( tag:inbox OR is:killed OR is:highlight ) | ||
24 | L = search ( tag:inbox AND is:link ) AND NOT ( is:killed ) | ||
25 | |||
26 | h = move first | ||
27 | t = move up | ||
28 | n = move down | ||
29 | s = move last | ||
30 | [[search]] | ||
31 | a = | ||
32 | s = | ||
33 | |||
34 | u = toggletags unread | ||
35 | i = toggletags inbox | ||
36 | j = untag unread,inbox | ||
37 | r = toggletags later | ||
38 | [[thread]] | ||
39 | s = | ||
40 | S = | ||
41 | n = | ||
42 | 'g j' = | ||
43 | 'g k' = | ||
44 | 'g l' = | ||
45 | w = save | ||
46 | W = save --all | ||
47 | 'g h' = move parent | ||
48 | 'g t' = move next sibling | ||
49 | 'g n' = move previous sibling | ||
50 | 'g s' = move first reply \ No newline at end of file | ||
diff --git a/user-profiles/feeds/default.nix b/user-profiles/feeds/default.nix deleted file mode 100644 index 82be90c7..00000000 --- a/user-profiles/feeds/default.nix +++ /dev/null | |||
@@ -1,11 +0,0 @@ | |||
1 | { config, flakeInputs, pkgs, lib, userName, customUtils, ... }: | ||
2 | { | ||
3 | home-manager.users.${userName} = {...}: { | ||
4 | imports = [ | ||
5 | (customUtils.overrideModuleArgs | ||
6 | (import ./module.nix) | ||
7 | (inputs: inputs // { inherit flakeInputs; inherit (config.nixpkgs) system; }) | ||
8 | ) | ||
9 | ]; | ||
10 | }; | ||
11 | } | ||
diff --git a/user-profiles/feeds/imm-notmuch-insert.py b/user-profiles/feeds/imm-notmuch-insert.py deleted file mode 100644 index b7eed292..00000000 --- a/user-profiles/feeds/imm-notmuch-insert.py +++ /dev/null | |||
@@ -1,52 +0,0 @@ | |||
1 | #!@python@/bin/python | ||
2 | |||
3 | import json | ||
4 | import sys | ||
5 | import subprocess | ||
6 | from io import BytesIO | ||
7 | from email.message import EmailMessage | ||
8 | import configparser | ||
9 | from os import environ | ||
10 | from datetime import * | ||
11 | from dateutil.tz import * | ||
12 | from dateutil.parser import isoparse | ||
13 | from html2text import html2text | ||
14 | |||
15 | def main(): | ||
16 | notmuchConfig = configparser.ConfigParser() | ||
17 | notmuchConfig.read(environ.get('NOTMUCH_CONFIG')) | ||
18 | |||
19 | callbackMessage = json.load(sys.stdin) | ||
20 | |||
21 | msg = EmailMessage() | ||
22 | authors = ', '.join(map(lambda author: author['name'], callbackMessage['feed_item']['authors'])) | ||
23 | if authors: | ||
24 | msg['From'] = f"{callbackMessage['feed_definition']['title']} ({authors}) <imm@imm.invalid>" | ||
25 | else: | ||
26 | msg['From'] = f"{callbackMessage['feed_definition']['title']} <imm@imm.invalid>" | ||
27 | msg['To'] = f"{notmuchConfig['user']['name']} <{notmuchConfig['user']['primary_email']}>" | ||
28 | if 'title' in callbackMessage['feed_item'] and callbackMessage['feed_item']['title']: | ||
29 | msg['Subject'] = callbackMessage['feed_item']['title'] | ||
30 | msg['Item-Identifier'] = f"{callbackMessage['feed_item']['identifier']}" | ||
31 | for link in callbackMessage['feed_item']['links']: | ||
32 | msg.add_header('Link', link['uri']) | ||
33 | date = None | ||
34 | if 'date' in callbackMessage['feed_item']: | ||
35 | date = isoparse(callbackMessage['feed_item']['date']) | ||
36 | else: | ||
37 | date = datetime.now(tzlocal()) | ||
38 | msg['Date'] = date.strftime('%a, %e %b %Y %T %z') | ||
39 | |||
40 | if 'content' in callbackMessage['feed_item'] and callbackMessage['feed_item']['content']: | ||
41 | msg.set_content(html2text(callbackMessage['feed_item']['content'])) | ||
42 | msg.add_alternative(callbackMessage['feed_item']['content'], subtype='html') | ||
43 | |||
44 | |||
45 | subprocess.run( | ||
46 | args=['notmuch', 'insert'], | ||
47 | check=True, | ||
48 | input=bytes(msg) | ||
49 | ) | ||
50 | |||
51 | if __name__ == '__main__': | ||
52 | sys.exit(main()) | ||
diff --git a/user-profiles/feeds/module.nix b/user-profiles/feeds/module.nix deleted file mode 100644 index 63e827eb..00000000 --- a/user-profiles/feeds/module.nix +++ /dev/null | |||
@@ -1,236 +0,0 @@ | |||
1 | { config, flakeInputs, pkgs, lib, system, ... }: | ||
2 | |||
3 | with lib; | ||
4 | |||
5 | let | ||
6 | inherit (flakeInputs.home-manager.lib) hm; | ||
7 | |||
8 | databasePath = "${config.xdg.dataHome}/feeds"; | ||
9 | |||
10 | imm = | ||
11 | let | ||
12 | hlib = pkgs.haskell.lib; | ||
13 | haskellPackages = pkgs.haskellPackages.override { | ||
14 | overrides = finalHaskell: prevHaskell: { | ||
15 | uri-bytestring = finalHaskell.callCabal2nix "uri-bytestring" (pkgs.fetchFromGitHub { | ||
16 | owner = "gkleen"; | ||
17 | repo = "uri-bytestring"; | ||
18 | rev = "5f7f32c8274bc4d1b81d99582f5148fe3e8b637e"; | ||
19 | sha256 = "XLanwyCDIlMuOkpE5LbTNOBfL+1kZX+URfj9Bhs1Nsc="; | ||
20 | fetchSubmodules = true; | ||
21 | }) {}; | ||
22 | atom-conduit = finalHaskell.callCabal2nix "atom-conduit" (pkgs.fetchFromGitHub { | ||
23 | owner = "gkleen"; | ||
24 | repo = "atom-conduit"; | ||
25 | rev = "022f0182a02373f87c06a0a09817c8c41efe2425"; | ||
26 | sha256 = "8yEyh3ymqkoM/YP+eBqPq1I5ofzj0Qn7ojL7IWx1DPo="; | ||
27 | fetchSubmodules = true; | ||
28 | }) {}; | ||
29 | rss-conduit = finalHaskell.callCabal2nix "rss-condit" (pkgs.fetchFromGitHub { | ||
30 | owner = "gkleen"; | ||
31 | repo = "rss-conduit"; | ||
32 | rev = "dbb0960a8d3dc519f1607aa0223b3a25a49282ef"; | ||
33 | sha256 = "Md1XApZWkdv4JvNoaVnjz0S85LbEC6w9U3PUcwXfu94="; | ||
34 | fetchSubmodules = true; | ||
35 | }) {}; | ||
36 | beam-core = hlib.doJailbreak (finalHaskell.callCabal2nix "beam-core" "${beamSrc}/beam-core" {}); | ||
37 | beam-migrate = hlib.doJailbreak (finalHaskell.callCabal2nix "beam-migrate" "${beamSrc}/beam-migrate" {}); | ||
38 | beam-sqlite = hlib.doJailbreak (finalHaskell.callCabal2nix "beam-sqlite" "${beamSrc}/beam-sqlite" {}); | ||
39 | |||
40 | imm = finalHaskell.callCabal2nix "imm" (pkgs.fetchFromGitHub { | ||
41 | owner = "k0ral"; | ||
42 | repo = "imm"; | ||
43 | rev = "5033879667264cb44cee65671a66f6aa43f249e7"; | ||
44 | sha256 = "PG22caLQmAGhLZP49HsazuNd8IFKKaTuhXIQBD8v4Fs="; | ||
45 | fetchSubmodules = true; | ||
46 | }) {}; | ||
47 | }; | ||
48 | }; | ||
49 | beamSrc = pkgs.fetchFromGitHub { | ||
50 | owner = "haskell-beam"; | ||
51 | repo = "beam"; | ||
52 | rev = "efd464b079755a781c2bb7a2fc030d6c141bbb8a"; | ||
53 | sha256 = "8nTuBP/vD0L/qMo4h3XNrGZvpIwXuMVdj40j5gvHU6w="; | ||
54 | fetchSubmodules = true; | ||
55 | }; | ||
56 | in haskellPackages.imm; | ||
57 | immWrapped = pkgs.runCommand "${imm.name}-wrapped-${config.home.username}" | ||
58 | { nativeBuildInputs = with pkgs; [ makeWrapper ]; | ||
59 | } '' | ||
60 | mkdir -p $out/bin | ||
61 | makeWrapper ${imm}/bin/imm $out/bin/imm \ | ||
62 | --add-flags --callbacks=${notmuchCallbacks} | ||
63 | ''; | ||
64 | |||
65 | notmuchCallbacks = pkgs.writeText "imm-callbacks-${config.home.username}.dhall" '' | ||
66 | [ { _executable = "${immNotmuchInsert}/bin/imm-notmuch-insert" | ||
67 | , _arguments = [] : List Text | ||
68 | } | ||
69 | ] | ||
70 | ''; | ||
71 | |||
72 | immNotmuchInsert = pkgs.stdenv.mkDerivation rec { | ||
73 | name = "imm-notmuch-insert-${config.home.username}"; | ||
74 | src = ./imm-notmuch-insert.py; | ||
75 | |||
76 | phases = [ "buildPhase" "checkPhase" "installPhase" "fixupPhase" ]; | ||
77 | |||
78 | python = pkgs.python39.withPackages (ps: with ps; [ configparser python-dateutil html2text ]); | ||
79 | |||
80 | nativeBuildInputs = with pkgs; [ makeWrapper ]; | ||
81 | |||
82 | buildPhase = '' | ||
83 | substituteAll $src imm-notmuch-insert | ||
84 | ''; | ||
85 | |||
86 | doCheck = true; | ||
87 | checkPhase = '' | ||
88 | ${python}/bin/python -m py_compile imm-notmuch-insert | ||
89 | ''; | ||
90 | |||
91 | installPhase = '' | ||
92 | install -m 0755 -D -t $out/bin \ | ||
93 | imm-notmuch-insert | ||
94 | ''; | ||
95 | |||
96 | fixupPhase = '' | ||
97 | wrapProgram $out/bin/imm-notmuch-insert \ | ||
98 | --prefix PATH : ${pkgs.notmuch}/bin \ | ||
99 | --set NOTMUCH_CONFIG ${configPath} | ||
100 | ''; | ||
101 | }; | ||
102 | |||
103 | mkIniKeyValue = key: value: | ||
104 | let | ||
105 | tweakVal = v: | ||
106 | if isString v then | ||
107 | v | ||
108 | else if isList v then | ||
109 | concatMapStringsSep ";" tweakVal v | ||
110 | else if isBool v then | ||
111 | (if v then "true" else "false") | ||
112 | else | ||
113 | toString v; | ||
114 | in "${key}=${tweakVal value}"; | ||
115 | |||
116 | notmuchIni = { | ||
117 | database = { path = databasePath; }; | ||
118 | |||
119 | maildir = { synchronize_flags = false; }; | ||
120 | |||
121 | new = { | ||
122 | ignore = []; | ||
123 | tags = ["new"]; | ||
124 | }; | ||
125 | |||
126 | user = { | ||
127 | name = config.home.username; | ||
128 | primary_email = "${config.home.username}@imm.invalid"; | ||
129 | }; | ||
130 | |||
131 | search = { exclude_tags = ["deleted"]; }; | ||
132 | }; | ||
133 | configPath = pkgs.writeText "notmuchrc" (generators.toINI { mkKeyValue = mkIniKeyValue; } notmuchIni); | ||
134 | |||
135 | afewConfigDir = pkgs.symlinkJoin { | ||
136 | name = "afew-config"; | ||
137 | paths = [ | ||
138 | (pkgs.writeTextDir "config" '' | ||
139 | [InboxFilter] | ||
140 | '') | ||
141 | ]; | ||
142 | }; | ||
143 | |||
144 | notmuchHooksDir = | ||
145 | let | ||
146 | afewHook = pkgs.writeShellScript "afew" '' | ||
147 | exec -- ${pkgs.afew}/bin/afew -c ${afewConfigDir} -C ${configPath} --tag --new -vv | ||
148 | ''; | ||
149 | in pkgs.linkFarm "notmuch-hooks" [ | ||
150 | { name = "post-new"; | ||
151 | path = afewHook; | ||
152 | } | ||
153 | { name = "post-insert"; | ||
154 | path = afewHook; | ||
155 | } | ||
156 | ]; | ||
157 | |||
158 | notmuchWrapped = pkgs.runCommand "${pkgs.notmuch.name}-wrapped-${config.home.username}" | ||
159 | { nativeBuildInputs = with pkgs; [ makeWrapper ]; | ||
160 | } '' | ||
161 | mkdir -p $out/bin | ||
162 | makeWrapper ${pkgs.notmuch}/bin/notmuch $out/bin/notmuch-feeds \ | ||
163 | --set NOTMUCH_CONFIG ${configPath} | ||
164 | ''; | ||
165 | alotWrapped = pkgs.runCommand "${pkgs.alot.name}-wrapped-${config.home.username}" | ||
166 | { nativeBuildInputs = with pkgs; [ makeWrapper gnused ]; | ||
167 | } '' | ||
168 | mkdir -p $out/bin | ||
169 | makeWrapper ${pkgs.alot}/bin/alot $out/bin/alot-feeds \ | ||
170 | --prefix MAILCAPS : ${alotMailcaps} \ | ||
171 | --add-flags --config=${alotConfig} \ | ||
172 | --add-flags --notmuch-config=${configPath} | ||
173 | |||
174 | mkdir $out/share | ||
175 | ln -s ${pkgs.alot}/share/alot $out/share | ||
176 | mkdir -p $out/share/applications | ||
177 | sed -r 's/alot/alot-feeds/g' ${pkgs.alot}/share/applications/alot.desktop > $out/share/applications/alot-feeds.desktop | ||
178 | mkdir -p $out/share/zsh/site-functions | ||
179 | sed -r 's/alot/alot-feeds/g' ${pkgs.alot}/share/zsh/site-functions/_alot > $out/share/zsh/site-functions/_alot-feeds | ||
180 | ''; | ||
181 | |||
182 | alotConfig = pkgs.runCommand "alot" { | ||
183 | realname = notmuchIni.user.name; | ||
184 | address = notmuchIni.user.primary_email; | ||
185 | } "substituteAll ${./alot.config} $out"; | ||
186 | alotMailcaps = pkgs.writeText "mailcaps" '' | ||
187 | text/html; ${pkgs.lynx}/bin/lynx -dump -dont_wrap_pre -assume_charset=utf-8 -display_charset=utf-8 "%s"; nametemplate=%s.html; copiousoutput | ||
188 | ''; | ||
189 | in { | ||
190 | config = { | ||
191 | home.packages = [ immWrapped notmuchWrapped pkgs.notmuch.man alotWrapped ]; | ||
192 | |||
193 | home.activation.createImm = hm.dag.entryAfter ["writeBoundary"] '' | ||
194 | $DRY_RUN_CMD mkdir -p $VERBOSE_ARG ${config.xdg.configHome}/imm | ||
195 | ''; | ||
196 | |||
197 | home.activation.createFeedsDatabase = hm.dag.entryAfter ["linkGeneration" "writeBoundary"] '' | ||
198 | $DRY_RUN_CMD mkdir -p -m 0750 $VERBOSE_ARG ${databasePath} | ||
199 | $DRY_RUN_CMD mkdir -p $VERBOSE_ARG ${databasePath}/new ${databasePath}/cur ${databasePath}/tmp | ||
200 | if ! [[ -d ${databasePath}/.notmuch ]]; then | ||
201 | NOTMUCH_VERBOSE_ARG="--quiet" | ||
202 | if [[ -v VERBOSE ]]; then | ||
203 | NOTMUCH_VERBOSE_ARG="--verbose" | ||
204 | fi | ||
205 | NOTMUCH_CONFIG=${configPath} $DRY_RUN_CMD ${pkgs.notmuch}/bin/notmuch new $NOTMUCH_VERBOSE_ARG | ||
206 | fi | ||
207 | $DRY_RUN_CMD ln -Tsf $VERBOSE_ARG ${notmuchHooksDir} ${databasePath}/.notmuch/hooks | ||
208 | ''; | ||
209 | |||
210 | systemd.user.services."logrotate-imm" = { | ||
211 | Unit = { | ||
212 | Description = "Rotate imm logfile"; | ||
213 | }; | ||
214 | Service = { | ||
215 | Type = "oneshot"; | ||
216 | ExecStart = '' | ||
217 | ${pkgs.logrotate}/bin/logrotate --state ${config.xdg.configHome}/imm/imm.logrotate ${pkgs.writeText "logrotate.conf" '' | ||
218 | ${config.xdg.configHome}/imm/imm.log { | ||
219 | rotate 5 | ||
220 | size 1024k | ||
221 | } | ||
222 | ''} | ||
223 | ''; | ||
224 | }; | ||
225 | }; | ||
226 | systemd.user.timers."logrotate-imm" = { | ||
227 | Timer = { | ||
228 | OnActiveSec = "6h"; | ||
229 | OnUnitActiveSec = "6h"; | ||
230 | }; | ||
231 | Install = { | ||
232 | WantedBy = ["default.target"]; | ||
233 | }; | ||
234 | }; | ||
235 | }; | ||
236 | } | ||
diff --git a/user-profiles/tmux/default.nix b/user-profiles/tmux/default.nix index 11c53788..dc4e791f 100644 --- a/user-profiles/tmux/default.nix +++ b/user-profiles/tmux/default.nix | |||
@@ -1,10 +1,11 @@ | |||
1 | { userName, pkgs, lib, ... }: | 1 | { userName, pkgs, lib, ... }: |
2 | { | 2 | { |
3 | home-manager.users.${userName} = { | 3 | home-manager.users.${userName} = { config, ... }: { |
4 | programs.tmux = { | 4 | programs.tmux = { |
5 | enable = true; | 5 | enable = true; |
6 | clock24 = true; | 6 | clock24 = true; |
7 | historyLimit = 50000; | 7 | historyLimit = 50000; |
8 | mouse = true; | ||
8 | extraConfig = lib.readFile (pkgs.stdenv.mkDerivation { | 9 | extraConfig = lib.readFile (pkgs.stdenv.mkDerivation { |
9 | name = "tmux.conf"; | 10 | name = "tmux.conf"; |
10 | src = ./tmux.conf; | 11 | src = ./tmux.conf; |
@@ -13,11 +14,10 @@ | |||
13 | 14 | ||
14 | phases = [ "installPhase" ]; | 15 | phases = [ "installPhase" ]; |
15 | 16 | ||
16 | inherit (pkgs) zsh; | ||
17 | mandb = pkgs.man-db; | ||
18 | |||
19 | installPhase = '' | 17 | installPhase = '' |
20 | substituteAll $src $out | 18 | substitute $src $out \ |
19 | --subst-var-by zsh ${config.programs.zsh.package} \ | ||
20 | --subst-var-by man ${config.programs.man.package} | ||
21 | ''; | 21 | ''; |
22 | }); | 22 | }); |
23 | }; | 23 | }; |
diff --git a/user-profiles/tmux/tmux.conf b/user-profiles/tmux/tmux.conf index 415d13e7..9e658800 100644 --- a/user-profiles/tmux/tmux.conf +++ b/user-profiles/tmux/tmux.conf | |||
@@ -1,23 +1,20 @@ | |||
1 | set-option -g history-limit 50000 | ||
2 | set-option -g status-bg black | 1 | set-option -g status-bg black |
3 | set-option -g status-fg white | 2 | set-option -g status-fg white |
4 | set-option -g clock-mode-colour white | 3 | set-option -g clock-mode-colour white |
5 | set-option -g clock-mode-style 24 | ||
6 | set-option -g bell-action any | 4 | set-option -g bell-action any |
7 | set-option -g default-shell @zsh@/bin/zsh | 5 | set-option -g default-shell @zsh@ |
8 | set-option -g update-environment 'DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY PROMPT_INFO PATH PGHOST PGLOG' | 6 | set-option -g update-environment 'DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY PROMPT_INFO PATH PGHOST PGLOG' |
9 | set-option -g mouse on | ||
10 | set-option -g set-clipboard on | 7 | set-option -g set-clipboard on |
11 | set-option -g terminal-overrides 'rxvt-uni*:XT:Ms=\E]52;%p1%s;%p2%s\007' | 8 | set-option -g terminal-overrides 'rxvt-uni*:XT:Ms=\E]52;%p1%s;%p2%s\007' |
12 | 9 | ||
13 | set-environment -g LESS " -R " | 10 | set-environment -g LESS " -R " |
14 | 11 | ||
15 | ## determine if we should enable 256-colour support | 12 | ## determine if we should enable 256-colour support |
16 | if "[[ ''${TERM} =~ 256color || ''${TERM} == fbterm || ''${TERM} =~ alacritty ]]" 'set -g default-terminal tmux-256color' | 13 | if "[[ ''${TERM} =~ 256color || ''${TERM} == fbterm || ''${TERM} =~ alacritty || ''${TERM} =~ kitty ]]" 'set -g default-terminal tmux-256color' |
17 | 14 | ||
18 | set-option -g status-right "" | 15 | set-option -g status-right "" |
19 | 16 | ||
20 | bind / command-prompt "split-window -h 'exec @mandb@/bin/man %%'" | 17 | bind / command-prompt "split-window -h 'exec @man@ %%'" |
21 | bind C clock-mode | 18 | bind C clock-mode |
22 | bind r switch-client -r | 19 | bind r switch-client -r |
23 | 20 | ||
diff --git a/user-profiles/utils.nix b/user-profiles/utils.nix index 13eb6033..da79e336 100644 --- a/user-profiles/utils.nix +++ b/user-profiles/utils.nix | |||
@@ -1,19 +1,6 @@ | |||
1 | { userName, lib, pkgs, config, ... }: | 1 | { userName, lib, pkgs, config, ... }: |
2 | let | 2 | let |
3 | cfg = config.home-manager.users.${userName}; | 3 | cfg = config.home-manager.users.${userName}; |
4 | |||
5 | wrappedLess = pkgs.less.overrideAttrs (oldAttrs: { | ||
6 | pname = "${oldAttrs.pname or "less"}-wrapper"; | ||
7 | |||
8 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ (with pkgs; [makeWrapper]); | ||
9 | |||
10 | postInstall = '' | ||
11 | ${oldAttrs.postInstall or ""} | ||
12 | |||
13 | wrapProgram $out/bin/less \ | ||
14 | --prefix PATH : ${lib.makeBinPath (with pkgs; [binutils])} | ||
15 | ''; | ||
16 | }); | ||
17 | in { | 4 | in { |
18 | home-manager.users.${userName} = { | 5 | home-manager.users.${userName} = { |
19 | programs = { | 6 | programs = { |
@@ -55,19 +42,23 @@ in { | |||
55 | }; | 42 | }; |
56 | 43 | ||
57 | jq.enable = true; | 44 | jq.enable = true; |
45 | |||
46 | lesspipe.enable = true; | ||
47 | |||
48 | man.enable = true; | ||
58 | }; | 49 | }; |
59 | 50 | ||
60 | home.sessionVariables = { | 51 | home.sessionVariables = { |
61 | LESSCOLORIZER = "pygmentize -O style=rrt"; | 52 | LESSCOLORIZER = "${lib.getExe' pkgs.python3Packages.pygments "pygmentize"} -O style=rrt"; |
62 | }; | 53 | }; |
63 | 54 | ||
64 | home.packages = with pkgs; [ | 55 | home.packages = with pkgs; [ |
65 | autossh usbutils pciutils eza silver-searcher pwgen xkcdpass | 56 | autossh usbutils pciutils eza silver-searcher pwgen xkcdpass |
66 | unzip magic-wormhole qrencode tty-clock dnsutils openssl sshfs | 57 | unzip magic-wormhole dnsutils openssl sshfs |
67 | psmisc mosh tree vnstat file pv bc zip nmap aspell | 58 | psmisc mosh tree vnstat file pv bc zip nmap aspell |
68 | aspellDicts.de aspellDicts.en borgbackup man-pages rsync socat | 59 | aspellDicts.de aspellDicts.en borgbackup man-pages rsync socat |
69 | inetutils yq cached-nix-shell persistent-nix-shell rage | 60 | inetutils yq cached-nix-shell persistent-nix-shell rage |
70 | smartmontools hdparm nix-output-monitor wrappedLess dscp | 61 | smartmontools hdparm nix-output-monitor less dscp |
71 | iputils | 62 | iputils |
72 | ]; | 63 | ]; |
73 | }; | 64 | }; |
diff --git a/user-profiles/zsh/default.nix b/user-profiles/zsh/default.nix index 428e2459..ab523a52 100644 --- a/user-profiles/zsh/default.nix +++ b/user-profiles/zsh/default.nix | |||
@@ -1,38 +1,72 @@ | |||
1 | { userName, pkgs, customUtils, lib, config, ... }: | 1 | { userName, pkgs, customUtils, lib, config, ... }: |
2 | let | 2 | { |
3 | dotDir = ".config/zsh"; | 3 | config = { |
4 | p10kZsh = "${dotDir}/.p10k.zsh"; | 4 | home-manager.users.${userName} = let sysConfig = config; in { config, ... }: { |
5 | cfg = config.home-manager.users.${userName}; | 5 | config = { |
6 | in { | 6 | programs.zsh = { |
7 | home-manager.users.${userName} = { | 7 | dotDir = ".config/zsh"; |
8 | programs.zsh = { | 8 | enable = true; |
9 | inherit dotDir; | 9 | autocd = true; |
10 | enable = true; | 10 | enableCompletion = true; |
11 | autocd = true; | 11 | enableVteIntegration = true; |
12 | enableCompletion = true; | 12 | history = { |
13 | append = true; | ||
14 | expireDuplicatesFirst = true; | ||
15 | extended = true; | ||
16 | findNoDups = true; | ||
17 | }; | ||
18 | syntaxHighlighting.enable = true; | ||
19 | zsh-abbr = { | ||
20 | enable = true; | ||
21 | abbreviations = { | ||
22 | re = "systemctl restart"; | ||
23 | ure = "systemctl --user restart"; | ||
24 | st = "systemctl status"; | ||
25 | ust = "systemctl --user status"; | ||
26 | }; | ||
27 | globalAbbreviations = { | ||
28 | "L" = "| less"; | ||
29 | "S" = "&> /dev/null"; | ||
30 | "G" = "| grep"; | ||
31 | "B" = "&> /dev/null &"; | ||
32 | "BB" = "&> /dev/null &!"; | ||
33 | "J" = lib.mkIf config.programs.jq.enable "| jq '.'"; | ||
34 | }; | ||
35 | }; | ||
13 | 36 | ||
14 | plugins = [ | 37 | plugins = [ |
15 | { name = "powerlevel10k"; | 38 | { name = "powerlevel10k"; |
16 | file = "share/zsh-powerlevel10k/powerlevel10k.zsh-theme"; | 39 | file = "share/zsh-powerlevel10k/powerlevel10k.zsh-theme"; |
17 | src = pkgs.zsh-powerlevel10k; | 40 | src = pkgs.zsh-powerlevel10k; |
18 | } | 41 | } |
19 | ]; | 42 | ]; |
20 | initExtraFirst = '' | 43 | initContent = lib.mkMerge [ |
21 | if [[ $TERM == "dumb" ]]; then | 44 | (lib.mkBefore '' |
22 | unsetopt zle | 45 | if [[ $TERM == "dumb" ]]; then |
23 | PS1='$ ' | 46 | unsetopt zle |
24 | return | 47 | PS1='$ ' |
25 | fi | 48 | return |
26 | ''; | 49 | fi |
27 | initExtraBeforeCompInit = '' | 50 | '') |
28 | source "${cfg.home.homeDirectory}/${p10kZsh}" | 51 | (lib.mkOrder 550 '' |
29 | ''; | 52 | source "$HOME/${config.xdg.configFile."zsh/.p10k.zsh".target}" |
30 | initExtra = lib.mkAfter '' | 53 | '') |
31 | source ${./zshrc} | 54 | (lib.mkAfter '' |
32 | source "${pkgs.zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" | 55 | source ${./zshrc} |
33 | ''; | 56 | '') |
57 | ]; | ||
58 | }; | ||
59 | |||
60 | xdg.configFile."zsh/.p10k.zsh".source = ./p10k.zsh; | ||
61 | }; | ||
34 | }; | 62 | }; |
35 | 63 | ||
36 | home.file.${p10kZsh}.source = ./p10k.zsh; | 64 | programs.zsh.enable = true; |
65 | environment.pathsToLink = [ "/share/zsh" ]; | ||
66 | environment.shellAliases = lib.mkOverride 90 {}; | ||
67 | |||
68 | nixpkgs.externalConfig.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ | ||
69 | "zsh-abbr" | ||
70 | ]; | ||
37 | }; | 71 | }; |
38 | } | 72 | } |
diff --git a/user-profiles/zsh/zshrc b/user-profiles/zsh/zshrc index ed614182..af3aca64 100644 --- a/user-profiles/zsh/zshrc +++ b/user-profiles/zsh/zshrc | |||
@@ -33,9 +33,3 @@ zle -N self-insert url-quote-magic | |||
33 | zle -N bracketed-paste bracketed-paste-magic | 33 | zle -N bracketed-paste bracketed-paste-magic |
34 | 34 | ||
35 | setopt extended_glob | 35 | setopt extended_glob |
36 | |||
37 | alias -g L='| less' | ||
38 | alias -g S='&> /dev/null' | ||
39 | alias -g G='| grep' | ||
40 | alias -g B='&> /dev/null &' | ||
41 | alias -g BB='&> /dev/null &!' | ||
diff --git a/users/gkleen/default.nix b/users/gkleen/default.nix index 5cc32521..5ce93de7 100644 --- a/users/gkleen/default.nix +++ b/users/gkleen/default.nix | |||
@@ -1,7 +1,7 @@ | |||
1 | { flake, userName, pkgs, customUtils, lib, ... }: | 1 | { flake, userName, pkgs, customUtils, lib, ... }: |
2 | { | 2 | { |
3 | imports = with flake.nixosModules.userProfiles.${userName}; [ | 3 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
4 | zsh tmux utils direnv | 4 | utils direnv |
5 | ]; | 5 | ]; |
6 | 6 | ||
7 | users.users.${userName} = { | 7 | users.users.${userName} = { |
diff --git a/users/root.nix b/users/root.nix index b61f9cfd..ed1acd50 100644 --- a/users/root.nix +++ b/users/root.nix | |||
@@ -3,7 +3,7 @@ let | |||
3 | haveGKleen = flake.nixosModules.accounts ? "gkleen@${hostName}"; | 3 | haveGKleen = flake.nixosModules.accounts ? "gkleen@${hostName}"; |
4 | in { | 4 | in { |
5 | imports = with flake.nixosModules.userProfiles.${userName}; [ | 5 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
6 | zsh tmux direnv utils | 6 | direnv utils |
7 | ]; | 7 | ]; |
8 | 8 | ||
9 | users.users.${userName} = lib.mkIf haveGKleen { | 9 | users.users.${userName} = lib.mkIf haveGKleen { |