diff options
208 files changed, 8150 insertions, 4740 deletions
@@ -1,7 +1,5 @@ | |||
1 | **/result | 1 | **/result |
2 | **/result-* | 2 | **/result-* |
3 | **/#*# | ||
4 | **/.#* | ||
5 | **/.gup | 3 | **/.gup |
6 | .direnv | 4 | .direnv |
7 | 5 | ||
@@ -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 ] |
@@ -26,3 +32,6 @@ creation_rules: | |||
26 | - path_regex: ^hosts/sif/ | 32 | - path_regex: ^hosts/sif/ |
27 | key_groups: | 33 | key_groups: |
28 | - age: [ *admin_gkleen, *machine_sif ] | 34 | - age: [ *admin_gkleen, *machine_sif ] |
35 | - path_regex: ^modules/nix-access-tokens/ | ||
36 | key_groups: | ||
37 | - age: [ *admin_gkleen, *machine_sif, *machine_surtr, *machine_vidhar ] | ||
diff --git a/_sources/generated.json b/_sources/generated.json index 47e088c4..d98f141f 100644 --- a/_sources/generated.json +++ b/_sources/generated.json | |||
@@ -20,23 +20,9 @@ | |||
20 | }, | 20 | }, |
21 | "version": "8ef9a5b73e5d1063cf912c70027c655fb19d1109" | 21 | "version": "8ef9a5b73e5d1063cf912c70027c655fb19d1109" |
22 | }, | 22 | }, |
23 | "batman-adv": { | ||
24 | "cargoLocks": null, | ||
25 | "date": null, | ||
26 | "extract": null, | ||
27 | "name": "batman-adv", | ||
28 | "passthru": null, | ||
29 | "pinned": false, | ||
30 | "src": { | ||
31 | "sha256": "sha256-VYyIkH5IFfKN6EOHZxSx6AaepD3a22/hhmLhqkle5Z0=", | ||
32 | "type": "tarball", | ||
33 | "url": "https://downloads.open-mesh.org/batman/stable/sources/batman-adv/batman-adv-2024.4.tar.gz" | ||
34 | }, | ||
35 | "version": "2024.4" | ||
36 | }, | ||
37 | "bpf-examples": { | 23 | "bpf-examples": { |
38 | "cargoLocks": null, | 24 | "cargoLocks": null, |
39 | "date": "2025-01-03", | 25 | "date": "2025-03-06", |
40 | "extract": null, | 26 | "extract": null, |
41 | "name": "bpf-examples", | 27 | "name": "bpf-examples", |
42 | "passthru": null, | 28 | "passthru": null, |
@@ -48,12 +34,12 @@ | |||
48 | "name": null, | 34 | "name": null, |
49 | "owner": "xdp-project", | 35 | "owner": "xdp-project", |
50 | "repo": "bpf-examples", | 36 | "repo": "bpf-examples", |
51 | "rev": "8d53e6fc46ae625bd16b38eb1007ece99460eada", | 37 | "rev": "64e7da048b14822bef06f3971189c4c0985422e7", |
52 | "sha256": "sha256-BUncjyaywmtSMVhbWZDy9XiNlGJet8Z0lzmUqm3f+HU=", | 38 | "sha256": "sha256-cyyRNvU35ujxkLraOqw2oiZwUblBpJaEncPl2++VHL4=", |
53 | "sparseCheckout": [], | 39 | "sparseCheckout": [], |
54 | "type": "github" | 40 | "type": "github" |
55 | }, | 41 | }, |
56 | "version": "8d53e6fc46ae625bd16b38eb1007ece99460eada" | 42 | "version": "64e7da048b14822bef06f3971189c4c0985422e7" |
57 | }, | 43 | }, |
58 | "emacs-scratch_el": { | 44 | "emacs-scratch_el": { |
59 | "cargoLocks": null, | 45 | "cargoLocks": null, |
@@ -90,12 +76,12 @@ | |||
90 | "name": null, | 76 | "name": null, |
91 | "owner": "Mange", | 77 | "owner": "Mange", |
92 | "repo": "emoji-data", | 78 | "repo": "emoji-data", |
93 | "rev": "v2.6", | 79 | "rev": "v2.7", |
94 | "sha256": "sha256-6nBiT9q139P1pXLqkV1JejE0s2rZn1gUbNsejXJR6RU=", | 80 | "sha256": "sha256-bUFh0Q7xcnKTBgVBUJU8BH6zzq1Y3krLfJJAgx5TqKs=", |
95 | "sparseCheckout": [], | 81 | "sparseCheckout": [], |
96 | "type": "github" | 82 | "type": "github" |
97 | }, | 83 | }, |
98 | "version": "v2.6" | 84 | "version": "v2.7" |
99 | }, | 85 | }, |
100 | "lesspipe": { | 86 | "lesspipe": { |
101 | "cargoLocks": null, | 87 | "cargoLocks": null, |
@@ -111,6 +97,26 @@ | |||
111 | }, | 97 | }, |
112 | "version": "2.17" | 98 | "version": "2.17" |
113 | }, | 99 | }, |
100 | "mako": { | ||
101 | "cargoLocks": null, | ||
102 | "date": "2025-04-17", | ||
103 | "extract": null, | ||
104 | "name": "mako", | ||
105 | "passthru": null, | ||
106 | "pinned": false, | ||
107 | "src": { | ||
108 | "deepClone": false, | ||
109 | "fetchSubmodules": false, | ||
110 | "leaveDotGit": false, | ||
111 | "name": null, | ||
112 | "rev": "84637d1cb42def0ef74d6ec961e05c3900b4c23f", | ||
113 | "sha256": "sha256-5Mv0P/SoUiJIiYvGm5wvNZhtz2ytwsqqRc3Pk3ju0WA=", | ||
114 | "sparseCheckout": [], | ||
115 | "type": "git", | ||
116 | "url": "https://github.com/emersion/mako" | ||
117 | }, | ||
118 | "version": "84637d1cb42def0ef74d6ec961e05c3900b4c23f" | ||
119 | }, | ||
114 | "mpv-autosave": { | 120 | "mpv-autosave": { |
115 | "cargoLocks": null, | 121 | "cargoLocks": null, |
116 | "date": "2020-10-22", | 122 | "date": "2020-10-22", |
@@ -196,7 +202,7 @@ | |||
196 | }, | 202 | }, |
197 | "mpv-reload": { | 203 | "mpv-reload": { |
198 | "cargoLocks": null, | 204 | "cargoLocks": null, |
199 | "date": "2024-03-22", | 205 | "date": "2025-02-07", |
200 | "extract": null, | 206 | "extract": null, |
201 | "name": "mpv-reload", | 207 | "name": "mpv-reload", |
202 | "passthru": null, | 208 | "passthru": null, |
@@ -208,16 +214,16 @@ | |||
208 | "name": null, | 214 | "name": null, |
209 | "owner": "4e6", | 215 | "owner": "4e6", |
210 | "repo": "mpv-reload", | 216 | "repo": "mpv-reload", |
211 | "rev": "1a6a9383ba1774708fddbd976e7a9b72c3eec938", | 217 | "rev": "60e6fb1c578aa9af80d725857dac8e439095b033", |
212 | "sha256": "sha256-BshxCjec/UNGyiC0/g1Rai2NvG2qOIHXDDEUYwwdij0=", | 218 | "sha256": "sha256-elA9bi5ov5MbehLD1kyS4Z8zKgTc+8dcOPq32muRGcE=", |
213 | "sparseCheckout": [], | 219 | "sparseCheckout": [], |
214 | "type": "github" | 220 | "type": "github" |
215 | }, | 221 | }, |
216 | "version": "1a6a9383ba1774708fddbd976e7a9b72c3eec938" | 222 | "version": "60e6fb1c578aa9af80d725857dac8e439095b033" |
217 | }, | 223 | }, |
218 | "mpv-subselect": { | 224 | "mpv-subselect": { |
219 | "cargoLocks": null, | 225 | "cargoLocks": null, |
220 | "date": "2024-12-22", | 226 | "date": "2025-04-04", |
221 | "extract": null, | 227 | "extract": null, |
222 | "name": "mpv-subselect", | 228 | "name": "mpv-subselect", |
223 | "passthru": null, | 229 | "passthru": null, |
@@ -227,13 +233,13 @@ | |||
227 | "fetchSubmodules": false, | 233 | "fetchSubmodules": false, |
228 | "leaveDotGit": false, | 234 | "leaveDotGit": false, |
229 | "name": null, | 235 | "name": null, |
230 | "rev": "77d0148aa6aa952f07f06212cabe32d54dfdf49e", | 236 | "rev": "26d24a0fd1d69988eaedda6056a2c87d0a55b6cb", |
231 | "sha256": "sha256-VxwwTxE8c8rRQt/m2NA7cRC7+7O1ItYFFGv81nxqIxg=", | 237 | "sha256": "sha256-+eVga4b7KIBnfrtmlgq/0HNjQVS3SK6YWVXCPvOeOOc=", |
232 | "sparseCheckout": [], | 238 | "sparseCheckout": [], |
233 | "type": "git", | 239 | "type": "git", |
234 | "url": "https://github.com/CogentRedTester/mpv-sub-select" | 240 | "url": "https://github.com/CogentRedTester/mpv-sub-select" |
235 | }, | 241 | }, |
236 | "version": "77d0148aa6aa952f07f06212cabe32d54dfdf49e" | 242 | "version": "26d24a0fd1d69988eaedda6056a2c87d0a55b6cb" |
237 | }, | 243 | }, |
238 | "mpv-youtube-quality": { | 244 | "mpv-youtube-quality": { |
239 | "cargoLocks": null, | 245 | "cargoLocks": null, |
@@ -255,6 +261,36 @@ | |||
255 | }, | 261 | }, |
256 | "version": "1f8c31457459ffc28cd1c3f3c2235a53efad7148" | 262 | "version": "1f8c31457459ffc28cd1c3f3c2235a53efad7148" |
257 | }, | 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 | }, | ||
258 | "postfix-mta-sts-resolver": { | 294 | "postfix-mta-sts-resolver": { |
259 | "cargoLocks": null, | 295 | "cargoLocks": null, |
260 | "date": null, | 296 | "date": null, |
@@ -263,11 +299,11 @@ | |||
263 | "passthru": null, | 299 | "passthru": null, |
264 | "pinned": false, | 300 | "pinned": false, |
265 | "src": { | 301 | "src": { |
266 | "sha256": "sha256-wpBbT/KXd2iU6Jn6Y/6i5C+Wv3OsUTjFcJDhUm6w46I=", | 302 | "sha256": "sha256-DrPWxAlzdtb5K0Z+yVi+rL1h7CyLj0/Fiio8B2H/Ssg=", |
267 | "type": "tarball", | 303 | "type": "tarball", |
268 | "url": "https://github.com/Snawoot/postfix-mta-sts-resolver/archive/refs/tags/v1.4.0.tar.gz" | 304 | "url": "https://github.com/Snawoot/postfix-mta-sts-resolver/archive/refs/tags/v1.5.0.tar.gz" |
269 | }, | 305 | }, |
270 | "version": "1.4.0" | 306 | "version": "1.5.0" |
271 | }, | 307 | }, |
272 | "postfwd": { | 308 | "postfwd": { |
273 | "cargoLocks": null, | 309 | "cargoLocks": null, |
@@ -359,6 +395,26 @@ | |||
359 | }, | 395 | }, |
360 | "version": "0.2.1" | 396 | "version": "0.2.1" |
361 | }, | 397 | }, |
398 | "swayosd": { | ||
399 | "cargoLocks": null, | ||
400 | "date": "2025-04-20", | ||
401 | "extract": null, | ||
402 | "name": "swayosd", | ||
403 | "passthru": null, | ||
404 | "pinned": false, | ||
405 | "src": { | ||
406 | "deepClone": false, | ||
407 | "fetchSubmodules": false, | ||
408 | "leaveDotGit": false, | ||
409 | "name": null, | ||
410 | "rev": "ce1f34d80a7f8b4393a5551ea0535bd8beabb28c", | ||
411 | "sha256": "sha256-Z9c/5jKxs5ctUuVu7g+BXA1Wy4lyZLpGATtj2jd84jI=", | ||
412 | "sparseCheckout": [], | ||
413 | "type": "git", | ||
414 | "url": "https://github.com/ErikReider/SwayOSD" | ||
415 | }, | ||
416 | "version": "ce1f34d80a7f8b4393a5551ea0535bd8beabb28c" | ||
417 | }, | ||
362 | "tomorrow-night-paradise-theme": { | 418 | "tomorrow-night-paradise-theme": { |
363 | "cargoLocks": null, | 419 | "cargoLocks": null, |
364 | "date": "2012-06-04", | 420 | "date": "2012-06-04", |
@@ -381,7 +437,7 @@ | |||
381 | }, | 437 | }, |
382 | "v4l2loopback": { | 438 | "v4l2loopback": { |
383 | "cargoLocks": null, | 439 | "cargoLocks": null, |
384 | "date": "2024-11-26", | 440 | "date": "2025-04-29", |
385 | "extract": null, | 441 | "extract": null, |
386 | "name": "v4l2loopback", | 442 | "name": "v4l2loopback", |
387 | "passthru": null, | 443 | "passthru": null, |
@@ -393,16 +449,16 @@ | |||
393 | "name": null, | 449 | "name": null, |
394 | "owner": "umlaeute", | 450 | "owner": "umlaeute", |
395 | "repo": "v4l2loopback", | 451 | "repo": "v4l2loopback", |
396 | "rev": "e750af9eb17d729b8c5257a4bcd2faba2b28029c", | 452 | "rev": "8d806ad688961d8840081a609c39d1a82d296b24", |
397 | "sha256": "sha256-ePA1LcxQInrLLpbZ7Wljv75lWl6V6s9KkdMp0tF1vhk=", | 453 | "sha256": "sha256-zuE/qFI8QCWCePmHWjTIPTh2KzmDkwQ2uj5C1dAwo1c=", |
398 | "sparseCheckout": [], | 454 | "sparseCheckout": [], |
399 | "type": "github" | 455 | "type": "github" |
400 | }, | 456 | }, |
401 | "version": "e750af9eb17d729b8c5257a4bcd2faba2b28029c" | 457 | "version": "8d806ad688961d8840081a609c39d1a82d296b24" |
402 | }, | 458 | }, |
403 | "xcompose": { | 459 | "xcompose": { |
404 | "cargoLocks": null, | 460 | "cargoLocks": null, |
405 | "date": "2022-09-14", | 461 | "date": "2025-03-11", |
406 | "extract": null, | 462 | "extract": null, |
407 | "name": "xcompose", | 463 | "name": "xcompose", |
408 | "passthru": null, | 464 | "passthru": null, |
@@ -414,12 +470,12 @@ | |||
414 | "name": null, | 470 | "name": null, |
415 | "owner": "kragen", | 471 | "owner": "kragen", |
416 | "repo": "xcompose", | 472 | "repo": "xcompose", |
417 | "rev": "cd8d3e622f547ec9f83d7f64f51d4a27ee812681", | 473 | "rev": "8b5a6a0c788fd0a4b921d9d3737174defb863873", |
418 | "sha256": "sha256-fkl2lDv/DdrqPjVsEUKSRD3BNGwTjTsA0ovI8akFI6U=", | 474 | "sha256": "sha256-6EjQErdBOd5hqcrdaf88E1UZVYIc3FOfv34hvUwOWdA=", |
419 | "sparseCheckout": [], | 475 | "sparseCheckout": [], |
420 | "type": "github" | 476 | "type": "github" |
421 | }, | 477 | }, |
422 | "version": "cd8d3e622f547ec9f83d7f64f51d4a27ee812681" | 478 | "version": "8b5a6a0c788fd0a4b921d9d3737174defb863873" |
423 | }, | 479 | }, |
424 | "yt-dlp": { | 480 | "yt-dlp": { |
425 | "cargoLocks": null, | 481 | "cargoLocks": null, |
@@ -430,10 +486,10 @@ | |||
430 | "pinned": false, | 486 | "pinned": false, |
431 | "src": { | 487 | "src": { |
432 | "name": null, | 488 | "name": null, |
433 | "sha256": "sha256-rA5ytakBe6EEtCWFRiAafO3DjovSByfgxjt3yCm0Jek=", | 489 | "sha256": "sha256-6nOFTF2rwSTymjWo+um8XUIu8yMb6+6ivfqCrBkanCk=", |
434 | "type": "url", | 490 | "type": "url", |
435 | "url": "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2024.12.23.tar.gz" | 491 | "url": "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2025.5.22.tar.gz" |
436 | }, | 492 | }, |
437 | "version": "2024.12.23" | 493 | "version": "2025.5.22" |
438 | } | 494 | } |
439 | } \ No newline at end of file | 495 | } \ No newline at end of file |
diff --git a/_sources/generated.nix b/_sources/generated.nix index 161dc4e1..3bf73fed 100644 --- a/_sources/generated.nix +++ b/_sources/generated.nix | |||
@@ -16,25 +16,17 @@ | |||
16 | }; | 16 | }; |
17 | date = "2021-05-30"; | 17 | date = "2021-05-30"; |
18 | }; | 18 | }; |
19 | batman-adv = { | ||
20 | pname = "batman-adv"; | ||
21 | version = "2024.4"; | ||
22 | src = fetchTarball { | ||
23 | url = "https://downloads.open-mesh.org/batman/stable/sources/batman-adv/batman-adv-2024.4.tar.gz"; | ||
24 | sha256 = "sha256-VYyIkH5IFfKN6EOHZxSx6AaepD3a22/hhmLhqkle5Z0="; | ||
25 | }; | ||
26 | }; | ||
27 | bpf-examples = { | 19 | bpf-examples = { |
28 | pname = "bpf-examples"; | 20 | pname = "bpf-examples"; |
29 | version = "8d53e6fc46ae625bd16b38eb1007ece99460eada"; | 21 | version = "64e7da048b14822bef06f3971189c4c0985422e7"; |
30 | src = fetchFromGitHub { | 22 | src = fetchFromGitHub { |
31 | owner = "xdp-project"; | 23 | owner = "xdp-project"; |
32 | repo = "bpf-examples"; | 24 | repo = "bpf-examples"; |
33 | rev = "8d53e6fc46ae625bd16b38eb1007ece99460eada"; | 25 | rev = "64e7da048b14822bef06f3971189c4c0985422e7"; |
34 | fetchSubmodules = true; | 26 | fetchSubmodules = true; |
35 | sha256 = "sha256-BUncjyaywmtSMVhbWZDy9XiNlGJet8Z0lzmUqm3f+HU="; | 27 | sha256 = "sha256-cyyRNvU35ujxkLraOqw2oiZwUblBpJaEncPl2++VHL4="; |
36 | }; | 28 | }; |
37 | date = "2025-01-03"; | 29 | date = "2025-03-06"; |
38 | }; | 30 | }; |
39 | emacs-scratch_el = { | 31 | emacs-scratch_el = { |
40 | pname = "emacs-scratch_el"; | 32 | pname = "emacs-scratch_el"; |
@@ -50,13 +42,13 @@ | |||
50 | }; | 42 | }; |
51 | emoji-data = { | 43 | emoji-data = { |
52 | pname = "emoji-data"; | 44 | pname = "emoji-data"; |
53 | version = "v2.6"; | 45 | version = "v2.7"; |
54 | src = fetchFromGitHub { | 46 | src = fetchFromGitHub { |
55 | owner = "Mange"; | 47 | owner = "Mange"; |
56 | repo = "emoji-data"; | 48 | repo = "emoji-data"; |
57 | rev = "v2.6"; | 49 | rev = "v2.7"; |
58 | fetchSubmodules = true; | 50 | fetchSubmodules = true; |
59 | sha256 = "sha256-6nBiT9q139P1pXLqkV1JejE0s2rZn1gUbNsejXJR6RU="; | 51 | sha256 = "sha256-bUFh0Q7xcnKTBgVBUJU8BH6zzq1Y3krLfJJAgx5TqKs="; |
60 | }; | 52 | }; |
61 | }; | 53 | }; |
62 | lesspipe = { | 54 | lesspipe = { |
@@ -67,6 +59,20 @@ | |||
67 | sha256 = "sha256-afJuTByGUMU6kFqGGa3pbPaFVdYGcJYiR0RfDNYNgDk="; | 59 | sha256 = "sha256-afJuTByGUMU6kFqGGa3pbPaFVdYGcJYiR0RfDNYNgDk="; |
68 | }; | 60 | }; |
69 | }; | 61 | }; |
62 | mako = { | ||
63 | pname = "mako"; | ||
64 | version = "84637d1cb42def0ef74d6ec961e05c3900b4c23f"; | ||
65 | src = fetchgit { | ||
66 | url = "https://github.com/emersion/mako"; | ||
67 | rev = "84637d1cb42def0ef74d6ec961e05c3900b4c23f"; | ||
68 | fetchSubmodules = false; | ||
69 | deepClone = false; | ||
70 | leaveDotGit = false; | ||
71 | sparseCheckout = [ ]; | ||
72 | sha256 = "sha256-5Mv0P/SoUiJIiYvGm5wvNZhtz2ytwsqqRc3Pk3ju0WA="; | ||
73 | }; | ||
74 | date = "2025-04-17"; | ||
75 | }; | ||
70 | mpv-autosave = { | 76 | mpv-autosave = { |
71 | pname = "mpv-autosave"; | 77 | pname = "mpv-autosave"; |
72 | version = "744c3ee61d2f0a8e9bb4e308dec6897215ae4704"; | 78 | version = "744c3ee61d2f0a8e9bb4e308dec6897215ae4704"; |
@@ -118,29 +124,29 @@ | |||
118 | }; | 124 | }; |
119 | mpv-reload = { | 125 | mpv-reload = { |
120 | pname = "mpv-reload"; | 126 | pname = "mpv-reload"; |
121 | version = "1a6a9383ba1774708fddbd976e7a9b72c3eec938"; | 127 | version = "60e6fb1c578aa9af80d725857dac8e439095b033"; |
122 | src = fetchFromGitHub { | 128 | src = fetchFromGitHub { |
123 | owner = "4e6"; | 129 | owner = "4e6"; |
124 | repo = "mpv-reload"; | 130 | repo = "mpv-reload"; |
125 | rev = "1a6a9383ba1774708fddbd976e7a9b72c3eec938"; | 131 | rev = "60e6fb1c578aa9af80d725857dac8e439095b033"; |
126 | fetchSubmodules = false; | 132 | fetchSubmodules = false; |
127 | sha256 = "sha256-BshxCjec/UNGyiC0/g1Rai2NvG2qOIHXDDEUYwwdij0="; | 133 | sha256 = "sha256-elA9bi5ov5MbehLD1kyS4Z8zKgTc+8dcOPq32muRGcE="; |
128 | }; | 134 | }; |
129 | date = "2024-03-22"; | 135 | date = "2025-02-07"; |
130 | }; | 136 | }; |
131 | mpv-subselect = { | 137 | mpv-subselect = { |
132 | pname = "mpv-subselect"; | 138 | pname = "mpv-subselect"; |
133 | version = "77d0148aa6aa952f07f06212cabe32d54dfdf49e"; | 139 | version = "26d24a0fd1d69988eaedda6056a2c87d0a55b6cb"; |
134 | src = fetchgit { | 140 | src = fetchgit { |
135 | url = "https://github.com/CogentRedTester/mpv-sub-select"; | 141 | url = "https://github.com/CogentRedTester/mpv-sub-select"; |
136 | rev = "77d0148aa6aa952f07f06212cabe32d54dfdf49e"; | 142 | rev = "26d24a0fd1d69988eaedda6056a2c87d0a55b6cb"; |
137 | fetchSubmodules = false; | 143 | fetchSubmodules = false; |
138 | deepClone = false; | 144 | deepClone = false; |
139 | leaveDotGit = false; | 145 | leaveDotGit = false; |
140 | sparseCheckout = [ ]; | 146 | sparseCheckout = [ ]; |
141 | sha256 = "sha256-VxwwTxE8c8rRQt/m2NA7cRC7+7O1ItYFFGv81nxqIxg="; | 147 | sha256 = "sha256-+eVga4b7KIBnfrtmlgq/0HNjQVS3SK6YWVXCPvOeOOc="; |
142 | }; | 148 | }; |
143 | date = "2024-12-22"; | 149 | date = "2025-04-04"; |
144 | }; | 150 | }; |
145 | mpv-youtube-quality = { | 151 | mpv-youtube-quality = { |
146 | pname = "mpv-youtube-quality"; | 152 | pname = "mpv-youtube-quality"; |
@@ -156,12 +162,28 @@ | |||
156 | }; | 162 | }; |
157 | date = "2020-02-10"; | 163 | date = "2020-02-10"; |
158 | }; | 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 | }; | ||
159 | postfix-mta-sts-resolver = { | 181 | postfix-mta-sts-resolver = { |
160 | pname = "postfix-mta-sts-resolver"; | 182 | pname = "postfix-mta-sts-resolver"; |
161 | version = "1.4.0"; | 183 | version = "1.5.0"; |
162 | src = fetchTarball { | 184 | src = fetchTarball { |
163 | url = "https://github.com/Snawoot/postfix-mta-sts-resolver/archive/refs/tags/v1.4.0.tar.gz"; | 185 | url = "https://github.com/Snawoot/postfix-mta-sts-resolver/archive/refs/tags/v1.5.0.tar.gz"; |
164 | sha256 = "sha256-wpBbT/KXd2iU6Jn6Y/6i5C+Wv3OsUTjFcJDhUm6w46I="; | 186 | sha256 = "sha256-DrPWxAlzdtb5K0Z+yVi+rL1h7CyLj0/Fiio8B2H/Ssg="; |
165 | }; | 187 | }; |
166 | }; | 188 | }; |
167 | postfwd = { | 189 | postfwd = { |
@@ -218,6 +240,20 @@ | |||
218 | sha256 = "sha256-7d/0fepOvdswuBGJCCMULB2kXOFBLP78yqX4NmByCF8="; | 240 | sha256 = "sha256-7d/0fepOvdswuBGJCCMULB2kXOFBLP78yqX4NmByCF8="; |
219 | }; | 241 | }; |
220 | }; | 242 | }; |
243 | swayosd = { | ||
244 | pname = "swayosd"; | ||
245 | version = "ce1f34d80a7f8b4393a5551ea0535bd8beabb28c"; | ||
246 | src = fetchgit { | ||
247 | url = "https://github.com/ErikReider/SwayOSD"; | ||
248 | rev = "ce1f34d80a7f8b4393a5551ea0535bd8beabb28c"; | ||
249 | fetchSubmodules = false; | ||
250 | deepClone = false; | ||
251 | leaveDotGit = false; | ||
252 | sparseCheckout = [ ]; | ||
253 | sha256 = "sha256-Z9c/5jKxs5ctUuVu7g+BXA1Wy4lyZLpGATtj2jd84jI="; | ||
254 | }; | ||
255 | date = "2025-04-20"; | ||
256 | }; | ||
221 | tomorrow-night-paradise-theme = { | 257 | tomorrow-night-paradise-theme = { |
222 | pname = "tomorrow-night-paradise-theme"; | 258 | pname = "tomorrow-night-paradise-theme"; |
223 | version = "70225a5bf90d495e13a9260bfdc268632ece0801"; | 259 | version = "70225a5bf90d495e13a9260bfdc268632ece0801"; |
@@ -234,34 +270,34 @@ | |||
234 | }; | 270 | }; |
235 | v4l2loopback = { | 271 | v4l2loopback = { |
236 | pname = "v4l2loopback"; | 272 | pname = "v4l2loopback"; |
237 | version = "e750af9eb17d729b8c5257a4bcd2faba2b28029c"; | 273 | version = "8d806ad688961d8840081a609c39d1a82d296b24"; |
238 | src = fetchFromGitHub { | 274 | src = fetchFromGitHub { |
239 | owner = "umlaeute"; | 275 | owner = "umlaeute"; |
240 | repo = "v4l2loopback"; | 276 | repo = "v4l2loopback"; |
241 | rev = "e750af9eb17d729b8c5257a4bcd2faba2b28029c"; | 277 | rev = "8d806ad688961d8840081a609c39d1a82d296b24"; |
242 | fetchSubmodules = true; | 278 | fetchSubmodules = true; |
243 | sha256 = "sha256-ePA1LcxQInrLLpbZ7Wljv75lWl6V6s9KkdMp0tF1vhk="; | 279 | sha256 = "sha256-zuE/qFI8QCWCePmHWjTIPTh2KzmDkwQ2uj5C1dAwo1c="; |
244 | }; | 280 | }; |
245 | date = "2024-11-26"; | 281 | date = "2025-04-29"; |
246 | }; | 282 | }; |
247 | xcompose = { | 283 | xcompose = { |
248 | pname = "xcompose"; | 284 | pname = "xcompose"; |
249 | version = "cd8d3e622f547ec9f83d7f64f51d4a27ee812681"; | 285 | version = "8b5a6a0c788fd0a4b921d9d3737174defb863873"; |
250 | src = fetchFromGitHub { | 286 | src = fetchFromGitHub { |
251 | owner = "kragen"; | 287 | owner = "kragen"; |
252 | repo = "xcompose"; | 288 | repo = "xcompose"; |
253 | rev = "cd8d3e622f547ec9f83d7f64f51d4a27ee812681"; | 289 | rev = "8b5a6a0c788fd0a4b921d9d3737174defb863873"; |
254 | fetchSubmodules = false; | 290 | fetchSubmodules = false; |
255 | sha256 = "sha256-fkl2lDv/DdrqPjVsEUKSRD3BNGwTjTsA0ovI8akFI6U="; | 291 | sha256 = "sha256-6EjQErdBOd5hqcrdaf88E1UZVYIc3FOfv34hvUwOWdA="; |
256 | }; | 292 | }; |
257 | date = "2022-09-14"; | 293 | date = "2025-03-11"; |
258 | }; | 294 | }; |
259 | yt-dlp = { | 295 | yt-dlp = { |
260 | pname = "yt-dlp"; | 296 | pname = "yt-dlp"; |
261 | version = "2024.12.23"; | 297 | version = "2025.5.22"; |
262 | src = fetchurl { | 298 | src = fetchurl { |
263 | url = "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2024.12.23.tar.gz"; | 299 | url = "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2025.5.22.tar.gz"; |
264 | sha256 = "sha256-rA5ytakBe6EEtCWFRiAafO3DjovSByfgxjt3yCm0Jek="; | 300 | sha256 = "sha256-6nOFTF2rwSTymjWo+um8XUIu8yMb6+6ivfqCrBkanCk="; |
265 | }; | 301 | }; |
266 | }; | 302 | }; |
267 | } | 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 bcfd1224..706eb241 100644 --- a/accounts/gkleen@sif/default.nix +++ b/accounts/gkleen@sif/default.nix | |||
@@ -4,33 +4,6 @@ with lib; | |||
4 | 4 | ||
5 | let | 5 | let |
6 | cfg = config.home-manager.users.${userName}; | 6 | cfg = config.home-manager.users.${userName}; |
7 | emacsScratch = pkgs.stdenv.mkDerivation (sources.emacs-scratch_el // rec { | ||
8 | phases = [ "installPhase" ]; | ||
9 | |||
10 | installPhase = '' | ||
11 | mkdir -p $out/share/emacs/site-lisp | ||
12 | cp $src/scratch.el $out/share/emacs/site-lisp/default.el | ||
13 | ''; | ||
14 | }); | ||
15 | muteScript = pkgs.stdenv.mkDerivation { | ||
16 | name = "mute"; | ||
17 | src = ./scripts/mute.zsh; | ||
18 | |||
19 | buildInputs = with pkgs; [ makeWrapper ]; | ||
20 | |||
21 | phases = [ "installPhase" ]; | ||
22 | |||
23 | installPhase = '' | ||
24 | mkdir -p $out/bin | ||
25 | install -m 0755 $src $out/bin/mute | ||
26 | wrapProgram $out/bin/mute \ | ||
27 | --prefix PATH : ${pkgs.zsh}/bin \ | ||
28 | --prefix PATH : ${pkgs.findutils}/bin \ | ||
29 | --prefix PATH : ${pkgs.util-linux}/bin \ | ||
30 | --prefix PATH : ${pkgs.coreutils}/bin \ | ||
31 | --prefix PATH : ${pkgs.pulseaudio}/bin | ||
32 | ''; | ||
33 | }; | ||
34 | wrapElectron = { package, bin ? package.meta.mainProgram or package.pname or (pkgs.lib.strings.nameFromURL package.name "-"), outBin ? bin, sandbox ? true }: pkgs.symlinkJoin { | 7 | wrapElectron = { package, bin ? package.meta.mainProgram or package.pname or (pkgs.lib.strings.nameFromURL package.name "-"), outBin ? bin, sandbox ? true }: pkgs.symlinkJoin { |
35 | name = "${package.name}-wrapped"; | 8 | name = "${package.name}-wrapped"; |
36 | buildInputs = with pkgs; [ makeWrapper ]; | 9 | buildInputs = with pkgs; [ makeWrapper ]; |
@@ -47,10 +20,6 @@ let | |||
47 | ''; | 20 | ''; |
48 | }; | 21 | }; |
49 | 22 | ||
50 | wrappedChrome = wrapElectron { package = pkgs.google-chrome; outBin = "google-chrome"; }; | ||
51 | wrappedZulip = wrapElectron { package = pkgs.zulip; bin = "zulip"; outBin = "zulip"; }; | ||
52 | wrappedElementDesktop = wrapElectron { package = pkgs.element-desktop; bin = "element-desktop"; }; | ||
53 | wrappedRocketChatDesktop = wrapElectron { package = pkgs.rocketchat-desktop; bin = "rocketchat-desktop"; outBin = "rocketchat"; }; | ||
54 | wrappedYTMDesktop = wrapElectron { package = pkgs.ytmdesktop; sandbox = false; }; | 23 | wrappedYTMDesktop = wrapElectron { package = pkgs.ytmdesktop; sandbox = false; }; |
55 | 24 | ||
56 | wrappedKeepassxc = pkgs.symlinkJoin { | 25 | wrappedKeepassxc = pkgs.symlinkJoin { |
@@ -63,7 +32,7 @@ let | |||
63 | text = '' | 32 | text = '' |
64 | [D-BUS Service] | 33 | [D-BUS Service] |
65 | Name=org.keepassxc.KeePassXC.MainWindow | 34 | Name=org.keepassxc.KeePassXC.MainWindow |
66 | Exec=${pkgs.coreutils}/bin/false | 35 | Exec=${lib.getExe' pkgs.coreutils "false"} |
67 | SystemdService=keepassxc.service | 36 | SystemdService=keepassxc.service |
68 | ''; | 37 | ''; |
69 | }) | 38 | }) |
@@ -73,36 +42,46 @@ let | |||
73 | text = '' | 42 | text = '' |
74 | [D-BUS Service] | 43 | [D-BUS Service] |
75 | Name=org.freedesktop.secrets | 44 | Name=org.freedesktop.secrets |
76 | Exec=${pkgs.coreutils}/bin/false | 45 | Exec=${lib.getExe' pkgs.coreutils "false"} |
77 | SystemdService=keepassxc.service | 46 | SystemdService=keepassxc.service |
78 | ''; | 47 | ''; |
79 | }) | 48 | }) |
80 | ]; | 49 | ]; |
81 | }; | 50 | }; |
82 | 51 | ||
83 | lockCommand = "${config.systemd.package}/bin/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 | }; | ||
84 | in { | 64 | in { |
85 | imports = with flake.nixosModules.userProfiles.${userName}; [ | 65 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
86 | mpv yt-dlp (args: import ./xcompose.nix (inputs // args)) | 66 | zsh tmux mpv yt-dlp (args: import ./xcompose.nix (inputs // args)) |
87 | ]; | 67 | ]; |
88 | 68 | ||
89 | config = { | 69 | config = { |
90 | services.displayManager.defaultSession = "Hyprland"; # "none+xmonad"; | ||
91 | |||
92 | home-manager.users.${userName} = { | 70 | home-manager.users.${userName} = { |
93 | imports = [ | 71 | imports = [ |
94 | ./libvirt | 72 | ./libvirt |
95 | ./niri | 73 | ./niri |
74 | ./synadm | ||
96 | flakeInputs.nix-index-database.hmModules.nix-index | 75 | flakeInputs.nix-index-database.hmModules.nix-index |
97 | flakeInputs.impermanence.nixosModules.home-manager.impermanence | 76 | flakeInputs.impermanence.nixosModules.home-manager.impermanence |
98 | ]; | 77 | ]; |
99 | 78 | ||
100 | home.stateVersion = "20.09"; | 79 | home.stateVersion = "20.09"; |
101 | 80 | ||
102 | nixpkgs.config = { | 81 | # nixpkgs.config = { |
103 | allowUnfree = true; | 82 | # allowUnfree = true; |
104 | zathura.useMupdf = false; | 83 | # zathura.useMupdf = false; |
105 | }; | 84 | # }; |
106 | 85 | ||
107 | nix.registry = { | 86 | nix.registry = { |
108 | "flk" = { | 87 | "flk" = { |
@@ -112,14 +91,14 @@ in { | |||
112 | }; | 91 | }; |
113 | to = { | 92 | to = { |
114 | type = "git"; | 93 | type = "git"; |
115 | url = "file:///home/gkleen/config/nixos-flakes"; | 94 | url = "file:///home/gkleen/projects/machines"; |
116 | }; | 95 | }; |
117 | }; | 96 | }; |
118 | }; | 97 | }; |
119 | 98 | ||
120 | programs = { | 99 | programs = { |
121 | ssh = { | 100 | ssh = { |
122 | matchBlocks = import ./ssh-hosts.nix { inherit pkgs; }; # customUtils.nixImport { dir = ./ssh-hosts; }; | 101 | matchBlocks = import ./ssh-hosts.nix inputs; # customUtils.nixImport { dir = ./ssh-hosts; }; |
123 | extraConfig = '' | 102 | extraConfig = '' |
124 | Match host uniworx3.ifi.lmu.de,uniworx4.ifi.lmu.de,uniworx5.ifi.lmu.de,uni2workgw.ifi.lmu.de,blackbeard.tcs.ifi.lmu.de,gitlab2.rz.ifi.lmu.de,oregon.tcs.ifi.lmu.de !exec "nc -z -w 1 %h %p &>/dev/null" | 103 | Match host uniworx3.ifi.lmu.de,uniworx4.ifi.lmu.de,uniworx5.ifi.lmu.de,uni2workgw.ifi.lmu.de,blackbeard.tcs.ifi.lmu.de,gitlab2.rz.ifi.lmu.de,oregon.tcs.ifi.lmu.de !exec "nc -z -w 1 %h %p &>/dev/null" |
125 | ProxyJump remote.cip.ifi.lmu.de | 104 | ProxyJump remote.cip.ifi.lmu.de |
@@ -137,8 +116,8 @@ in { | |||
137 | ''} | 116 | ''} |
138 | 117 | ||
139 | Match host *.mathinst.loc,*.math.lmu.de !host ssh.math.lmu.de !exec "nc -z -w 1 %h %p &>/dev/null" | 118 | Match host *.mathinst.loc,*.math.lmu.de !host ssh.math.lmu.de !exec "nc -z -w 1 %h %p &>/dev/null" |
140 | # ProxyCommand ${pkgs.socat}/bin/socat - SOCKS4A:127.0.0.1:%h:%p,socksport=8118 | 119 | ProxyCommand ${lib.getExe pkgs.socat} - SOCKS4A:127.0.0.1:%h:%p,socksport=8118 |
141 | ProxyJump ssh.math.lmu.de | 120 | # ProxyJump ssh.math.lmu.de |
142 | 121 | ||
143 | Match host *.cipmath.loc !host cip04.cipmath.loc,mgmt-cls01.cipmath.loc !exec "nc -z -w 1 %h %p &>/dev/null" | 122 | Match host *.cipmath.loc !host cip04.cipmath.loc,mgmt-cls01.cipmath.loc !exec "nc -z -w 1 %h %p &>/dev/null" |
144 | ProxyJump cip04 | 123 | ProxyJump cip04 |
@@ -155,22 +134,31 @@ in { | |||
155 | 134 | ||
156 | emacs = { | 135 | emacs = { |
157 | enable = true; | 136 | enable = true; |
158 | package = pkgs.emacs29-pgtk; | 137 | package = pkgs.emacs-pgtk; |
159 | extraPackages = epkgs: with epkgs; [ | 138 | extraPackages = epkgs: with epkgs; [ |
160 | evil evil-dvorak undo-tree magit haskell-tng-mode nix-mode | 139 | evil evil-dvorak undo-tree magit haskell-tng-mode nix-mode |
161 | yaml-mode json-mode shakespeare-mode smart-mode-line | 140 | yaml-mode json-mode shakespeare-mode smart-mode-line |
162 | highlight-parentheses highlight-symbol ag sass-mode lua-mode | 141 | highlight-parentheses highlight-symbol ag sass-mode |
163 | fira-code-mode use-package wanderlust # notmuch | 142 | lua-mode fira-code-mode use-package wanderlust # notmuch |
164 | git-gutter emacsScratch | 143 | git-gutter scratch edit-server mediawiki editorconfig |
165 | edit-server mediawiki editorconfig typescript-mode | 144 | typescript-mode markdown-mode nftables-mode rustic |
166 | markdown-mode nftables-mode rustic lsp-mode lsp-ui | 145 | lsp-mode lsp-ui direnv company projectile |
167 | direnv company projectile tomorrow-night-paradise-theme | 146 | tomorrow-night-paradise-theme |
168 | treesit-grammars.with-all-grammars magit-delta scad-mode | 147 | treesit-grammars.with-all-grammars magit-delta scad-mode |
169 | ]; | 148 | ]; |
170 | overrides = self: super: { | 149 | overrides = self: super: { |
171 | tomorrow-night-paradise-theme = super.trivialBuild { | 150 | tomorrow-night-paradise-theme = super.trivialBuild { |
172 | inherit (sources.tomorrow-night-paradise-theme) pname version src; | 151 | inherit (sources.tomorrow-night-paradise-theme) pname version src; |
173 | }; | 152 | }; |
153 | scratch = pkgs.stdenv.mkDerivation { | ||
154 | inherit (sources.emacs-scratch_el) pname version src; | ||
155 | |||
156 | phases = [ "unpackPhase" "installPhase" ]; | ||
157 | |||
158 | installPhase = '' | ||
159 | install -Dt $out/share/emacs/site-lisp scratch.el | ||
160 | ''; | ||
161 | }; | ||
174 | }; | 162 | }; |
175 | }; | 163 | }; |
176 | firefox = { | 164 | firefox = { |
@@ -185,7 +173,12 @@ in { | |||
185 | }; | 173 | }; |
186 | }; | 174 | }; |
187 | 175 | ||
188 | zathura.enable = true; | 176 | zathura = { |
177 | enable = true; | ||
178 | options = { | ||
179 | scroll-page-aware = true; | ||
180 | }; | ||
181 | }; | ||
189 | imv.enable = true; | 182 | imv.enable = true; |
190 | 183 | ||
191 | mpv.config = { | 184 | mpv.config = { |
@@ -194,13 +187,91 @@ in { | |||
194 | gpu-api = "vulkan"; | 187 | gpu-api = "vulkan"; |
195 | }; | 188 | }; |
196 | 189 | ||
197 | zsh.initExtra = '' | 190 | zsh.initContent = let |
198 | 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 | ||
199 | ''; | 271 | ''; |
200 | zsh.dirHashes = let | 272 | zsh.dirHashes = let |
201 | 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; |
202 | inputNames = { | 274 | inputNames = { |
203 | "nixpkgs" = "nixos"; | ||
204 | }; | 275 | }; |
205 | in flakeHashes // { | 276 | in flakeHashes // { |
206 | u2w = "$HOME/projects/uni2work"; | 277 | u2w = "$HOME/projects/uni2work"; |
@@ -212,6 +283,16 @@ in { | |||
212 | pro = "$HOME/projects/pro"; | 283 | pro = "$HOME/projects/pro"; |
213 | media = "$HOME/media"; | 284 | media = "$HOME/media"; |
214 | }; | 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 | }; | ||
215 | 296 | ||
216 | obs-studio = { | 297 | obs-studio = { |
217 | enable = true; | 298 | enable = true; |
@@ -221,7 +302,7 @@ in { | |||
221 | gh = { | 302 | gh = { |
222 | enable = true; | 303 | enable = true; |
223 | settings = { | 304 | settings = { |
224 | editor = "${config.home-manager.users.${userName}.programs.emacs.package}/bin/emacsclient"; | 305 | editor = lib.getExe' editor "emacsclient"; |
225 | gitProtocol = "ssh"; | 306 | gitProtocol = "ssh"; |
226 | }; | 307 | }; |
227 | }; | 308 | }; |
@@ -247,16 +328,10 @@ in { | |||
247 | # notify_on_cmd_finish = "invisible 120"; | 328 | # notify_on_cmd_finish = "invisible 120"; |
248 | }; | 329 | }; |
249 | keybindings = { | 330 | keybindings = { |
250 | "kitty_mod+n" = "detach_window"; | 331 | "kitty_mod+n" = "new_os_window_with_cwd"; |
251 | "kitty_mod+m" = "detach_window ask"; | 332 | "kitty_mod+m" = "detach_window ask"; |
252 | }; | 333 | "kitty_mod+enter" = "new_window_with_cwd"; |
253 | }; | 334 | "kitty_mod+t" = "new_tab_with_cwd"; |
254 | wpaperd = { | ||
255 | enable = true; | ||
256 | settings.default = { | ||
257 | path = "~/.wallpapers"; | ||
258 | duration = "8h"; | ||
259 | mode = "center"; | ||
260 | }; | 335 | }; |
261 | }; | 336 | }; |
262 | fuzzel = { | 337 | fuzzel = { |
@@ -269,7 +344,7 @@ in { | |||
269 | font = "Fira Sans"; | 344 | font = "Fira Sans"; |
270 | }; | 345 | }; |
271 | colors = { | 346 | colors = { |
272 | background = "000000aa"; | 347 | background = "000000cc"; |
273 | text = "cdd6f4ff"; | 348 | text = "cdd6f4ff"; |
274 | match = "94e2d5ff"; | 349 | match = "94e2d5ff"; |
275 | selection = "585b70ff"; | 350 | selection = "585b70ff"; |
@@ -282,34 +357,46 @@ in { | |||
282 | }; | 357 | }; |
283 | }; | 358 | }; |
284 | }; | 359 | }; |
360 | pandoc = { | ||
361 | enable = true; | ||
362 | extraAbbreviations = ["i.A." "d.h." "D.h." "gdw."]; | ||
363 | }; | ||
364 | nushell = { | ||
365 | enable = true; | ||
366 | settings.show_banner = false; | ||
367 | }; | ||
368 | fd.enable = true; | ||
285 | }; | 369 | }; |
286 | 370 | ||
287 | services = { | 371 | services = { |
288 | dunst = { | 372 | wpaperd = { |
289 | settings = import ./dunst-settings.nix inputs; | ||
290 | iconTheme = { | ||
291 | package = pkgs.paper-icon-theme; | ||
292 | name = "Paper"; | ||
293 | }; | ||
294 | enable = true; | 373 | enable = true; |
374 | settings.default = { | ||
375 | path = "~/.wallpapers"; | ||
376 | duration = "15m"; | ||
377 | mode = "center"; | ||
378 | }; | ||
295 | }; | 379 | }; |
296 | emacs = { | 380 | emacs = { |
297 | enable = true; | 381 | enable = true; |
298 | socketActivation.enable = true; | 382 | socketActivation.enable = true; |
299 | client = { | 383 | client = { |
300 | enable = true; | 384 | enable = true; |
301 | arguments = mkForce ["--reuse-frame" "--alternate-editor" "\"\""]; | 385 | arguments = mkForce ["--create-frame" "--alternate-editor" (lib.getExe cfg.services.emacs.package)]; |
302 | }; | 386 | }; |
303 | }; | 387 | }; |
304 | gpg-agent = { | 388 | gpg-agent = { |
305 | enable = true; | 389 | enable = true; |
306 | enableSshSupport = true; | 390 | enableSshSupport = true; |
307 | extraConfig = '' | 391 | extraConfig = '' |
308 | pinentry-program ${pkgs.pinentry-gtk2}/bin/pinentry | 392 | pinentry-program ${lib.getExe' pkgs.pinentry-gtk2 "pinentry"} |
309 | grab | 393 | grab |
310 | ''; | 394 | ''; |
311 | }; | 395 | }; |
312 | xembed-sni-proxy.enable = true; | 396 | xembed-sni-proxy = { |
397 | enable = true; | ||
398 | package = pkgs.kdePackages.plasma-workspace; | ||
399 | }; | ||
313 | udiskie = { | 400 | udiskie = { |
314 | enable = true; | 401 | enable = true; |
315 | automount = false; | 402 | automount = false; |
@@ -321,8 +408,11 @@ in { | |||
321 | device_mounted = []; | 408 | device_mounted = []; |
322 | }; | 409 | }; |
323 | device_config = [ | 410 | device_config = [ |
324 | { mount_path = "/run/etc-metadata"; ignore = true; } | 411 | { loop_file = "/nix/store/*-etc-metadata.erofs"; is_mounted = false; ignore = true; } |
412 | { mount_path = "/run/nixos-etc-metadata"; ignore = true; } | ||
413 | { mount_path = "/run/nixos-etc-metadata.*"; ignore = true; } | ||
325 | ]; | 414 | ]; |
415 | icon_names.media = ["drive-removable-media-symbolic"]; | ||
326 | }; | 416 | }; |
327 | }; | 417 | }; |
328 | network-manager-applet.enable = true; | 418 | network-manager-applet.enable = true; |
@@ -339,7 +429,7 @@ in { | |||
339 | batch = "true"; | 429 | batch = "true"; |
340 | log = "false"; | 430 | log = "false"; |
341 | repeat = "watch"; | 431 | repeat = "watch"; |
342 | sshcmd = "${pkgs.openssh}/bin/ssh"; | 432 | sshcmd = lib.getExe' pkgs.openssh "ssh"; |
343 | ui = "text"; | 433 | ui = "text"; |
344 | }; | 434 | }; |
345 | }; | 435 | }; |
@@ -359,31 +449,17 @@ in { | |||
359 | enable = true; | 449 | enable = true; |
360 | events = [ | 450 | events = [ |
361 | { event = "before-sleep"; command = lockCommand; } | 451 | { event = "before-sleep"; command = lockCommand; } |
362 | # { event = "after-resume"; command = "${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms on"; } | ||
363 | { event = "lock"; command = lockCommand; } | 452 | { event = "lock"; command = lockCommand; } |
364 | ]; | 453 | ]; |
365 | timeouts = [ | 454 | timeouts = [ |
366 | # { timeout = 300; | 455 | { timeout = 600; command = lockCommand; } |
367 | # command = "${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms off"; | ||
368 | # } | ||
369 | { timeout = 330; command = lockCommand; } | ||
370 | ]; | 456 | ]; |
371 | extraArgs = [ | 457 | extraArgs = [ |
458 | "-w" | ||
372 | "idlehint" "30" | 459 | "idlehint" "30" |
373 | ]; | 460 | ]; |
374 | }; | 461 | }; |
375 | poweralertd.enable = true; | 462 | poweralertd.enable = true; |
376 | avizo = { | ||
377 | enable = true; | ||
378 | settings.default = { | ||
379 | time = "1.0"; | ||
380 | background = "rgba(0, 0, 0, 0.8)"; | ||
381 | border-color = "rgba(0, 0, 0, 1)"; | ||
382 | bar-fg-color = "rgba(160, 160, 160, 1)"; | ||
383 | bar-bg-color = "rgba(32, 32, 32, 0.96)"; | ||
384 | # y-offset = "0.25"; | ||
385 | }; | ||
386 | }; | ||
387 | }; | 463 | }; |
388 | 464 | ||
389 | home.pointerCursor = { | 465 | home.pointerCursor = { |
@@ -415,6 +491,13 @@ in { | |||
415 | }; | 491 | }; |
416 | }; | 492 | }; |
417 | 493 | ||
494 | qt.kde.settings = { | ||
495 | kwalletrc = { | ||
496 | KSecretD.Enabled = false; | ||
497 | Wallet."Default Wallet" = "store"; | ||
498 | }; | ||
499 | }; | ||
500 | |||
418 | xsession.preferStatusNotifierItems = true; | 501 | xsession.preferStatusNotifierItems = true; |
419 | 502 | ||
420 | xresources.properties = import ./xresources.nix; | 503 | xresources.properties = import ./xresources.nix; |
@@ -423,18 +506,19 @@ in { | |||
423 | packages = with pkgs; [ | 506 | packages = with pkgs; [ |
424 | fira fira-code pwvucontrol wrappedKeepassxc wl-clipboard-rs | 507 | fira fira-code pwvucontrol wrappedKeepassxc wl-clipboard-rs |
425 | mumble pulseaudio-ctl pamixer libnotify screen-message | 508 | mumble pulseaudio-ctl pamixer libnotify screen-message |
426 | wrappedYTMDesktop libsForQt5.qt5ct playerctl evince | 509 | wrappedYTMDesktop libsForQt5.qt5ct playerctl evince papers |
427 | thunderbird zoom-us steam steam-run wireshark virt-manager | 510 | thunderbird zoom-us xdg-desktop-portal steam steam-run |
428 | rclone cached-nix-shell worktime fira-code-symbols | 511 | wireshark virt-manager rclone cached-nix-shell worktime |
429 | libreoffice xournalpp google-chrome nixos-shell virt-viewer | 512 | fira-code-symbols libreoffice xournalpp google-chrome |
430 | freerdp gnome-icon-theme paper-icon-theme sshpassSecret | 513 | nixos-shell virt-viewer freerdp gnome-icon-theme |
431 | weechat element-desktop matrix-synapse-tools.synadm | 514 | paper-icon-theme sshpassSecret weechat element-desktop |
432 | flakeInputs.deploy-rs.packages.${config.nixpkgs.system}.deploy-rs | 515 | sieve-connect gimp3 inkscape udiskie glab nitrokey-app |
433 | sieve-connect gimp inkscape udiskie glab nitrokey-app | ||
434 | pynitrokey gtklock wlrctl remmina openscad spice-record | 516 | pynitrokey gtklock wlrctl remmina openscad spice-record |
435 | libguestfs-with-appliance nerd-fonts.fira-mono | 517 | libguestfs-with-appliance nerd-fonts.fira-mono |
436 | nerd-fonts.symbols-only nerd-fonts.fira-code powerline-fonts | 518 | nerd-fonts.symbols-only nerd-fonts.fira-code powerline-fonts |
437 | ]; | 519 | swtpm (hunspellWithDicts (with hunspellDicts; [en_GB-large de_DE])) |
520 | libation libqalculate | ||
521 | ] ++ mapAttrsToList (_name: pkg: pkgs.callPackage pkg {}) (customUtils.nixImport { dir = ./utils; }); | ||
438 | 522 | ||
439 | file = { | 523 | file = { |
440 | ".backup-munin".source = ./backup-patterns; | 524 | ".backup-munin".source = ./backup-patterns; |
@@ -454,12 +538,9 @@ in { | |||
454 | QT_QPA_PLATFORMTHEME = "qt5ct"; | 538 | QT_QPA_PLATFORMTHEME = "qt5ct"; |
455 | LIBVIRT_DEFAULT_URI = "qemu:///system"; | 539 | LIBVIRT_DEFAULT_URI = "qemu:///system"; |
456 | STACK_XDG = 1; | 540 | STACK_XDG = 1; |
457 | EDITOR = pkgs.writeShellScript "editor" '' | 541 | EDITOR = lib.getExe' editor "emacsclient"; |
458 | args=("--reuse-frame" "--alternate-editor" "") | 542 | RCLONE_PASSWORD_COMMAND = "${lib.getExe' pkgs.libsecret "secret-tool"} lookup service rclone"; |
459 | args+=("$@") | 543 | SYSTEMD_TINT_BACKGROUND = "false"; |
460 | exec -a emacsclient ${cfg.services.emacs.package}/bin/emacsclient "''${args[@]}" | ||
461 | ''; | ||
462 | RCLONE_PASSWORD_COMMAND = "${pkgs.libsecret}/bin/secret-tool lookup service rclone"; | ||
463 | }; | 544 | }; |
464 | 545 | ||
465 | extraProfileCommands = '' | 546 | extraProfileCommands = '' |
@@ -468,18 +549,11 @@ in { | |||
468 | }; | 549 | }; |
469 | 550 | ||
470 | xdg.configFile = { | 551 | xdg.configFile = { |
471 | "dunst/dunstrc.d" = { | ||
472 | source = ./dunstrc.d; | ||
473 | recursive = true; | ||
474 | onChange = '' | ||
475 | ${pkgs.systemd}/bin/systemctl --user try-restart dunst | ||
476 | ''; | ||
477 | }; | ||
478 | "wireplumber" = { | 552 | "wireplumber" = { |
479 | source = ./wireplumber; | 553 | source = ./wireplumber; |
480 | recursive = true; | 554 | recursive = true; |
481 | onChange = '' | 555 | onChange = '' |
482 | ${pkgs.systemd}/bin/systemctl --user try-restart wireplumber | 556 | ${lib.getExe' config.systemd.package "systemctl"} --user try-restart wireplumber |
483 | ''; | 557 | ''; |
484 | }; | 558 | }; |
485 | "stack/config.yaml" = { | 559 | "stack/config.yaml" = { |
@@ -503,37 +577,36 @@ in { | |||
503 | General = { | 577 | General = { |
504 | dot_as_separator = 0; | 578 | dot_as_separator = 0; |
505 | }; | 579 | }; |
580 | Mode = { | ||
581 | calculate_as_you_type = 1; | ||
582 | }; | ||
506 | }; | 583 | }; |
507 | }; | 584 | }; |
508 | "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 | }; | ||
591 | "systemd/user/xdg-desktop-portal.service.d/after-graphical-session.conf".text = '' | ||
592 | [Unit] | ||
593 | After=graphical-session.target | ||
594 | ''; | ||
595 | "systemd/user/home-manager.service.d/before-graphical-session.conf".text = '' | ||
596 | [Unit] | ||
597 | Before=graphical-session-pre.target | ||
598 | ''; | ||
599 | "pdfpc/pdfpcrc".text = '' | ||
600 | mouse 8 prev | ||
601 | mouse 9 next | ||
602 | ''; | ||
509 | }; | 603 | }; |
510 | 604 | ||
511 | xdg.dataFile = { | 605 | xdg.dataFile = { |
512 | "pandoc/abbreviations" = { | ||
513 | source = pkgs.runCommand "pandoc-abbreviations" { | ||
514 | buildInputs = [ pkgs.pandoc pkgs.coreutils ]; | ||
515 | } (let | ||
516 | germanAbbrevs = pkgs.fetchFromGitHub { | ||
517 | owner = "jfilter"; | ||
518 | repo = "german-abbreviations"; | ||
519 | rev = "8eb9dae93b6f05d7c53374cd217ab2dc89558e0c"; | ||
520 | sha256 = "SaD3tSqzen6Y3SPICe6/9vhe4iMHlArZ3kFQaEk7Hps="; | ||
521 | }; | ||
522 | in '' | ||
523 | cat \ | ||
524 | <(pandoc --print-default-data-file=abbreviations) \ | ||
525 | <(grep -E '^[^ ]+\.$' ${germanAbbrevs}/german_abbreviations.txt) \ | ||
526 | ${pkgs.writeText "abbrevs.txt" '' | ||
527 | i.A. | ||
528 | d.h. | ||
529 | D.h. | ||
530 | gdw. | ||
531 | ''} \ | ||
532 | | sort | uniq >$out | ||
533 | ''); | ||
534 | }; | ||
535 | "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"; |
536 | "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"; | ||
537 | "emoji-data/list.txt".source = pkgs.stdenv.mkDerivation { | 610 | "emoji-data/list.txt".source = pkgs.stdenv.mkDerivation { |
538 | inherit (sources.emoji-data) pname src; | 611 | inherit (sources.emoji-data) pname src; |
539 | version = lib.removePrefix "v" sources.emoji-data.version; | 612 | version = lib.removePrefix "v" sources.emoji-data.version; |
@@ -619,19 +692,68 @@ in { | |||
619 | name = "Rainbow"; | 692 | name = "Rainbow"; |
620 | exec = toString (pkgs.writeShellScript "rainbow" '' | 693 | exec = toString (pkgs.writeShellScript "rainbow" '' |
621 | exec -- \ | 694 | exec -- \ |
622 | ${config.systemd.package}/bin/systemd-run --wait --user --slice-inherit \ | 695 | ${lib.getExe' config.systemd.package "systemd-run"} --wait --user --slice-inherit \ |
623 | --property 'CPUAccounting=yes' --property 'CPUQuotaPeriodSec=50ms' \ | 696 | --property 'CPUAccounting=yes' --property 'CPUQuotaPeriodSec=50ms' \ |
624 | --property 'Environment=DSCP=46' \ | 697 | -E DSCP=46 -E NIXOS_OZONE_WL \ |
625 | -- ${pkgs.dscp}/bin/dscp ${pkgs.google-chrome}/bin/google-chrome-stable \ | 698 | -- ${lib.getExe pkgs.dscp} ${lib.getExe' pkgs.google-chrome "google-chrome-stable"} \ |
626 | --force-device-scale-factor=1.5 \ | ||
627 | --class=Rainbow \ | 699 | --class=Rainbow \ |
628 | --kiosk "https://web.openrainbow.com" \ | 700 | --app="https://web.openrainbow.com" \ |
629 | --user-data-dir=''${HOME}/.config/google-chrome-rainbow | 701 | --user-data-dir=''${HOME}/.config/google-chrome-rainbow |
630 | ''); | 702 | ''); |
631 | icon = pkgs.fetchurl { | 703 | icon = pkgs.fetchurl { |
632 | url = "https://web.openrainbow.com/rb/2.139.17/assets/skins/rainbow/images/homepage/logo__rainbow.svg"; | 704 | url = "https://web.openrainbow.com/rb/2.139.17/assets/skins/rainbow/images/homepage/logo__rainbow.svg"; |
633 | hash = "sha256-5fmo8rDqVDpzkGaPjk4Y+SsSZpAsY7VUQSFW6WdHwuU="; | 705 | hash = "sha256-5fmo8rDqVDpzkGaPjk4Y+SsSZpAsY7VUQSFW6WdHwuU="; |
634 | }; | 706 | }; |
707 | settings = { | ||
708 | StartupWMClass = "Rainbow"; | ||
709 | }; | ||
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 | }; | ||
635 | }; | 757 | }; |
636 | }; | 758 | }; |
637 | 759 | ||
diff --git a/accounts/gkleen@sif/dunst-settings.nix b/accounts/gkleen@sif/dunst-settings.nix deleted file mode 100644 index 72687aea..00000000 --- a/accounts/gkleen@sif/dunst-settings.nix +++ /dev/null | |||
@@ -1,45 +0,0 @@ | |||
1 | { pkgs, ... }: | ||
2 | { | ||
3 | global = { | ||
4 | font = "Fira Sans 12"; | ||
5 | markup = "full"; | ||
6 | format = "<i>%s</i> %p\\n%b"; | ||
7 | alignment = "left"; | ||
8 | # geometry = "1216x10-32+64"; | ||
9 | width = 500; | ||
10 | height = 100; | ||
11 | offset = "4x4"; | ||
12 | origin = "top-right"; | ||
13 | shrink = true; | ||
14 | monitor = 0; | ||
15 | follow = "none"; | ||
16 | padding = 6; | ||
17 | horizontal_padding = 6; | ||
18 | separator_height = 1; | ||
19 | separator_color = "frame"; | ||
20 | idle_threshold = 0; | ||
21 | |||
22 | transparency = 10; | ||
23 | |||
24 | frame_width = 1; | ||
25 | frame_color = "#999999"; | ||
26 | |||
27 | word_wrap = true; | ||
28 | show_age_threshold = 15; | ||
29 | show_indicators = false; | ||
30 | icon_position = "right"; | ||
31 | min_icon_size = 25; | ||
32 | max_icon_size = 25; | ||
33 | sort = false; | ||
34 | sticky_history = false; | ||
35 | |||
36 | dmenu = "fuzzel --dmenu"; | ||
37 | browser = "${pkgs.xdg-utils}/bin/xdg-open"; | ||
38 | }; | ||
39 | # shortcuts = { | ||
40 | # close = "ctrl+space"; | ||
41 | # close_all = "ctrl+shift+space"; | ||
42 | # history = "ctrl+comma"; | ||
43 | # context = "ctrl+period"; | ||
44 | # }; | ||
45 | } | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/00-urgency_low.conf b/accounts/gkleen@sif/dunstrc.d/00-urgency_low.conf deleted file mode 100644 index 98c94b64..00000000 --- a/accounts/gkleen@sif/dunstrc.d/00-urgency_low.conf +++ /dev/null | |||
@@ -1,4 +0,0 @@ | |||
1 | [urgency_low] | ||
2 | background="#000000aa" | ||
3 | foreground="#999999" | ||
4 | timeout=5 \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/01-urgency_normal.conf b/accounts/gkleen@sif/dunstrc.d/01-urgency_normal.conf deleted file mode 100644 index f8fa8e2d..00000000 --- a/accounts/gkleen@sif/dunstrc.d/01-urgency_normal.conf +++ /dev/null | |||
@@ -1,4 +0,0 @@ | |||
1 | [urgency_normal] | ||
2 | background="#000000aa" | ||
3 | foreground="#ffffff" | ||
4 | timeout=15 \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/02-urgency_critical.conf b/accounts/gkleen@sif/dunstrc.d/02-urgency_critical.conf deleted file mode 100644 index a08bf4b1..00000000 --- a/accounts/gkleen@sif/dunstrc.d/02-urgency_critical.conf +++ /dev/null | |||
@@ -1,4 +0,0 @@ | |||
1 | [urgency_critical] | ||
2 | background="#900000aa" | ||
3 | foreground="#ffffff" | ||
4 | timeout=0 | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/10-brightness.conf b/accounts/gkleen@sif/dunstrc.d/10-brightness.conf deleted file mode 100644 index c54595ab..00000000 --- a/accounts/gkleen@sif/dunstrc.d/10-brightness.conf +++ /dev/null | |||
@@ -1,5 +0,0 @@ | |||
1 | [brightness] | ||
2 | appname="brightness" | ||
3 | set_stack_tag="brightness" | ||
4 | set_transient=yes | ||
5 | history_ignore=yes | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/10-pulseaudio-ctl.conf b/accounts/gkleen@sif/dunstrc.d/10-pulseaudio-ctl.conf deleted file mode 100644 index 074f4535..00000000 --- a/accounts/gkleen@sif/dunstrc.d/10-pulseaudio-ctl.conf +++ /dev/null | |||
@@ -1,5 +0,0 @@ | |||
1 | [pulseaudio-ctl] | ||
2 | body="Current is *" | ||
3 | history_ignore=yes | ||
4 | set_stack_tag="volume" | ||
5 | summary="Volume *" | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/20-element.conf b/accounts/gkleen@sif/dunstrc.d/20-element.conf deleted file mode 100644 index 5ff6031e..00000000 --- a/accounts/gkleen@sif/dunstrc.d/20-element.conf +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | [element-im] | ||
2 | appname=Element | ||
3 | timeout=0 \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/20-kitty.conf b/accounts/gkleen@sif/dunstrc.d/20-kitty.conf deleted file mode 100644 index b27ee27e..00000000 --- a/accounts/gkleen@sif/dunstrc.d/20-kitty.conf +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | [kitty] | ||
2 | appname=kitty | ||
3 | urgency=low | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/20-mail.conf b/accounts/gkleen@sif/dunstrc.d/20-mail.conf deleted file mode 100644 index cb568e01..00000000 --- a/accounts/gkleen@sif/dunstrc.d/20-mail.conf +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | [element] | ||
2 | appname="notmuch" | ||
3 | timeout=0 \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/dunstrc.d/20-zulip.conf b/accounts/gkleen@sif/dunstrc.d/20-zulip.conf deleted file mode 100644 index d7fbd32c..00000000 --- a/accounts/gkleen@sif/dunstrc.d/20-zulip.conf +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | [zulip] | ||
2 | appname="Zulip" | ||
3 | timeout=0 \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/emacs.el b/accounts/gkleen@sif/emacs.el index 183cb322..3beefba6 100644 --- a/accounts/gkleen@sif/emacs.el +++ b/accounts/gkleen@sif/emacs.el | |||
@@ -51,7 +51,7 @@ | |||
51 | 51 | ||
52 | ;; (require 'scratch) | 52 | ;; (require 'scratch) |
53 | (global-set-key (kbd "C-x B") 'scratch-create) | 53 | (global-set-key (kbd "C-x B") 'scratch-create) |
54 | (setq initial-major-mode 'scratch-mode) | 54 | ;; (setq initial-major-mode 'scratch-mode) |
55 | (setq initial-scratch-message "") | 55 | (setq initial-scratch-message "") |
56 | 56 | ||
57 | (global-set-key (kbd "C-x K") 'kill-current-buffer) | 57 | (global-set-key (kbd "C-x K") 'kill-current-buffer) |
@@ -228,6 +228,7 @@ necessarily running." | |||
228 | (global-set-key (kbd "C-x k") 'kill-buffer-with-special-emacsclient-handling)) | 228 | (global-set-key (kbd "C-x k") 'kill-buffer-with-special-emacsclient-handling)) |
229 | 229 | ||
230 | (add-hook 'server-switch-hook 'install-emacsclient-wrapped-kill-buffer) | 230 | (add-hook 'server-switch-hook 'install-emacsclient-wrapped-kill-buffer) |
231 | (add-hook 'server-switch-hook #'raise-frame) | ||
231 | 232 | ||
232 | (defun move-file (new-location) | 233 | (defun move-file (new-location) |
233 | "Write this file to NEW-LOCATION, and delete the old one." | 234 | "Write this file to NEW-LOCATION, and delete the old one." |
@@ -253,3 +254,5 @@ necessarily running." | |||
253 | (bind-key "C-x C-m" #'move-file) | 254 | (bind-key "C-x C-m" #'move-file) |
254 | 255 | ||
255 | (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/firefox-chrome.css b/accounts/gkleen@sif/firefox-chrome.css index 8900e2b9..726f1e4b 100644 --- a/accounts/gkleen@sif/firefox-chrome.css +++ b/accounts/gkleen@sif/firefox-chrome.css | |||
@@ -4,6 +4,21 @@ | |||
4 | font-size:12px; | 4 | font-size:12px; |
5 | } | 5 | } |
6 | 6 | ||
7 | #sidebar-main:has([expanded]) { | ||
8 | min-width:20em !important; | ||
9 | max-width:20em !important; | ||
10 | } | ||
11 | |||
12 | #sidebar, #sidebar-box { | ||
13 | min-width:35em !important; | ||
14 | max-width:35em !important; | ||
15 | } | ||
16 | |||
17 | #sidebar-box { | ||
18 | margin-right: var(--space-small); | ||
19 | } | ||
20 | |||
21 | /* | ||
7 | #sidebar { | 22 | #sidebar { |
8 | min-width:20em !important; | 23 | min-width:20em !important; |
9 | max-width:20em !important; | 24 | max-width:20em !important; |
@@ -19,8 +34,21 @@ | |||
19 | } | 34 | } |
20 | 35 | ||
21 | #toolbar-menubar[inactive="true"] + #TabsToolbar { | 36 | #toolbar-menubar[inactive="true"] + #TabsToolbar { |
22 | visibility: collapse !important; | 37 | visibility: collapse !important; |
23 | } | 38 | } |
24 | 39 | ||
25 | #sidebar-box[sidebarcommand="tabcenter-reborn_ariasuni-sidebar-action"] #sidebar-header { visibility: collapse !important; } | 40 | #sidebar-box[sidebarcommand="tabcenter-reborn_ariasuni-sidebar-action"] #sidebar-header { visibility: collapse !important; } |
26 | #sidebar-box[sidebarcommand="_3c078156-979c-498b-8990-85f7987dd929_-sidebar-action"] #sidebar-header { visibility: collapse !important; } | 41 | #sidebar-box[sidebarcommand="_3c078156-979c-498b-8990-85f7987dd929_-sidebar-action"] #sidebar-header { visibility: collapse !important; } |
42 | */ | ||
43 | |||
44 | .titlebar-buttonbox-container{ display: none; } | ||
45 | #vertical-spacer { display: none; } | ||
46 | #tabbrowser-tabs[orient="vertical"] .tab-background { | ||
47 | border-radius: var(--border-radius-small) !important; | ||
48 | } | ||
49 | hbox:has(> #tabs-newtab-button) { | ||
50 | display: none; | ||
51 | } | ||
52 | #sidebar-main .tools-and-extensions { | ||
53 | justify-content: space-around !important; | ||
54 | } | ||
diff --git a/accounts/gkleen@sif/libvirt/default.nix b/accounts/gkleen@sif/libvirt/default.nix index 70ac22b9..4e5a9b90 100644 --- a/accounts/gkleen@sif/libvirt/default.nix +++ b/accounts/gkleen@sif/libvirt/default.nix | |||
@@ -7,6 +7,7 @@ with flakeInputs.nixVirt.lib; | |||
7 | config = { | 7 | config = { |
8 | virtualisation.libvirt = { | 8 | virtualisation.libvirt = { |
9 | enable = true; | 9 | enable = true; |
10 | swtpm.enable = true; | ||
10 | connections."qemu:///session" = { | 11 | connections."qemu:///session" = { |
11 | domains = [ | 12 | domains = [ |
12 | { definition = domain.writeXML (updateManyAttrsByPath [ | 13 | { definition = domain.writeXML (updateManyAttrsByPath [ |
diff --git a/accounts/gkleen@sif/niri/default.nix b/accounts/gkleen@sif/niri/default.nix index 6a8d10a0..8752f3e3 100644 --- a/accounts/gkleen@sif/niri/default.nix +++ b/accounts/gkleen@sif/niri/default.nix | |||
@@ -1,15 +1,234 @@ | |||
1 | { config, pkgs, lib, ... }: | 1 | { config, hostConfig, pkgs, lib, flakeInputs, ... }: |
2 | let | 2 | let |
3 | niri = config.programs.niri.package; | 3 | cfg = config.programs.niri; |
4 | |||
5 | kdl = flakeInputs.niri-flake.lib.kdl; | ||
6 | |||
7 | niri = cfg.package; | ||
4 | terminal = lib.getExe config.programs.kitty.package; | 8 | terminal = lib.getExe config.programs.kitty.package; |
5 | lightctl = lib.getExe' config.services.avizo.package "lightctl"; | 9 | makoctl = lib.getExe' config.services.mako.package "makoctl"; |
6 | volumectl = lib.getExe' config.services.avizo.package "volumectl"; | 10 | loginctl = lib.getExe' hostConfig.systemd.package "loginctl"; |
7 | dunstctl = lib.getExe' config.services.dunst.package "dunstctl"; | 11 | systemctl = lib.getExe' hostConfig.systemd.package "systemctl"; |
12 | swayosd-client = lib.getExe' config.services.swayosd.package "swayosd-client"; | ||
13 | |||
14 | focus_or_spawn = pkgs.writeShellApplication { | ||
15 | name = "focus-or-spawn"; | ||
16 | runtimeInputs = [ niri pkgs.gojq pkgs.gnugrep pkgs.socat ]; | ||
17 | text = '' | ||
18 | window_select="$1" | ||
19 | shift | ||
20 | workspace_name="$1" | ||
21 | shift | ||
22 | |||
23 | workspaces_json="$(niri msg -j workspaces)" | ||
24 | workspace_output="$(jq -r --arg workspace_name "$workspace_name" '.[] | select(.name == $workspace_name) | .output' <<<"$workspaces_json")" | ||
25 | # active_workspace="$(jq -r --arg workspace_output "$workspace_output" '.[] | select(.output == $workspace_output and .is_active) | .id' <<<"$workspaces_json")" | ||
26 | active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")" | ||
27 | if [[ $workspace_output != "$active_output" ]]; then | ||
28 | niri msg action move-workspace-to-monitor --reference "$workspace_name" "$active_output" | ||
29 | # socat STDIO "$NIRI_SOCKET" <<<'{"Action":{"FocusWorkspace":{"reference":{"Id":'"''${active_workspace}"'}}}}' | ||
30 | # niri msg action move-workspace-to-index --reference "$workspace_name" 1 | ||
31 | fi | ||
32 | |||
33 | while IFS=$'\n' read -r window_json; do | ||
34 | if [[ -n $(jq -c "$window_select" <<<"$window_json") ]]; then | ||
35 | if jq -e '.is_focused' <<<"$window_json" >/dev/null; then | ||
36 | niri msg action focus-workspace-previous | ||
37 | else | ||
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 | ||
43 | fi | ||
44 | exit 0 | ||
45 | fi | ||
46 | done < <(niri msg -j windows | jq -c '.[]') | ||
47 | |||
48 | exec "$@" | ||
49 | ''; | ||
50 | }; | ||
51 | focus-or-spawn-action = config.lib.niri.actions.spawn (lib.getExe focus_or_spawn); | ||
52 | |||
53 | with_adjacent_workspace = pkgs.writeShellApplication { | ||
54 | name = "with-adjacent-workspace"; | ||
55 | runtimeInputs = [ niri pkgs.gojq pkgs.socat ]; | ||
56 | text = '' | ||
57 | blacklist="$1" | ||
58 | shift | ||
59 | direction="$1" | ||
60 | shift | ||
61 | action="$1" | ||
62 | shift | ||
63 | |||
64 | workspaces_json="$(niri msg -j workspaces)" | ||
65 | active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")" | ||
66 | workspace_output="$(jq -r --arg active_workspace "$active_workspace" '.[] | select(.id == ($active_workspace | tonumber)) | .output' <<<"$workspaces_json")" | ||
67 | workspace_idx="$(jq -r '.[] | select(.is_focused) | .idx' <<<"$workspaces_json")" | ||
68 | |||
69 | jq_script='map(select(' | ||
70 | case "$direction" in | ||
71 | down) | ||
72 | # shellcheck disable=SC2016 | ||
73 | jq_script=''${jq_script}'.idx > ($workspace_idx | tonumber)';; | ||
74 | up) | ||
75 | # shellcheck disable=SC2016 | ||
76 | jq_script=''${jq_script}'.idx < ($workspace_idx | tonumber)';; | ||
77 | esac | ||
78 | # shellcheck disable=SC2016 | ||
79 | jq_script=''${jq_script}' and .output == $workspace_output and ((.name == null) or (.name | test($blacklist) | not)))) | sort_by(.idx)' | ||
80 | [[ $direction == "up" ]] && jq_script=''${jq_script}' | reverse' | ||
81 | jq_script=''${jq_script}' | .[0]' | ||
82 | |||
83 | workspace_json=$(jq -c --arg blacklist "$blacklist" --arg workspace_output "$workspace_output" --arg workspace_idx "$workspace_idx" "$jq_script" <<<"$workspaces_json") | ||
84 | [[ -n $workspace_json && $workspace_json != null ]] || exit 0 | ||
85 | jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET" | ||
86 | ''; | ||
87 | }; | ||
88 | with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^${lib.concatMapStringsSep "|" ({ name, ...}: name) cfg.scratchspaces}$"; | ||
89 | focus-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; | ||
90 | move-column-to-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}, "focus": true}}}''; | ||
91 | |||
92 | with_unnamed_workspace = pkgs.writeShellApplication { | ||
93 | name = "with-unnamed-workspace"; | ||
94 | runtimeInputs = [ niri pkgs.gojq pkgs.socat ]; | ||
95 | text = '' | ||
96 | action="$1" | ||
97 | shift | ||
98 | |||
99 | workspaces_json="$(niri msg -j workspaces)" | ||
100 | active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")" | ||
101 | active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")" | ||
102 | |||
103 | history_json="$(socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/niri-workspace-history.sock)" | ||
104 | workspace_json="$(jq -c --arg active_output "$active_output" --argjson history "$history_json" 'map(select(.output == $active_output and .name == null)) | map({"value": ., "history_idx": ((. as $workspace | ($history[$active_output] | index($workspace | .id))) as $active_idx | if $active_idx then $active_idx else ($history[$active_output] | length) + 1 end)}) | sort_by(.history_idx, .value.idx) | map(.value) | .[0]' <<<"$workspaces_json")" | ||
105 | [[ -n $workspace_json && $workspace_json != null ]] || exit 0 | ||
106 | jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET" | ||
107 | ''; | ||
108 | }; | ||
109 | with-unnamed-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_unnamed_workspace); | ||
110 | |||
111 | with_empty_unnamed_workspace = pkgs.writeShellApplication { | ||
112 | name = "with-empty-unnamed-workspace"; | ||
113 | runtimeInputs = [ niri pkgs.gojq pkgs.socat ]; | ||
114 | text = '' | ||
115 | action="$1" | ||
116 | shift | ||
117 | |||
118 | workspaces_json="$(niri msg -j workspaces)" | ||
119 | active_output="$(jq '.[] | select(.is_focused) | .output' <<<"$workspaces_json")" | ||
120 | target_workspace_id="$(jq --argjson active_output "$active_output" 'map(select(.active_window_id == null and .name == null and .output == $active_output)) | sort_by(.idx) | .[0].id' <<<"$workspaces_json")" | ||
121 | jq --argjson workspace_id "$target_workspace_id" -nc "$action" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET" | ||
122 | ''; | ||
123 | }; | ||
124 | with-empty-unnamed-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_empty_unnamed_workspace); | ||
125 | |||
126 | with_select_window = pkgs.writeShellApplication { | ||
127 | name = "with-select-window"; | ||
128 | runtimeInputs = [ niri pkgs.gojq pkgs.socat config.programs.fuzzel.package pkgs.gawk ]; | ||
129 | text = '' | ||
130 | window_select="$1" | ||
131 | shift | ||
132 | action="$1" | ||
133 | shift | ||
134 | |||
135 | windows_json="$(niri msg -j windows)" | ||
136 | active_workspace="$(jq -r '.[] | select(.is_focused) | .workspace_id' <<<"$windows_json")" | ||
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)" | ||
138 | # shellcheck disable=SC2016 | ||
139 | window_json="$(gojq -rc --arg active_workspace "$active_workspace" --arg window_ix "$window_ix" 'map(select('"$window_select"')) | .[($window_ix | tonumber)]' <<<"$windows_json")" | ||
140 | |||
141 | [[ -z "$window_json" ]] && exit 1 | ||
142 | |||
143 | jq -c "$action" <<<"$window_json" | socat STDIO "$NIRI_SOCKET" | ||
144 | ''; | ||
145 | }; | ||
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")); | ||
8 | in { | 166 | in { |
9 | imports = [ | 167 | imports = [ |
10 | ./waybar.nix | 168 | ./waybar.nix |
169 | ./mako.nix | ||
170 | ./swayosd.nix | ||
11 | ]; | 171 | ]; |
12 | 172 | ||
173 | options = { | ||
174 | programs.niri.scratchspaces = lib.mkOption { | ||
175 | type = lib.types.listOf (lib.types.submodule ({ config, ... }: { | ||
176 | options = { | ||
177 | name = lib.mkOption { | ||
178 | type = lib.types.str; | ||
179 | }; | ||
180 | match = lib.mkOption { | ||
181 | type = lib.types.listOf (lib.types.attrsOf kdl.types.kdl-args); | ||
182 | default = []; | ||
183 | }; | ||
184 | exclude = lib.mkOption { | ||
185 | type = lib.types.listOf (lib.types.attrsOf kdl.types.kdl-args); | ||
186 | default = []; | ||
187 | }; | ||
188 | windowRuleExtra = lib.mkOption { | ||
189 | type = kdl.types.kdl-nodes; | ||
190 | default = []; | ||
191 | }; | ||
192 | key = lib.mkOption { | ||
193 | type = lib.types.nullOr lib.types.str; | ||
194 | default = null; | ||
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 | }; | ||
207 | spawn = lib.mkOption { | ||
208 | type = lib.types.nullOr (lib.types.listOf lib.types.str); | ||
209 | default = null; | ||
210 | }; | ||
211 | app-id = lib.mkOption { | ||
212 | type = lib.types.nullOr lib.types.str; | ||
213 | default = null; | ||
214 | }; | ||
215 | selector = lib.mkOption { | ||
216 | type = lib.types.nullOr lib.types.str; | ||
217 | default = null; | ||
218 | }; | ||
219 | }; | ||
220 | |||
221 | config = lib.mkMerge [ | ||
222 | (lib.mkIf (config.app-id != null) { | ||
223 | match = lib.mkDefault [ { app-id = "^${lib.escapeRegex config.app-id}$"; } ]; | ||
224 | selector = lib.mkDefault "select(.app_id == \"${config.app-id}\")"; | ||
225 | }) | ||
226 | ]; | ||
227 | })); | ||
228 | default = []; | ||
229 | }; | ||
230 | }; | ||
231 | |||
13 | config = { | 232 | config = { |
14 | systemd.user.services.xwayland-satellite = { | 233 | systemd.user.services.xwayland-satellite = { |
15 | Unit = { | 234 | Unit = { |
@@ -21,7 +240,9 @@ in { | |||
21 | Service = { | 240 | Service = { |
22 | Type = "notify"; | 241 | Type = "notify"; |
23 | NotifyAccess = "all"; | 242 | NotifyAccess = "all"; |
24 | ExecStart = lib.getExe pkgs.xwayland-satellite-unstable; | 243 | Environment = [ "DISPLAY=:0" ]; |
244 | ExecStart = ''${lib.getExe pkgs.xwayland-satellite-unstable} ''${DISPLAY}''; | ||
245 | ExecStartPre = "${systemctl} --user import-environment DISPLAY"; | ||
25 | StandardOutput = "journal"; | 246 | StandardOutput = "journal"; |
26 | }; | 247 | }; |
27 | Install = { | 248 | Install = { |
@@ -29,111 +250,765 @@ in { | |||
29 | }; | 250 | }; |
30 | }; | 251 | }; |
31 | 252 | ||
32 | programs.niri.settings = { | 253 | services.swayidle = { |
33 | prefer-no-csd = true; | 254 | events = [ |
34 | screenshot-path = "${config.home.homeDirectory}/screenshots"; | 255 | { event = "after-resume"; command = "${lib.getExe niri} msg action power-on-monitors"; } |
256 | ]; | ||
257 | timeouts = [ | ||
258 | { timeout = 540; | ||
259 | command = "${lib.getExe niri} msg action power-off-monitors"; | ||
260 | } | ||
261 | ]; | ||
262 | }; | ||
35 | 263 | ||
36 | input = { | 264 | systemd.user.sockets.niri-workspace-history = { |
37 | keyboard.xkb = { | 265 | Socket = { |
38 | layout = "us,"; | 266 | ListenStream = "%t/niri-workspace-history.sock"; |
39 | variant = "dvp,"; | 267 | SocketMode = "0600"; |
40 | options = "compose:caps,grp:win_space_toggle"; | ||
41 | }; | ||
42 | }; | 268 | }; |
43 | 269 | }; | |
44 | environment = { | 270 | systemd.user.services.niri-workspace-history = { |
45 | NIXOS_OZONE_WL = "1"; | 271 | Unit = { |
46 | QT_QPA_PLATFORM = "wayland"; | 272 | BindsTo = [ "niri.service" ]; |
47 | GDK_BACKEND = "wayland"; | 273 | After = [ "niri.service" ]; |
48 | SDL_VIDEODRIVER = "wayland"; | ||
49 | }; | 274 | }; |
275 | Install = { | ||
276 | WantedBy = [ "niri.service" ]; | ||
277 | }; | ||
278 | Service = { | ||
279 | Type = "simple"; | ||
280 | Sockets = [ "niri-workspace-history.socket" ]; | ||
281 | ExecStart = pkgs.writers.writePython3 "niri-workspace-history" { flakeIgnore = ["E501"]; } '' | ||
282 | import os | ||
283 | import socket | ||
284 | import json | ||
285 | # import sys | ||
286 | from collections import defaultdict | ||
287 | from threading import Thread, Lock | ||
288 | from socketserver import StreamRequestHandler, ThreadingTCPServer | ||
289 | from contextlib import contextmanager | ||
290 | from io import TextIOWrapper | ||
50 | 291 | ||
51 | cursor.hide-when-typing = true; | ||
52 | 292 | ||
53 | binds = with config.lib.niri.actions; { | 293 | @contextmanager |
54 | "Mod+Return".action = spawn terminal; | 294 | def detaching(thing): |
55 | "Mod+Q".action = close-window; | 295 | try: |
56 | "Mod+D".action = spawn (lib.getExe config.programs.fuzzel.package); | 296 | yield thing |
57 | "Mod+Shift+D".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path"; | 297 | finally: |
298 | thing.detach() | ||
58 | 299 | ||
59 | "Mod+H".action = focus-column-left; | ||
60 | "Mod+T".action = focus-window-down; | ||
61 | "Mod+N".action = focus-window-up; | ||
62 | "Mod+S".action = focus-column-right; | ||
63 | 300 | ||
64 | "Mod+Shift+H".action = move-column-left; | 301 | workspace_history = defaultdict(list) |
65 | "Mod+Shift+T".action = move-window-down; | 302 | history_lock = Lock() |
66 | "Mod+Shift+N".action = move-window-up; | ||
67 | "Mod+Shift+S".action = move-column-right; | ||
68 | 303 | ||
69 | "Mod+Control+H".action = focus-monitor-left; | ||
70 | "Mod+Control+T".action = focus-monitor-down; | ||
71 | "Mod+Control+N".action = focus-monitor-up; | ||
72 | "Mod+Control+S".action = focus-monitor-right; | ||
73 | 304 | ||
74 | "Mod+Shift+Control+H".action = move-workspace-to-monitor-left; | 305 | def monitor_niri(): |
75 | "Mod+Shift+Control+T".action = move-workspace-to-monitor-down; | 306 | workspaces = list() |
76 | "Mod+Shift+Control+N".action = move-workspace-to-monitor-up; | ||
77 | "Mod+Shift+Control+S".action = move-workspace-to-monitor-right; | ||
78 | 307 | ||
79 | "Mod+G".action = focus-workspace-down; | 308 | def focus_workspace(output, workspace): |
80 | "Mod+C".action = focus-workspace-up; | 309 | with history_lock: |
310 | workspace_history[output] = [workspace] + [ws for ws in workspace_history[output] if ws != workspace] | ||
311 | # print(json.dumps(workspace_history), file=sys.stderr) | ||
81 | 312 | ||
82 | "Mod+Control+G".action = move-column-to-workspace-down; | 313 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
83 | "Mod+Control+C".action = move-column-to-workspace-up; | 314 | sock.connect(os.environ["NIRI_SOCKET"]) |
315 | sock.send(b"\"EventStream\"\n") | ||
316 | for line in sock.makefile(buffering=1, encoding='utf-8'): | ||
317 | if line_json := json.loads(line): | ||
318 | if "WorkspacesChanged" in line_json: | ||
319 | workspaces = line_json["WorkspacesChanged"]["workspaces"] | ||
320 | for ws in workspaces: | ||
321 | if ws["is_focused"]: | ||
322 | focus_workspace(ws["output"], ws["id"]) | ||
323 | if "WorkspaceActivated" in line_json: | ||
324 | for ws in workspaces: | ||
325 | if ws["id"] != line_json["WorkspaceActivated"]["id"]: | ||
326 | continue | ||
327 | focus_workspace(ws["output"], ws["id"]) | ||
328 | break | ||
84 | 329 | ||
85 | "Mod+Shift+G".action = move-workspace-down; | ||
86 | "Mod+Shift+C".action = move-workspace-up; | ||
87 | 330 | ||
88 | "Mod+M".action = consume-window-into-column; | 331 | class RequestHandler(StreamRequestHandler): |
89 | "Mod+W".action = expel-window-from-column; | 332 | def handle(self): |
333 | with detaching(TextIOWrapper(self.wfile, encoding='utf-8', write_through=True)) as out: | ||
334 | with history_lock: | ||
335 | json.dump(workspace_history, out) | ||
90 | 336 | ||
91 | "Mod+F".action = maximize-column; | ||
92 | "Mod+Shift+F".action = fullscreen-window; | ||
93 | 337 | ||
94 | "Mod+Space".action = switch-focus-between-floating-and-tiling; | 338 | class Server(ThreadingTCPServer): |
95 | "Mod+Shift+Space".action = toggle-window-floating; | 339 | def __init__(self): |
340 | ThreadingTCPServer.__init__(self, ("", 8000), RequestHandler, bind_and_activate=False) | ||
341 | self.socket = socket.fromfd(3, self.address_family, self.socket_type) | ||
96 | 342 | ||
97 | "Mod+Left".action = set-column-width "-10%"; | ||
98 | "Mod+Down".action = set-window-height "-10%"; | ||
99 | "Mod+Up".action = set-window-height "+10%"; | ||
100 | "Mod+Right".action = set-column-width "+10%"; | ||
101 | 343 | ||
102 | "Mod+Shift+Z" = { | 344 | def run_server(): |
103 | action = spawn (lib.getExe niri) "msg" "action" "power-off-monitors"; | 345 | with Server() as server: |
104 | allow-when-locked = true; | 346 | server.serve_forever() |
105 | }; | ||
106 | 347 | ||
107 | "XF86MonBrightnessUp" = { | ||
108 | action = spawn lightctl "-d" "-e4" "-n1" "up"; | ||
109 | allow-when-locked = true; | ||
110 | }; | ||
111 | "XF86MonBrightnessDown" = { | ||
112 | action = spawn lightctl "-d" "-e4" "-n1" "down"; | ||
113 | allow-when-locked = true; | ||
114 | }; | ||
115 | "XF86AudioRaiseVolume" = { | ||
116 | action = spawn volumectl "-d" "-u" "up"; | ||
117 | allow-when-locked = true; | ||
118 | }; | ||
119 | "XF86AudioLowerVolume" = { | ||
120 | action = spawn volumectl "-d" "-u" "down"; | ||
121 | allow-when-locked = true; | ||
122 | }; | ||
123 | "XF86AudioMute" = { | ||
124 | action = spawn volumectl "-d" "toggle-mute"; | ||
125 | allow-when-locked = true; | ||
126 | }; | ||
127 | "XF86AudioMicMute" = { | ||
128 | action = spawn volumectl "-d" "-m" "toggle-mute"; | ||
129 | allow-when-locked = true; | ||
130 | }; | ||
131 | 348 | ||
132 | "Mod+Semicolon".action = spawn dunstctl "close"; | 349 | niri = Thread(target=monitor_niri) |
133 | "Mod+Shift+Semicolon".action = spawn dunstctl "close-all"; | 350 | niri.daemon = True |
134 | "Mod+Period".action = spawn dunstctl "context"; | 351 | niri.start() |
135 | "Mod+Comma".action = spawn dunstctl "history-pop"; | 352 | |
353 | server_thread = Thread(target=run_server) | ||
354 | server_thread.daemon = True | ||
355 | server_thread.start() | ||
356 | |||
357 | while True: | ||
358 | server_thread.join(timeout=0.5) | ||
359 | niri.join(timeout=0.5) | ||
360 | |||
361 | if not (niri.is_alive() and server_thread.is_alive()): | ||
362 | break | ||
363 | ''; | ||
136 | }; | 364 | }; |
137 | }; | 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 | }; | ||
439 | |||
440 | programs.niri.scratchspaces = [ | ||
441 | { name = "pwctl"; | ||
442 | key = "Mod+Control+A"; | ||
443 | spawn = ["pwvucontrol"]; | ||
444 | app-id = "com.saivert.pwvucontrol"; | ||
445 | } | ||
446 | { name = "kpxc"; | ||
447 | exclude = [ | ||
448 | { title = "^Unlock Database.*"; } | ||
449 | { title = "^Access Request.*"; } | ||
450 | { title = ".*Passkey credentials$"; } | ||
451 | ]; | ||
452 | windowRuleExtra = with kdl; [ | ||
453 | (kdl.leaf "open-focused" false) | ||
454 | ]; | ||
455 | key = "Mod+Control+P"; | ||
456 | app-id = "org.keepassxc.KeePassXC"; | ||
457 | spawn = [ "keepassxc" ]; | ||
458 | } | ||
459 | { name = "bmgr"; | ||
460 | key = "Mod+Control+B"; | ||
461 | app-id = ".blueman-manager-wrapped"; | ||
462 | spawn = [ "blueman-manager" ]; | ||
463 | } | ||
464 | { name = "term"; | ||
465 | key = "Mod+Control+Return"; | ||
466 | app-id = "kitty-scratch"; | ||
467 | spawn = [ "kitty" "--app-id" "kitty-scratch" ]; | ||
468 | } | ||
469 | { name = "edit"; | ||
470 | match = [ { title = "^scratch$"; app-id = "^emacs$"; } ]; | ||
471 | key = "Mod+Control+E"; | ||
472 | selector = "select(.app_id == \"emacs\" and .title == \"scratch\")"; | ||
473 | spawn = [ "emacsclient" "-c" "--frame-parameters=(quote (name . \"scratch\"))" ]; | ||
474 | } | ||
475 | { name = "eff"; | ||
476 | key = "Mod+Control+O"; | ||
477 | app-id = "com.github.wwmm.easyeffects"; | ||
478 | spawn = [ "easyeffects" ]; | ||
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 | } | ||
494 | ]; | ||
495 | programs.niri.config = | ||
496 | let | ||
497 | inherit (kdl) node plain leaf flag; | ||
498 | optional-node = cond: v: | ||
499 | if cond | ||
500 | then v | ||
501 | else null; | ||
502 | opt-props = lib.filterAttrs (lib.const (value: value != null)); | ||
503 | in | ||
504 | [ (flag "prefer-no-csd") | ||
505 | |||
506 | (leaf "screenshot-path" "~/screenshots/%Y-%m-%dT%H:%M:%S.png") | ||
507 | |||
508 | (plain "hotkey-overlay" [ | ||
509 | (flag "skip-at-startup") | ||
510 | ]) | ||
511 | |||
512 | (plain "input" [ | ||
513 | (plain "keyboard" [ | ||
514 | (leaf "repeat-delay" 300) | ||
515 | (leaf "repeat-rate" 50) | ||
516 | |||
517 | (plain "xkb" [ | ||
518 | (leaf "layout" "us,us") | ||
519 | (leaf "variant" "dvp,") | ||
520 | (leaf "options" "compose:caps,grp:win_space_toggle") | ||
521 | ]) | ||
522 | ]) | ||
523 | |||
524 | (flag "workspace-auto-back-and-forth") | ||
525 | # (leaf "focus-follows-mouse" {}) | ||
526 | # (flag "warp-mouse-to-focus") | ||
527 | |||
528 | # (plain "touchpad" [ (flag "off") ]) | ||
529 | (plain "trackball" [ | ||
530 | (leaf "scroll-method" "on-button-down") | ||
531 | (leaf "scroll-button" 278) | ||
532 | ]) | ||
533 | (plain "touch" [ | ||
534 | (leaf "map-to-output" "eDP-1") | ||
535 | ]) | ||
536 | ]) | ||
537 | |||
538 | (plain "gestures" [ | ||
539 | (plain "hot-corners" [(flag "off")]) | ||
540 | ]) | ||
541 | |||
542 | (plain "environment" (lib.mapAttrsToList leaf { | ||
543 | NIXOS_OZONE_WL = "1"; | ||
544 | QT_QPA_PLATFORM = "wayland"; | ||
545 | QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; | ||
546 | GDK_BACKEND = "wayland"; | ||
547 | SDL_VIDEODRIVER = "wayland"; | ||
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; | ||
553 | })) | ||
554 | |||
555 | (node "output" "eDP-1" [ | ||
556 | (leaf "scale" 1.5) | ||
557 | (leaf "position" { x = 0; y = 0; }) | ||
558 | ]) | ||
559 | (node "output" "Ancor Communications Inc ASUS PB287Q 0x0000DD9B" [ | ||
560 | (leaf "scale" 1.5) | ||
561 | (leaf "position" { x = 2560; y = 0; }) | ||
562 | ]) | ||
563 | (node "output" "HP Inc. HP 727pu CN4417143K" [ | ||
564 | (leaf "mode" "2560x1440@119.998") | ||
565 | (leaf "scale" 1) | ||
566 | (leaf "position" { x = 2560; y = 0; }) | ||
567 | (flag "variable-refresh-rate") | ||
568 | ]) | ||
569 | |||
570 | (plain "debug" [ | ||
571 | (leaf "render-drm-device" "/dev/dri/by-path/pci-0000:00:02.0-render") | ||
572 | ]) | ||
573 | |||
574 | (plain "animations" [ | ||
575 | (leaf "slowdown" 0.5) | ||
576 | (plain "workspace-switch" [(flag "off")]) | ||
577 | ]) | ||
578 | |||
579 | (plain "layout" [ | ||
580 | (leaf "gaps" 8) | ||
581 | (plain "struts" [ | ||
582 | (leaf "left" 26) | ||
583 | (leaf "right" 26) | ||
584 | (leaf "top" 0) | ||
585 | (leaf "bottom" 0) | ||
586 | ]) | ||
587 | (plain "border" [ | ||
588 | (leaf "width" 2) | ||
589 | (leaf "active-gradient" { | ||
590 | from = "hsla(195 100% 45% 1)"; | ||
591 | to = "hsla(155 100% 37.5% 1)"; | ||
592 | angle = 29; | ||
593 | relative-to = "workspace-view"; | ||
594 | }) | ||
595 | (leaf "inactive-gradient" { | ||
596 | from = "hsla(0 0% 27.7% 1)"; | ||
597 | to = "hsla(0 0% 23% 1)"; | ||
598 | angle = 29; | ||
599 | relative-to = "workspace-view"; | ||
600 | }) | ||
601 | ]) | ||
602 | (plain "focus-ring" [ | ||
603 | (flag "off") | ||
604 | ]) | ||
605 | |||
606 | (plain "preset-column-widths" (map (prop: leaf "proportion" prop) [ | ||
607 | (1. / 4.) (1. / 3.) (1. / 2.) (2. / 3.) (3. / 4.) (1.) | ||
608 | ])) | ||
609 | (plain "default-column-width" [ (leaf "proportion" (1. / 2.)) ]) | ||
610 | (plain "preset-window-heights" (map (prop: leaf "proportion" prop) [ | ||
611 | (1. / 3.) (1. / 2.) (2. / 3.) (1.) | ||
612 | ])) | ||
613 | |||
614 | (flag "always-center-single-column") | ||
615 | |||
616 | (plain "tab-indicator" [ | ||
617 | (leaf "gap" 4) | ||
618 | (leaf "width" 8) | ||
619 | (leaf "gaps-between-tabs" 4) | ||
620 | (flag "place-within-column") | ||
621 | (leaf "length" { total-proportion = 1.; }) | ||
622 | (leaf "active-gradient" { | ||
623 | from = "hsla(195 100% 60% 0.75)"; | ||
624 | to = "hsla(155 100% 50% 0.75)"; | ||
625 | angle = 29; | ||
626 | relative-to = "workspace-view"; | ||
627 | }) | ||
628 | (leaf "inactive-gradient" { | ||
629 | from = "hsla(0 0% 42% 0.66)"; | ||
630 | to = "hsla(0 0% 35% 0.66)"; | ||
631 | angle = 29; | ||
632 | relative-to = "workspace-view"; | ||
633 | }) | ||
634 | ]) | ||
635 | ]) | ||
636 | |||
637 | (plain "cursor" [ | ||
638 | (flag "hide-when-typing") | ||
639 | ]) | ||
640 | |||
641 | (map (name: | ||
642 | (node "workspace" name [ | ||
643 | (leaf "open-on-output" "eDP-1") | ||
644 | ]) | ||
645 | ) (map ({name, ...}: name) cfg.scratchspaces)) | ||
646 | (map (name: | ||
647 | (leaf "workspace" name) | ||
648 | ) ["comm" "web" "vid" "bmr"]) | ||
649 | |||
650 | (plain "window-rule" [ | ||
651 | (leaf "clip-to-geometry" true) | ||
652 | ]) | ||
653 | |||
654 | (plain "window-rule" [ | ||
655 | (leaf "match" { is-floating = true; }) | ||
656 | (leaf "geometry-corner-radius" 8) | ||
657 | (plain "shadow" [ (flag "on") ]) | ||
658 | ]) | ||
659 | |||
660 | (plain "window-rule" [ | ||
661 | (leaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; }) | ||
662 | (leaf "block-out-from" "screencast") | ||
663 | ]) | ||
664 | (plain "window-rule" [ | ||
665 | (map (title: | ||
666 | (leaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; inherit title; }) | ||
667 | ) ["^Unlock Database.*" "^Access Request.*" ".*Passkey credentials$" "Browser Access Request$"]) | ||
668 | (leaf "open-focused" true) | ||
669 | (leaf "open-floating" true) | ||
670 | ]) | ||
671 | |||
672 | (map ({ name, match, exclude, windowRuleExtra, ... }: | ||
673 | (optional-node (match != []) (plain "window-rule" [ | ||
674 | (map (leaf "match") match) | ||
675 | (map (leaf "exclude") exclude) | ||
676 | (leaf "open-on-workspace" name) | ||
677 | (leaf "open-maximized" true) | ||
678 | windowRuleExtra | ||
679 | ])) | ||
680 | ) cfg.scratchspaces) | ||
681 | |||
682 | (plain "window-rule" [ | ||
683 | (leaf "match" { app-id = "^emacs$"; }) | ||
684 | (leaf "match" { app-id = "^firefox$"; }) | ||
685 | (plain "default-column-width" [(leaf "proportion" (2. / 3.))]) | ||
686 | ]) | ||
687 | (plain "window-rule" [ | ||
688 | (leaf "match" { app-id = "^kitty$"; }) | ||
689 | (leaf "match" { app-id = "^kitty-play$"; }) | ||
690 | (plain "default-column-width" [(leaf "proportion" (1. / 3.))]) | ||
691 | ]) | ||
692 | |||
693 | (plain "window-rule" [ | ||
694 | (leaf "match" { app-id = "^thunderbird$"; }) | ||
695 | (leaf "match" { app-id = "^Element$"; }) | ||
696 | (leaf "match" { app-id = "^chrome-web\.openrainbow\.com__-Default$"; }) | ||
697 | (leaf "open-on-workspace" "comm") | ||
698 | ]) | ||
699 | (plain "window-rule" [ | ||
700 | (leaf "match" { app-id = "^firefox$"; }) | ||
701 | (leaf "open-on-workspace" "web") | ||
702 | (leaf "open-maximized" true) | ||
703 | ]) | ||
704 | (plain "window-rule" [ | ||
705 | (leaf "match" { app-id = "^mpv$"; }) | ||
706 | (leaf "open-on-workspace" "vid") | ||
707 | (plain "default-column-width" [(leaf "proportion" 1.)]) | ||
708 | ]) | ||
709 | (plain "window-rule" [ | ||
710 | (leaf "match" { app-id = "^kitty-play$"; }) | ||
711 | (leaf "open-on-workspace" "vid") | ||
712 | (leaf "open-focused" false) | ||
713 | ]) | ||
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" [ | ||
720 | (leaf "match" { app-id = "^pdfpc$"; }) | ||
721 | (plain "default-column-width" [(leaf "proportion" 1.)]) | ||
722 | ]) | ||
723 | (plain "window-rule" [ | ||
724 | (leaf "match" { app-id = "^pdfpc$"; title = "^.*presentation.*$"; }) | ||
725 | (plain "default-column-width" [(leaf "proportion" 1.)]) | ||
726 | (leaf "open-fullscreen" true) | ||
727 | (leaf "open-on-workspace" "bmr") | ||
728 | (leaf "open-focused" false) | ||
729 | ]) | ||
730 | (plain "window-rule" [ | ||
731 | (map (leaf "match") [ | ||
732 | { app-id = "^Gimp-"; title = "^Quit GIMP$"; } | ||
733 | { app-id = "^org\\.kde\\.polkit-kde-authentication-agent-1$"; } | ||
734 | { app-id = "^xdg-desktop-portal-gtk$"; } | ||
735 | ]) | ||
736 | (leaf "open-floating" true) | ||
737 | ]) | ||
738 | (plain "window-rule" [ | ||
739 | (leaf "match" { app-id = "^org\\.pwmt\\.zathura$"; }) | ||
740 | (leaf "match" { app-id = "^evince$"; }) | ||
741 | (leaf "match" { app-id = "^org\\.gnome\\.Papers$"; }) | ||
742 | (leaf "default-column-display" "tabbed") | ||
743 | ]) | ||
744 | |||
745 | (plain "layer-rule" [ | ||
746 | (leaf "match" { namespace = "^notifications$"; }) | ||
747 | (leaf "match" { namespace = "^waybar$"; }) | ||
748 | (leaf "match" { namespace = "^launcher$"; }) | ||
749 | (leaf "block-out-from" "screencast") | ||
750 | ]) | ||
751 | |||
752 | (plain "binds" | ||
753 | (let | ||
754 | bind = name: cfg: node name (opt-props { | ||
755 | cooldown-ms = cfg.cooldown-ms or null; | ||
756 | } | ||
757 | // (lib.optionalAttrs (!(cfg.repeat or true)) { | ||
758 | repeat = false; | ||
759 | }) | ||
760 | // (lib.optionalAttrs (cfg.allow-when-locked or false) { | ||
761 | allow-when-locked = true; | ||
762 | })) (lib.mapAttrsToList leaf (lib.removeAttrs cfg.action ["__functor"])); | ||
763 | in | ||
764 | [ | ||
765 | (lib.mapAttrsToList bind (with config.lib.niri.actions; { | ||
766 | "Mod+Slash".action = show-hotkey-overlay; | ||
767 | |||
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"); | ||
784 | "Mod+Q".action = close-window; | ||
785 | "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package); | ||
786 | "Mod+Shift+O".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path"; | ||
787 | |||
788 | "Mod+Alt+E".action = spawn (lib.getExe' config.services.emacs.package "emacsclient") "-c"; | ||
789 | "Mod+Alt+Y".action = spawn (lib.getExe (pkgs.writeShellApplication { | ||
790 | name = "queue-yt-dlp"; | ||
791 | runtimeInputs = with pkgs; [ wl-clipboard-rs socat ]; | ||
792 | text = '' | ||
793 | socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/yt-dlp.sock <<<$'{ "urls": ["'"$(wl-paste)"$'"] }' | ||
794 | ''; | ||
795 | })); | ||
796 | "Mod+Alt+L".action = spawn (lib.getExe (pkgs.writeShellApplication { | ||
797 | name = "queue-yt-dlp"; | ||
798 | runtimeInputs = with pkgs; [ wl-clipboard-rs config.programs.kitty.package ]; | ||
799 | text = '' | ||
800 | exec -- kitty --app-id kitty-play --directory "$HOME"/media mpv "$(wl-paste)" | ||
801 | ''; | ||
802 | })); | ||
803 | "Mod+Alt+M".action = spawn (lib.getExe' pkgs.screen-message "sm") "-n" "Fira Mono" "-a" "1" "-f" "#fff" "-b" "#000"; | ||
804 | |||
805 | "Mod+U".action = spawn (lib.getExe (pkgs.writeShellApplication { | ||
806 | name = "qalc-fuzzel"; | ||
807 | runtimeInputs = with pkgs; [ wl-clipboard-rs libqalculate config.programs.fuzzel.package coreutils findutils libnotify gnugrep ]; | ||
808 | text = '' | ||
809 | RESULTS_DIR="$HOME/.cache/qalc-fuzzel" | ||
810 | prev() { | ||
811 | FOUND=false | ||
812 | while IFS= read -r line; do | ||
813 | [[ -n "$line" ]] || continue | ||
814 | FOUND=true | ||
815 | echo "$line" | ||
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) | ||
817 | $FOUND || echo | ||
818 | } | ||
819 | FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> " --width=60) || exit $? | ||
820 | if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then | ||
821 | QALC_RES="$FUZZEL_RES" | ||
822 | QALC_RET=0 | ||
823 | else | ||
824 | QALC_RES=$(qalc -set "autocalc off" "$FUZZEL_RES" 2>&1) | ||
825 | QALC_RET=$? | ||
826 | fi | ||
827 | [[ -n "$QALC_RES" ]] || exit 1 | ||
828 | EXISTING=false | ||
829 | set +o pipefail | ||
830 | grep -Fxrl "$QALC_RES" "$RESULTS_DIR" | xargs -r touch | ||
831 | [[ ''${PIPESTATUS[0]} -eq 0 ]] && EXISTING=true | ||
832 | set -o pipefail | ||
833 | if [[ $QALC_RET -eq 0 ]] && ! $EXISTING; then | ||
834 | set +o pipefail | ||
835 | RES_FILE="$RESULTS_DIR"/$(date -uIs).$(tr -Cd 'a-zA-Z0-9' </dev/random | head -c 10) | ||
836 | set -o pipefail | ||
837 | cat >"$RES_FILE" <<<"$QALC_RES" | ||
838 | fi | ||
839 | [[ "$QALC_RES" =~ .*\ =\ (.*) ]] && QALC_RES="''${BASH_REMATCH[1]}" | ||
840 | [[ $QALC_RET -eq 0 ]] && wl-copy "$QALC_RES" | ||
841 | notify-send "$QALC_RES" | ||
842 | ''; | ||
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"); | ||
859 | "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication { | ||
860 | name = "emoji-fuzzel"; | ||
861 | runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ]; | ||
862 | text = '' | ||
863 | FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " --cache "$HOME"/.cache/fuzzel-emoji --width=60 <"$HOME"/.local/share/emoji-data/list.txt) || exit $? | ||
864 | [[ -n "$FUZZEL_RES" ]] || exit 1 | ||
865 | wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste | ||
866 | ''; | ||
867 | })); | ||
868 | "Print".action = screenshot; | ||
869 | "Control+Print".action = screenshot-window; | ||
870 | "Shift+Print".action = kdl.magic-leaf "screenshot-screen"; | ||
871 | "Mod+B".action = with-select-window-action ".workspace_id == ($active_workspace | tonumber)" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; | ||
872 | "Mod+Shift+B".action = with-select-window-action "true" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; | ||
873 | |||
874 | "Mod+Escape" = { | ||
875 | allow-inhibiting = false; | ||
876 | action = toggle-keyboard-shortcuts-inhibit; | ||
877 | }; | ||
878 | |||
879 | "Mod+H".action = focus-column-left; | ||
880 | "Mod+T".action = focus-window-down; | ||
881 | "Mod+N".action = focus-window-up; | ||
882 | "Mod+S".action = focus-column-right; | ||
883 | |||
884 | "Mod+Shift+H".action = move-column-left; | ||
885 | "Mod+Shift+T".action = move-window-down; | ||
886 | "Mod+Shift+N".action = move-window-up; | ||
887 | "Mod+Shift+S".action = move-column-right; | ||
888 | |||
889 | "Mod+Control+H".action = focus-monitor-left; | ||
890 | "Mod+Control+T".action = focus-monitor-down; | ||
891 | "Mod+Control+N".action = focus-monitor-up; | ||
892 | "Mod+Control+S".action = focus-monitor-right; | ||
893 | |||
894 | "Mod+Shift+Control+H".action = move-workspace-to-monitor-left; | ||
895 | "Mod+Shift+Control+T".action = move-workspace-to-monitor-down; | ||
896 | "Mod+Shift+Control+N".action = move-workspace-to-monitor-up; | ||
897 | "Mod+Shift+Control+S".action = move-workspace-to-monitor-right; | ||
898 | |||
899 | "Mod+G".action = focus-adjacent-workspace "down"; | ||
900 | "Mod+C".action = focus-adjacent-workspace "up"; | ||
901 | |||
902 | "Mod+Shift+G".action = move-column-to-adjacent-workspace "down"; | ||
903 | "Mod+Shift+C".action = move-column-to-adjacent-workspace "up"; | ||
904 | |||
905 | "Mod+Shift+Control+G".action = move-workspace-down; | ||
906 | "Mod+Shift+Control+C".action = move-workspace-up; | ||
907 | |||
908 | "Mod+ParenLeft".action = focus-workspace "comm"; | ||
909 | "Mod+Shift+ParenLeft".action = kdl.magic-leaf "move-column-to-workspace" "comm"; | ||
910 | |||
911 | "Mod+ParenRight".action = focus-workspace "web"; | ||
912 | "Mod+Shift+ParenRight".action = kdl.magic-leaf "move-column-to-workspace" "web"; | ||
913 | |||
914 | "Mod+BraceRight".action = focus-workspace "read"; | ||
915 | "Mod+Shift+BraceRight".action = kdl.magic-leaf "move-column-to-workspace" "read"; | ||
916 | |||
917 | "Mod+BraceLeft".action = focus-workspace "mon"; | ||
918 | "Mod+Shift+BraceLeft".action = kdl.magic-leaf "move-column-to-workspace" "mon"; | ||
919 | |||
920 | "Mod+Asterisk".action = focus-workspace "vid"; | ||
921 | "Mod+Shift+Asterisk".action = kdl.magic-leaf "move-column-to-workspace" "vid"; | ||
922 | |||
923 | "Mod+Plus".action = with-unnamed-workspace-action ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; | ||
924 | "Mod+Shift+Plus".action = with-unnamed-workspace-action ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}, "focus": true}}}''; | ||
925 | |||
926 | "Mod+M".action = consume-or-expel-window-left; | ||
927 | "Mod+W".action = consume-or-expel-window-right; | ||
928 | |||
929 | "Mod+Shift+M".action = toggle-column-tabbed-display; | ||
930 | |||
931 | "Mod+R".action = switch-preset-column-width; | ||
932 | "Mod+Shift+R".action = maximize-column; | ||
933 | "Mod+Shift+Ctrl+R".action = switch-preset-window-height; | ||
934 | "Mod+F".action = center-column; | ||
935 | "Mod+Shift+F".action = toggle-windowed-fullscreen; | ||
936 | "Mod+Ctrl+Shift+F".action = fullscreen-window; | ||
937 | |||
938 | "Mod+V".action = switch-focus-between-floating-and-tiling; | ||
939 | "Mod+Shift+V".action = toggle-window-floating; | ||
940 | |||
941 | "Mod+Left".action = set-column-width "-10%"; | ||
942 | "Mod+Down".action = set-window-height "-10%"; | ||
943 | "Mod+Up".action = set-window-height "+10%"; | ||
944 | "Mod+Right".action = set-column-width "+10%"; | ||
945 | |||
946 | "Mod+Shift+Z" = { | ||
947 | action = spawn (lib.getExe niri) "msg" "action" "power-off-monitors"; | ||
948 | allow-when-locked = true; | ||
949 | }; | ||
950 | "Mod+Shift+L".action = spawn loginctl "lock-session"; | ||
951 | "Mod+Shift+E".action = quit; | ||
952 | "Mod+Shift+Minus" = { | ||
953 | action = spawn systemctl "suspend"; | ||
954 | allow-when-locked = true; | ||
955 | }; | ||
956 | "Mod+Shift+Control+Minus" = { | ||
957 | action = spawn systemctl "hibernate"; | ||
958 | allow-when-locked = true; | ||
959 | }; | ||
960 | "Mod+Shift+P" = { | ||
961 | action = spawn (lib.getExe pkgs.playerctl) "-a" "pause"; | ||
962 | allow-when-locked = true; | ||
963 | }; | ||
964 | |||
965 | "XF86MonBrightnessUp" = { | ||
966 | action = spawn swayosd-client "--brightness" "raise"; | ||
967 | allow-when-locked = true; | ||
968 | }; | ||
969 | "XF86MonBrightnessDown" = { | ||
970 | action = spawn swayosd-client "--brightness" "lower"; | ||
971 | allow-when-locked = true; | ||
972 | }; | ||
973 | "XF86AudioRaiseVolume" = { | ||
974 | action = spawn swayosd-client "--output-volume" "raise"; | ||
975 | allow-when-locked = true; | ||
976 | }; | ||
977 | "XF86AudioLowerVolume" = { | ||
978 | action = spawn swayosd-client "--output-volume" "lower"; | ||
979 | allow-when-locked = true; | ||
980 | }; | ||
981 | "XF86AudioMute" = { | ||
982 | action = spawn swayosd-client "--output-volume" "mute-toggle"; | ||
983 | allow-when-locked = true; | ||
984 | }; | ||
985 | "XF86AudioMicMute" = { | ||
986 | action = spawn swayosd-client "--input-volume" "mute-toggle"; | ||
987 | allow-when-locked = true; | ||
988 | }; | ||
989 | |||
990 | "Mod+Semicolon".action = spawn makoctl "dismiss" "--group"; | ||
991 | "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all"; | ||
992 | "Mod+Period".action = spawn makoctl "menu" "--" (lib.getExe config.programs.fuzzel.package) "--dmenu"; | ||
993 | "Mod+Comma".action = spawn makoctl "restore"; | ||
994 | |||
995 | "Mod+Control+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"FocusWorkspace\":{\"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"); | ||
1007 | })) | ||
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) | ||
1010 | ] | ||
1011 | )) | ||
1012 | ]; | ||
138 | }; | 1013 | }; |
139 | } | 1014 | } |
diff --git a/accounts/gkleen@sif/niri/mako.nix b/accounts/gkleen@sif/niri/mako.nix new file mode 100644 index 00000000..eba26caa --- /dev/null +++ b/accounts/gkleen@sif/niri/mako.nix | |||
@@ -0,0 +1,113 @@ | |||
1 | { config, lib, pkgs, ... }: | ||
2 | { | ||
3 | config = { | ||
4 | services.mako = { | ||
5 | enable = true; | ||
6 | settings = { | ||
7 | font = "Fira Sans 10"; | ||
8 | format = "<i>%s</i>\\n%b"; | ||
9 | margin = "2"; | ||
10 | max-visible = -1; | ||
11 | background-color = "#000000dd"; | ||
12 | progress-color = "source #223544ff"; | ||
13 | width = 384; | ||
14 | outer-margin = 1; | ||
15 | max-history = 100; | ||
16 | max-icon-size = 48; | ||
17 | }; | ||
18 | criteria = { | ||
19 | grouped.format = "<b>(%g)</b> <i>%s</i>\\n%b"; | ||
20 | "urgency=low".text-color = "#999999ff"; | ||
21 | "urgency=critical".background-color = "#900000dd"; | ||
22 | "app-name=Element".group-by = "summary"; | ||
23 | "app-name=poweralertd" = { | ||
24 | history = false; | ||
25 | ignore-timeout = true; | ||
26 | default-timeout = 2000; | ||
27 | }; | ||
28 | "app-name=worktime".history = false; | ||
29 | "mode=silent".invisible = true; | ||
30 | }; | ||
31 | package = pkgs.symlinkJoin { | ||
32 | name = "${pkgs.mako.name}-wrapped"; | ||
33 | paths = with pkgs; [ mako ]; | ||
34 | inherit (pkgs.mako) meta; | ||
35 | postBuild = '' | ||
36 | rm -r $out/share/dbus-1 | ||
37 | ''; | ||
38 | }; | ||
39 | }; | ||
40 | systemd.user.services.mako = { | ||
41 | Unit = { | ||
42 | Description = "Mako notification daemon"; | ||
43 | PartOf = [ "graphical-session.target" ]; | ||
44 | }; | ||
45 | Install = { | ||
46 | WantedBy = [ "graphical-session.target" ]; | ||
47 | }; | ||
48 | Service = { | ||
49 | Type = "dbus"; | ||
50 | BusName = "org.freedesktop.Notifications"; | ||
51 | ExecStart = lib.getExe config.services.mako.package; | ||
52 | RestartSec = 5; | ||
53 | Restart = "always"; | ||
54 | }; | ||
55 | }; | ||
56 | |||
57 | systemd.user.services.mako-follows-focus = { | ||
58 | Unit = { | ||
59 | BindsTo = [ "niri.service" "mako.service" ]; | ||
60 | After = [ "niri.service" "mako.service" ]; | ||
61 | }; | ||
62 | Service = { | ||
63 | Type = "simple"; | ||
64 | Restart = "always"; | ||
65 | ExecStart = pkgs.writers.writePython3 "mako-follows-focus" { | ||
66 | libraries = with pkgs.python3Packages; []; | ||
67 | } '' | ||
68 | import os | ||
69 | import socket | ||
70 | import json | ||
71 | import subprocess | ||
72 | |||
73 | |||
74 | current_output = None | ||
75 | workspaces = [] | ||
76 | |||
77 | |||
78 | def output_changed(new_output): | ||
79 | global current_output | ||
80 | |||
81 | if current_output == new_output: | ||
82 | return | ||
83 | |||
84 | current_output = new_output | ||
85 | subprocess.run(["makoctl", "reload"]) | ||
86 | |||
87 | |||
88 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||
89 | sock.connect(os.environ["NIRI_SOCKET"]) | ||
90 | sock.send(b"\"EventStream\"\n") | ||
91 | for line in sock.makefile(buffering=1, encoding='utf-8'): | ||
92 | if line_json := json.loads(line): | ||
93 | if "WorkspacesChanged" in line_json: | ||
94 | workspaces = line_json["WorkspacesChanged"]["workspaces"] | ||
95 | for workspace in workspaces: | ||
96 | if not workspace["is_focused"]: | ||
97 | continue | ||
98 | output_changed(workspace["output"]) | ||
99 | break | ||
100 | if "WorkspaceActivated" in line_json and line_json["WorkspaceActivated"]["focused"]: # noqa: E501 | ||
101 | for workspace in workspaces: | ||
102 | if not workspace["id"] == line_json["WorkspaceActivated"]["id"]: # noqa: E501 | ||
103 | continue | ||
104 | output_changed(workspace["output"]) | ||
105 | break | ||
106 | ''; | ||
107 | }; | ||
108 | Install = { | ||
109 | WantedBy = [ "mako.service" ]; | ||
110 | }; | ||
111 | }; | ||
112 | }; | ||
113 | } | ||
diff --git a/accounts/gkleen@sif/niri/swayosd.nix b/accounts/gkleen@sif/niri/swayosd.nix new file mode 100644 index 00000000..54ebb302 --- /dev/null +++ b/accounts/gkleen@sif/niri/swayosd.nix | |||
@@ -0,0 +1,66 @@ | |||
1 | { pkgs, ... }: | ||
2 | { | ||
3 | config = { | ||
4 | services.swayosd = { | ||
5 | enable = true; | ||
6 | topMargin = 0.4769706078; | ||
7 | stylePath = pkgs.runCommand "style.css" { | ||
8 | passAsFile = [ "src" ]; | ||
9 | src = '' | ||
10 | window#osd { | ||
11 | padding: 12px 20px; | ||
12 | border-radius: 999px; | ||
13 | border: none; | ||
14 | background: rgba(0, 0, 0, 0.87); | ||
15 | |||
16 | #container { | ||
17 | margin: 16px; | ||
18 | } | ||
19 | |||
20 | image, | ||
21 | label { | ||
22 | color: rgb(255, 255, 255); | ||
23 | |||
24 | &:disabled { | ||
25 | opacity: 1; | ||
26 | color: rgb(84, 84, 84); | ||
27 | } | ||
28 | } | ||
29 | |||
30 | progressbar { | ||
31 | min-height: 6px; | ||
32 | border-radius: 999px; | ||
33 | background: transparent; | ||
34 | border: none; | ||
35 | |||
36 | trough, progress { | ||
37 | min-height: inherit; | ||
38 | border-radius: inherit; | ||
39 | border: none; | ||
40 | } | ||
41 | |||
42 | trough { | ||
43 | background: rgb(127, 127, 127); | ||
44 | } | ||
45 | progress { | ||
46 | background: rgb(255, 255, 255); | ||
47 | } | ||
48 | |||
49 | &:disabled { | ||
50 | opacity: 1; | ||
51 | |||
52 | trough { | ||
53 | background: rgb(19, 19, 19); | ||
54 | } | ||
55 | progress { | ||
56 | background: rgb(38, 38, 38); | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | } | ||
61 | ''; | ||
62 | buildInputs = with pkgs; [sass]; | ||
63 | } "scss -C --sourcemap=none --style=compact $srcPath $out"; | ||
64 | }; | ||
65 | }; | ||
66 | } | ||
diff --git a/accounts/gkleen@sif/niri/waybar.nix b/accounts/gkleen@sif/niri/waybar.nix index 1a25b581..c02a9a76 100644 --- a/accounts/gkleen@sif/niri/waybar.nix +++ b/accounts/gkleen@sif/niri/waybar.nix | |||
@@ -1,5 +1,7 @@ | |||
1 | { lib, pkgs, ... }: | 1 | { lib, config, pkgs, ... }: |
2 | { | 2 | let |
3 | swayosd-client = lib.getExe' config.services.swayosd.package "swayosd-client"; | ||
4 | in { | ||
3 | config = { | 5 | config = { |
4 | programs.waybar = { | 6 | programs.waybar = { |
5 | enable = true; | 7 | enable = true; |
@@ -18,20 +20,76 @@ | |||
18 | { | 20 | { |
19 | layer = "top"; | 21 | layer = "top"; |
20 | position = "top"; | 22 | position = "top"; |
21 | height = 14; | 23 | height = 21; |
22 | output = [ "eDP-1" "DP-2" "DP-3" ]; | 24 | output = [ "eDP-1" "DP-2" "DP-3" ]; |
23 | modules-left = [ "niri/workspaces" ]; | 25 | modules-left = [ "niri/workspaces" ]; |
24 | modules-center = [ "niri/window" ]; | 26 | modules-center = [ "niri/window" ]; |
25 | modules-right = [ # "custom/worktime" "custom/worktime-today" | 27 | modules-right = [ "custom/worktime" "custom/worktime-today" |
26 | "custom/weather" | 28 | "custom/weather" |
27 | # "custom/keymap" | 29 | "custom/keymap" |
28 | "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "clock" ]; | 30 | "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "custom/mako" "custom/lid_inhibitor" "clock" ]; |
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 | }; | ||
38 | "custom/mako" = { | ||
39 | format = "{}"; | ||
40 | return-type = "json"; | ||
41 | exec = pkgs.writers.writePython3 "mako-silent" { libraries = [ pkgs.python3Packages.dbus-next ]; } '' | ||
42 | from dbus_next.aio import MessageBus | ||
43 | |||
44 | import asyncio | ||
45 | |||
46 | import json | ||
47 | |||
48 | |||
49 | loop = asyncio.new_event_loop() | ||
50 | asyncio.set_event_loop(loop) | ||
51 | |||
52 | |||
53 | async def main(): | ||
54 | bus = await MessageBus().connect() | ||
55 | # the introspection xml would normally be included in your project, but | ||
56 | # this is convenient for development | ||
57 | introspection = await bus.introspect('org.freedesktop.Notifications', '/fr/emersion/Mako') # noqa: E501 | ||
58 | |||
59 | obj = bus.get_proxy_object('org.freedesktop.Notifications', '/fr/emersion/Mako', introspection) # noqa: E501 | ||
60 | mako = obj.get_interface('fr.emersion.Mako') | ||
61 | properties = obj.get_interface('org.freedesktop.DBus.Properties') | ||
62 | |||
63 | async def print_mode(): | ||
64 | modes = await mako.get_modes() | ||
65 | is_silent = "silent" in modes | ||
66 | icon = "󰂛" if is_silent else "󰂚" | ||
67 | text = f"<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>" # noqa: E501 | ||
68 | if is_silent: | ||
69 | text = f"<span color=\"#ffffff\">{text}</span>" | ||
70 | print(json.dumps({'text': text, 'tooltip': ', '.join(modes)}, separators=(',', ':')), flush=True) # noqa: E501 | ||
71 | |||
72 | async def on_properties_changed(interface_name, changed_properties, invalidated_properties): # noqa: E501 | ||
73 | if "Modes" not in invalidated_properties: | ||
74 | return | ||
75 | |||
76 | await print_mode() | ||
77 | |||
78 | properties.on_properties_changed(on_properties_changed) | ||
79 | await print_mode() | ||
29 | 80 | ||
81 | await loop.create_future() | ||
82 | |||
83 | |||
84 | loop.run_until_complete(main()) | ||
85 | ''; | ||
86 | on-click = "makoctl mode -t silent"; | ||
87 | }; | ||
30 | "custom/weather" = { | 88 | "custom/weather" = { |
31 | format = "{}"; | 89 | format = "{}"; |
32 | tooltip = true; | 90 | tooltip = true; |
33 | interval = 3600; | 91 | interval = 3600; |
34 | exec = "${lib.getExe pkgs.wttrbar} --hide-conditions --nerd --custom-indicator \"<span font=\\\"Symbols Nerd Font Mono\\\" size=\\\"120%\\\">{ICON}</span> {FeelsLikeC}°\""; | 92 | exec = "${lib.getExe pkgs.wttrbar} --hide-conditions --nerd --custom-indicator \"<span font=\\\"Symbols Nerd Font Mono\\\" size=\\\"100%\\\">{ICON}</span> {FeelsLikeC}°\""; |
35 | return-type = "json"; | 93 | return-type = "json"; |
36 | }; | 94 | }; |
37 | "custom/keymap" = { | 95 | "custom/keymap" = { |
@@ -41,8 +99,6 @@ | |||
41 | exec = pkgs.writers.writePython3 "keymap" {} '' | 99 | exec = pkgs.writers.writePython3 "keymap" {} '' |
42 | import os | 100 | import os |
43 | import socket | 101 | import socket |
44 | import re | ||
45 | import subprocess | ||
46 | import json | 102 | import json |
47 | 103 | ||
48 | 104 | ||
@@ -55,33 +111,33 @@ | |||
55 | print(json.dumps({'text': short, 'tooltip': keymap}, separators=(',', ':')), flush=True) # noqa: E501 | 111 | print(json.dumps({'text': short, 'tooltip': keymap}, separators=(',', ':')), flush=True) # noqa: E501 |
56 | 112 | ||
57 | 113 | ||
58 | r = subprocess.run(["hyprctl", "devices", "-j"], check=True, stdout=subprocess.PIPE, text=True) # noqa: E501 | 114 | keyboard_layouts = [] |
59 | for keyboard in json.loads(r.stdout)['keyboards']: | ||
60 | if keyboard['name'] != "at-translated-set-2-keyboard": | ||
61 | continue | ||
62 | output(keyboard['active_keymap']) | ||
63 | 115 | ||
64 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | 116 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
65 | sock.connect(os.environ["XDG_RUNTIME_DIR"] + "/hypr/" + os.environ["HYPRLAND_INSTANCE_SIGNATURE"] + "/.socket2.sock") # noqa: E501 | 117 | sock.connect(os.environ["NIRI_SOCKET"]) |
66 | expected = re.compile(r'^activelayout>>at-translated-set-2-keyboard,(?P<keymap>.+)$') # noqa: E501 | 118 | sock.send(b"\"EventStream\"\n") |
67 | for line in sock.makefile(buffering=1, encoding='utf-8'): | 119 | for line in sock.makefile(buffering=1, encoding='utf-8'): |
68 | if match := expected.match(line): | 120 | if line_json := json.loads(line): |
69 | output(match.group("keymap")) | 121 | if "KeyboardLayoutsChanged" in line_json: |
122 | keyboard_layouts = line_json["KeyboardLayoutsChanged"]["keyboard_layouts"]["names"] # noqa: E501 | ||
123 | output(keyboard_layouts[line_json["KeyboardLayoutsChanged"]["keyboard_layouts"]["current_idx"]]) # noqa: E501 | ||
124 | if "KeyboardLayoutSwitched" in line_json: | ||
125 | output(keyboard_layouts[line_json["KeyboardLayoutSwitched"]["idx"]]) # noqa: E501 | ||
70 | ''; | 126 | ''; |
71 | on-click = "hyprctl switchxkblayout at-translated-set-2-keyboard next"; | 127 | on-click = "niri msg action switch-layout next"; |
72 | }; | 128 | }; |
73 | "custom/worktime" = { | 129 | "custom/worktime" = { |
74 | interval = 60; | 130 | interval = 60; |
75 | exec = lib.getExe pkgs.worktime; | 131 | exec = "${lib.getExe pkgs.worktime} time --waybar"; |
76 | tooltip = false; | 132 | return-type = "json"; |
77 | }; | 133 | }; |
78 | "custom/worktime-today" = { | 134 | "custom/worktime-today" = { |
79 | interval = 60; | 135 | interval = 60; |
80 | exec = "${lib.getExe pkgs.worktime} today"; | 136 | exec = "${lib.getExe pkgs.worktime} today --waybar"; |
81 | tooltip = false; | 137 | return-type = "json"; |
82 | }; | 138 | }; |
83 | "niri/workspaces" = { | 139 | "niri/workspaces" = { |
84 | all-outputs = true; | 140 | ignore = map ({ name, ... }: name) config.programs.niri.scratchspaces; |
85 | }; | 141 | }; |
86 | "niri/window" = { | 142 | "niri/window" = { |
87 | separate-outputs = true; | 143 | separate-outputs = true; |
@@ -142,8 +198,8 @@ | |||
142 | icon-size = iconSize; | 198 | icon-size = iconSize; |
143 | tooltip-format = "{percent}%"; | 199 | tooltip-format = "{percent}%"; |
144 | format-icons = ["󰃚" "󰃛" "󰃜" "󰃝" "󰃞" "󰃟" "󰃠"]; | 200 | format-icons = ["󰃚" "󰃛" "󰃜" "󰃝" "󰃞" "󰃟" "󰃠"]; |
145 | on-scroll-up = "lightctl -d -e4 -n1 up"; | 201 | on-scroll-up = "${swayosd-client} --brightness raise"; |
146 | on-scroll-down = "lightctl -d -e4 -n1 down"; | 202 | on-scroll-down = "${swayosd-client} --brightness lower"; |
147 | }; | 203 | }; |
148 | wireplumber = { | 204 | wireplumber = { |
149 | format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>"; | 205 | format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>"; |
@@ -152,22 +208,22 @@ | |||
152 | format-icons = ["󰕿" "󰖀" "󰕾"]; | 208 | format-icons = ["󰕿" "󰖀" "󰕾"]; |
153 | format-muted = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">󰝟</span>"; | 209 | format-muted = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">󰝟</span>"; |
154 | # ignored-sinks = ["Easy Effects Sink"]; | 210 | # ignored-sinks = ["Easy Effects Sink"]; |
155 | on-scroll-up = "volumectl -d -u up"; | 211 | on-scroll-up = "${swayosd-client} --output-volume raise"; |
156 | on-scroll-down = "volumectl -d -u down"; | 212 | on-scroll-down = "${swayosd-client} --output-volume lower"; |
157 | on-click = "volumectl -d toggle-mute"; | 213 | on-click = "${swayosd-client} --output-volume mute-toggle"; |
158 | }; | 214 | }; |
159 | } | 215 | } |
160 | { | 216 | { |
161 | layer = "top"; | 217 | layer = "top"; |
162 | position = "top"; | 218 | position = "top"; |
163 | height = 14; | 219 | height = 14; |
164 | output = [ "!eDP-1" "!DP-2" "!DP-3" ]; | 220 | output = [ "!eDP-1" "!DP-2" "!DP-3" "*" ]; |
165 | modules-left = [ "niri/workspaces" ]; | 221 | modules-left = [ "niri/workspaces" ]; |
166 | modules-center = [ "niri/window" ]; | 222 | modules-center = [ "niri/window" ]; |
167 | modules-right = [ "clock" ]; | 223 | modules-right = [ "clock" ]; |
168 | 224 | ||
169 | "niri/workspaces" = { | 225 | "niri/workspaces" = { |
170 | all-outputs = false; | 226 | ignore = map ({ name, ... }: name) config.programs.niri.scratchspaces; |
171 | }; | 227 | }; |
172 | "niri/window" = { | 228 | "niri/window" = { |
173 | separate-outputs = true; | 229 | separate-outputs = true; |
@@ -193,7 +249,7 @@ | |||
193 | 249 | ||
194 | * { | 250 | * { |
195 | border: none; | 251 | border: none; |
196 | font-family: "Fira Sans Nerd Font"; | 252 | font-family: "Fira Sans"; |
197 | font-size: 10pt; | 253 | font-size: 10pt; |
198 | min-height: 0; | 254 | min-height: 0; |
199 | } | 255 | } |
@@ -204,10 +260,10 @@ | |||
204 | } | 260 | } |
205 | 261 | ||
206 | .modules-left { | 262 | .modules-left { |
207 | margin-left: 9px; | 263 | margin-left: 38px; |
208 | } | 264 | } |
209 | .modules-right { | 265 | .modules-right { |
210 | margin-right: 9px; | 266 | margin-right: 38px; |
211 | } | 267 | } |
212 | 268 | ||
213 | .module { | 269 | .module { |
@@ -215,13 +271,11 @@ | |||
215 | } | 271 | } |
216 | 272 | ||
217 | #workspaces button { | 273 | #workspaces button { |
218 | color: @grey; | ||
219 | } | ||
220 | #workspaces button.hosting-monitor { | ||
221 | color: @white; | 274 | color: @white; |
275 | padding: 2px 5px; | ||
222 | } | 276 | } |
223 | #workspaces button.visible { | 277 | #workspaces button.empty { |
224 | color: @blue; | 278 | color: @grey; |
225 | } | 279 | } |
226 | #workspaces button.active { | 280 | #workspaces button.active { |
227 | color: @green; | 281 | color: @green; |
@@ -234,22 +288,31 @@ | |||
234 | color: @grey; | 288 | color: @grey; |
235 | margin: 0 5px; | 289 | margin: 0 5px; |
236 | } | 290 | } |
237 | #custom-weather, #custom-worktime-today { | 291 | #custom-weather { |
238 | margin-right: 3px; | 292 | margin-right: 3px; |
239 | } | 293 | } |
240 | #custom-keymap, #custom-weather { | 294 | #custom-keymap { |
241 | margin-left: 3px; | 295 | margin-left: 3px; |
296 | margin-right: 3px; | ||
242 | } | 297 | } |
243 | 298 | ||
244 | #tray { | 299 | #tray { |
245 | margin: 0; | 300 | margin: 0; |
246 | } | 301 | } |
247 | #battery, #idle_inhibitor, #backlight, #wireplumber { | 302 | #battery, #idle_inhibitor, #backlight, #wireplumber, #custom-mako, #custom-lid_inhibitor { |
248 | color: @grey; | 303 | color: @grey; |
249 | margin: 0 5px 0 2px; | 304 | margin: 0 5px 0 2px; |
250 | } | 305 | } |
251 | #idle_inhibitor { | 306 | #idle_inhibitor { |
252 | margin-right: 2px; | 307 | margin-right: 4px; |
308 | margin-left: 6px; | ||
309 | } | ||
310 | #custom-mako { | ||
311 | margin-right: 4px; | ||
312 | margin-left: 3px; | ||
313 | } | ||
314 | #custom-lid_inhibitor { | ||
315 | margin-right: 3px; | ||
253 | margin-left: 3px; | 316 | margin-left: 3px; |
254 | } | 317 | } |
255 | #battery { | 318 | #battery { |
@@ -270,17 +333,24 @@ | |||
270 | #idle_inhibitor.activated { | 333 | #idle_inhibitor.activated { |
271 | color: @white; | 334 | color: @white; |
272 | } | 335 | } |
336 | #custom-worktime.running, #custom-worktime-today.running { | ||
337 | color: @white; | ||
338 | } | ||
339 | #custom-worktime.over, #custom-worktime-today.over { | ||
340 | color: @orange; | ||
341 | } | ||
273 | 342 | ||
274 | #idle_inhibitor { | 343 | #idle_inhibitor, #custom-lid_inhibitor { |
275 | padding-top: 1px; | 344 | padding-top: 1px; |
276 | } | 345 | } |
277 | 346 | ||
278 | #privacy { | 347 | #privacy { |
279 | color: @red; | 348 | color: @red; |
280 | margin: -1px 2px 0px 5px; | 349 | margin: -1px 4px 0px 3px; |
281 | } | 350 | } |
282 | #clock { | 351 | #clock { |
283 | /* margin-right: 5px; */ | 352 | /* margin-right: 5px; */ |
353 | font-feature-settings: "tnum"; | ||
284 | } | 354 | } |
285 | ''; | 355 | ''; |
286 | }; | 356 | }; |
diff --git a/accounts/gkleen@sif/ssh-hosts.nix b/accounts/gkleen@sif/ssh-hosts.nix index 107f1e76..a250509b 100644 --- a/accounts/gkleen@sif/ssh-hosts.nix +++ b/accounts/gkleen@sif/ssh-hosts.nix | |||
@@ -1,5 +1,12 @@ | |||
1 | { pkgs, ... }: | 1 | { lib, pkgs, ... }: |
2 | { | 2 | let |
3 | autosshProxyPorts = { | ||
4 | "ssh.math.lmu.de" = 8118; | ||
5 | "mathw0h" = 8122; | ||
6 | "mathw0e" = 8124; | ||
7 | }; | ||
8 | autosshProxy = host: "${lib.getExe pkgs.socat} - SOCKS4A:127.0.0.1:%h:%p,socksport=${toString autosshProxyPorts.${host}}"; | ||
9 | in { | ||
3 | "git.ymir" = | 10 | "git.ymir" = |
4 | { hostname = "ymir.yggdrasil.li"; | 11 | { hostname = "ymir.yggdrasil.li"; |
5 | user = "gitolite"; | 12 | user = "gitolite"; |
@@ -290,15 +297,15 @@ | |||
290 | }; | 297 | }; |
291 | "mathw0d" = | 298 | "mathw0d" = |
292 | { hostname = "mathw0d.mathinst.loc"; | 299 | { hostname = "mathw0d.mathinst.loc"; |
293 | proxyJump = "mathw0h"; | 300 | proxyCommand = autosshProxy "mathw0h"; |
294 | }; | 301 | }; |
295 | "mathw0e" = | 302 | "mathw0e" = |
296 | { hostname = "mathw0e.mathinst.loc"; | 303 | { hostname = "mathw0e.mathinst.loc"; |
297 | proxyJump = "mathw0h"; | 304 | proxyCommand = autosshProxy "mathw0h"; |
298 | }; | 305 | }; |
299 | "mathw0f" = | 306 | "mathw0f" = |
300 | { hostname = "mathw0f.mathinst.loc"; | 307 | { hostname = "mathw0f.mathinst.loc"; |
301 | proxyJump = "mathw0h"; | 308 | proxyCommand = autosshProxy "mathw0h"; |
302 | }; | 309 | }; |
303 | "mathw0g" = | 310 | "mathw0g" = |
304 | { hostname = "mathw0g.mathinst.loc"; | 311 | { hostname = "mathw0g.mathinst.loc"; |
@@ -306,8 +313,8 @@ | |||
306 | "mathw0h" = | 313 | "mathw0h" = |
307 | { hostname = "mathw0h.mathinst.loc"; | 314 | { hostname = "mathw0h.mathinst.loc"; |
308 | }; | 315 | }; |
309 | "proxy.mathw0g" = | 316 | "proxy.ssh.math.lmu.de" = |
310 | { hostname = "mathw0g.mathinst.loc"; | 317 | { hostname = "ssh.math.lmu.de"; |
311 | extraOptions = { | 318 | extraOptions = { |
312 | ControlPath = "none"; | 319 | ControlPath = "none"; |
313 | ExitOnForwardFailure = "yes"; | 320 | ExitOnForwardFailure = "yes"; |
@@ -317,7 +324,17 @@ | |||
317 | }; | 324 | }; |
318 | "proxy.mathw0h" = | 325 | "proxy.mathw0h" = |
319 | { hostname = "mathw0h.mathinst.loc"; | 326 | { hostname = "mathw0h.mathinst.loc"; |
320 | proxyJump = "proxy.mathw0g"; | 327 | proxyCommand = autosshProxy "ssh.math.lmu.de"; |
328 | extraOptions = { | ||
329 | ControlPath = "none"; | ||
330 | ExitOnForwardFailure = "yes"; | ||
331 | ServerAliveCountMax = "15"; | ||
332 | ServerAliveInterval = "2"; | ||
333 | }; | ||
334 | }; | ||
335 | "proxy.mathw0e" = | ||
336 | { hostname = "mathw0e.mathinst.loc"; | ||
337 | proxyCommand = autosshProxy "mathw0h"; | ||
321 | extraOptions = { | 338 | extraOptions = { |
322 | ControlPath = "none"; | 339 | ControlPath = "none"; |
323 | ExitOnForwardFailure = "yes"; | 340 | ExitOnForwardFailure = "yes"; |
@@ -327,7 +344,7 @@ | |||
327 | }; | 344 | }; |
328 | "vrt-kvm06" = | 345 | "vrt-kvm06" = |
329 | { hostname = "vrt-kvm06"; | 346 | { hostname = "vrt-kvm06"; |
330 | proxyJump = "mathw0e"; | 347 | proxyCommand = autosshProxy "mathw0e"; |
331 | user = "root"; | 348 | user = "root"; |
332 | extraOptions = { | 349 | extraOptions = { |
333 | PasswordAuthentication = "yes"; | 350 | PasswordAuthentication = "yes"; |
@@ -336,7 +353,7 @@ | |||
336 | }; | 353 | }; |
337 | "vrt-kvm05" = | 354 | "vrt-kvm05" = |
338 | { hostname = "vrt-kvm05"; | 355 | { hostname = "vrt-kvm05"; |
339 | proxyJump = "mathw0e"; | 356 | proxyCommand = autosshProxy "mathw0e"; |
340 | user = "root"; | 357 | user = "root"; |
341 | extraOptions = { | 358 | extraOptions = { |
342 | PasswordAuthentication = "yes"; | 359 | PasswordAuthentication = "yes"; |
@@ -345,7 +362,7 @@ | |||
345 | }; | 362 | }; |
346 | "vrt-kvm04" = | 363 | "vrt-kvm04" = |
347 | { hostname = "vrt-kvm04"; | 364 | { hostname = "vrt-kvm04"; |
348 | proxyJump = "mathw0e"; | 365 | proxyCommand = autosshProxy "mathw0e"; |
349 | user = "root"; | 366 | user = "root"; |
350 | extraOptions = { | 367 | extraOptions = { |
351 | PasswordAuthentication = "yes"; | 368 | PasswordAuthentication = "yes"; |
@@ -354,7 +371,7 @@ | |||
354 | }; | 371 | }; |
355 | "vrt-kvm02" = | 372 | "vrt-kvm02" = |
356 | { hostname = "vrt-kvm02"; | 373 | { hostname = "vrt-kvm02"; |
357 | proxyJump = "mathw0e"; | 374 | proxyCommand = autosshProxy "mathw0e"; |
358 | user = "root"; | 375 | user = "root"; |
359 | extraOptions = { | 376 | extraOptions = { |
360 | PasswordAuthentication = "yes"; | 377 | PasswordAuthentication = "yes"; |
@@ -363,7 +380,7 @@ | |||
363 | }; | 380 | }; |
364 | "vrt-kvm03" = | 381 | "vrt-kvm03" = |
365 | { hostname = "vrt-kvm03"; | 382 | { hostname = "vrt-kvm03"; |
366 | proxyJump = "mathw0e"; | 383 | proxyCommand = autosshProxy "mathw0e"; |
367 | user = "root"; | 384 | user = "root"; |
368 | extraOptions = { | 385 | extraOptions = { |
369 | PasswordAuthentication = "yes"; | 386 | PasswordAuthentication = "yes"; |
@@ -372,7 +389,7 @@ | |||
372 | }; | 389 | }; |
373 | "vrt-kvm01" = | 390 | "vrt-kvm01" = |
374 | { hostname = "vrt-kvm01"; | 391 | { hostname = "vrt-kvm01"; |
375 | proxyJump = "mathw0e"; | 392 | proxyCommand = autosshProxy "mathw0e"; |
376 | user = "root"; | 393 | user = "root"; |
377 | extraOptions = { | 394 | extraOptions = { |
378 | PasswordAuthentication = "yes"; | 395 | PasswordAuthentication = "yes"; |
@@ -381,39 +398,44 @@ | |||
381 | }; | 398 | }; |
382 | "tts-www01" = | 399 | "tts-www01" = |
383 | { hostname = "tts-www01.mathinst.loc"; | 400 | { hostname = "tts-www01.mathinst.loc"; |
384 | proxyJump = "mathw0h"; | 401 | proxyCommand = autosshProxy "mathw0h"; |
385 | user = "root"; | 402 | user = "root"; |
386 | }; | 403 | }; |
387 | "vpn-wg01" = | 404 | "vpn-wg01" = |
388 | { hostname = "vpn-wg01.mathinst.loc"; | 405 | { hostname = "vpn-wg01.mathinst.loc"; |
389 | proxyJump = "mathw0h"; | 406 | proxyCommand = autosshProxy "mathw0h"; |
390 | user = "root"; | 407 | user = "root"; |
391 | }; | 408 | }; |
392 | "repo-apt01" = | 409 | "repo-apt01" = |
393 | { hostname = "repo-apt01.mathinst.loc"; | 410 | { hostname = "repo-apt01.mathinst.loc"; |
394 | proxyJump = "mathw0h"; | 411 | proxyCommand = autosshProxy "mathw0h"; |
395 | user = "root"; | 412 | user = "root"; |
396 | }; | 413 | }; |
397 | "ldap-lmumr01" = | 414 | "ldap-lmumr01" = |
398 | { hostname = "ldap-lmumr01.mathinst.loc"; | 415 | { hostname = "ldap-lmumr01.mathinst.loc"; |
399 | proxyJump = "mathw0h"; | 416 | proxyCommand = autosshProxy "mathw0h"; |
400 | user = "root"; | 417 | user = "root"; |
401 | }; | 418 | }; |
402 | "mail-mi01" = | 419 | "mail-mi01" = |
403 | { hostname = "mail-mi01.mathinst.loc"; | 420 | { hostname = "mail-mi01.mathinst.loc"; |
404 | proxyJump = "mathw0h"; | 421 | proxyCommand = autosshProxy "mathw0h"; |
405 | }; | 422 | }; |
406 | "mail-www02" = | 423 | "mail-www02" = |
407 | { hostname = "mail-www02.mathinst.loc"; | 424 | { hostname = "mail-www02.mathinst.loc"; |
408 | proxyJump = "mathw0h"; | 425 | proxyCommand = autosshProxy "mathw0h"; |
409 | }; | 426 | }; |
410 | "dpl-fai01" = | 427 | "dpl-fai01" = |
411 | { hostname = "dpl-fai01.mathinst.loc"; | 428 | { hostname = "dpl-fai01.mathinst.loc"; |
412 | user = "root"; | 429 | user = "root"; |
413 | }; | 430 | }; |
431 | "dpl-fai02" = | ||
432 | { hostname = "dpl-fai02.mathinst.loc"; | ||
433 | user = "root"; | ||
434 | proxyJump = "mgmt01"; | ||
435 | }; | ||
414 | "math05" = | 436 | "math05" = |
415 | { hostname = "math05.mathinst.loc"; | 437 | { hostname = "math05.mathinst.loc"; |
416 | proxyJump = "mathw0h"; | 438 | proxyCommand = autosshProxy "mathw0h"; |
417 | extraOptions.KexAlgorithms = "+diffie-hellman-group1-sha1"; | 439 | extraOptions.KexAlgorithms = "+diffie-hellman-group1-sha1"; |
418 | }; | 440 | }; |
419 | "switch01" = | 441 | "switch01" = |
@@ -439,20 +461,20 @@ | |||
439 | }; | 461 | }; |
440 | "www-mi01" = | 462 | "www-mi01" = |
441 | { hostname = "www-mi01.mathinst.loc"; | 463 | { hostname = "www-mi01.mathinst.loc"; |
442 | proxyJump = "mathw0h"; | 464 | proxyCommand = autosshProxy "mathw0h"; |
443 | }; | 465 | }; |
444 | "cip04" = | 466 | "cip04" = |
445 | { hostname = "cip04.cipmath.loc"; | 467 | { hostname = "cip04.cipmath.loc"; |
446 | proxyJump = "mathw0h"; | 468 | proxyCommand = autosshProxy "mathw0h"; |
447 | }; | 469 | }; |
448 | "mgmt-cls01" = | 470 | "mgmt-cls01" = |
449 | { user = "root"; | 471 | { user = "root"; |
450 | hostname = "mgmt-cls01.cipmath.loc"; | 472 | hostname = "mgmt-cls01.cipmath.loc"; |
451 | proxyJump = "ssh.math.lmu.de"; | 473 | proxyCommand = autosshProxy "ssh.math.lmu.de"; |
452 | }; | 474 | }; |
453 | "mgmt01" = | 475 | "mgmt01" = |
454 | { hostname = "mgmt01.mathinst.loc"; | 476 | { hostname = "mgmt01.mathinst.loc"; |
455 | proxyJump = "mathw0h"; | 477 | proxyCommand = autosshProxy "mathw0h"; |
456 | user = "root"; | 478 | user = "root"; |
457 | }; | 479 | }; |
458 | "ssh-lb01" = | 480 | "ssh-lb01" = |
@@ -471,17 +493,17 @@ | |||
471 | "rdlx02" = { hostname = "rdlx02.mathinst.loc"; proxyJump = "mgmt01"; }; | 493 | "rdlx02" = { hostname = "rdlx02.mathinst.loc"; proxyJump = "mgmt01"; }; |
472 | "math0d" = | 494 | "math0d" = |
473 | { hostname = "math0d.mathinst.loc"; | 495 | { hostname = "math0d.mathinst.loc"; |
474 | proxyJump = "mathw0h"; | 496 | proxyCommand = autosshProxy "mathw0h"; |
475 | }; | 497 | }; |
476 | "dhcp01" = | 498 | "dhcp01" = |
477 | { hostname = "dhcp01.mathinst.loc"; | 499 | { hostname = "dhcp01.mathinst.loc"; |
478 | user = "root"; | 500 | user = "root"; |
479 | proxyJump = "mathw0h"; | 501 | proxyCommand = autosshProxy "mathw0h"; |
480 | }; | 502 | }; |
481 | "dhcp02" = | 503 | "dhcp02" = |
482 | { hostname = "dhcp02.mathinst.loc"; | 504 | { hostname = "dhcp02.mathinst.loc"; |
483 | user = "root"; | 505 | user = "root"; |
484 | proxyJump = "mathw0h"; | 506 | proxyCommand = autosshProxy "mathw0h"; |
485 | }; | 507 | }; |
486 | "cc-gpu-l01" = | 508 | "cc-gpu-l01" = |
487 | { hostname = "cc-gpu-l01.mathinst.loc"; | 509 | { hostname = "cc-gpu-l01.mathinst.loc"; |
@@ -546,7 +568,7 @@ | |||
546 | user = "root"; | 568 | user = "root"; |
547 | }; | 569 | }; |
548 | "nas*" = | 570 | "nas*" = |
549 | { proxyJump = "mathw0e"; | 571 | { proxyCommand = autosshProxy "mathw0e"; |
550 | user = "admin"; | 572 | user = "admin"; |
551 | extraOptions = { | 573 | extraOptions = { |
552 | PasswordAuthentication = "yes"; | 574 | PasswordAuthentication = "yes"; |
@@ -554,9 +576,4 @@ | |||
554 | HostKeyAlgorithms = "+ecdsa-sha2-nistp256"; | 576 | HostKeyAlgorithms = "+ecdsa-sha2-nistp256"; |
555 | }; | 577 | }; |
556 | }; | 578 | }; |
557 | "game01" = | ||
558 | { hostname = "game01.yggdrasil.li"; | ||
559 | user = "factorio"; | ||
560 | identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil"; | ||
561 | }; | ||
562 | } | 579 | } |
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 c8400c28..90cccc58 100644 --- a/accounts/gkleen@sif/systemd.nix +++ b/accounts/gkleen@sif/systemd.nix | |||
@@ -6,7 +6,7 @@ let | |||
6 | cfg = config.home-manager.users.${userName}; | 6 | cfg = config.home-manager.users.${userName}; |
7 | 7 | ||
8 | autossh-socks-script = pkgs.writeScript "autossh" '' | 8 | autossh-socks-script = pkgs.writeScript "autossh" '' |
9 | #!${pkgs.zsh}/bin/zsh -xe | 9 | #!${lib.getExe pkgs.zsh} -xe |
10 | 10 | ||
11 | host="''${1%:*}" | 11 | host="''${1%:*}" |
12 | port="''${1#*:}" | 12 | port="''${1#*:}" |
@@ -15,31 +15,29 @@ let | |||
15 | cmd=() | 15 | cmd=() |
16 | 16 | ||
17 | if [[ -n "''${SSHPASS_SECRET}" ]]; then | 17 | if [[ -n "''${SSHPASS_SECRET}" ]]; then |
18 | cmd+=(${pkgs.sshpassSecret}/bin/sshpass-secret) | 18 | cmd+=(${lib.getExe' pkgs.sshpassSecret "sshpass-secret"}) |
19 | cmd+=("''${(@s/:/)SSHPASS_SECRET}") | 19 | cmd+=("''${(@s/:/)SSHPASS_SECRET}") |
20 | cmd+=(--) | 20 | cmd+=(--) |
21 | fi | 21 | fi |
22 | 22 | ||
23 | cmd+=(${pkgs.openssh}/bin/ssh -vN -D localhost:''${port} "''${host}") | 23 | cmd+=(${lib.getExe' pkgs.openssh "ssh"} -vN -D 127.0.0.1:''${port} "''${host}") |
24 | 24 | ||
25 | ( exec -a "''${cmd[1]}" -- ''${cmd} ) & | 25 | ( exec -a "''${cmd[1]}" -- ''${cmd} ) & |
26 | pid=$! | 26 | pid=$! |
27 | 27 | ||
28 | newpid="" | 28 | newpid="" |
29 | i=200 | 29 | i=200 |
30 | while ! newpid=$(${pkgs.lsof}/bin/lsof -Pi @localhost:"''${port}" -sTCP:LISTEN -t); do | 30 | while ! newpid=$(${lib.getExe pkgs.lsof} -Pi @localhost:"''${port}" -sTCP:LISTEN -t); do |
31 | if ! kill -0 "''${pid}"; then | 31 | if ! kill -0 "''${pid}"; then |
32 | wait "''${pid}" | 32 | wait "''${pid}" |
33 | exit $? | 33 | exit $? |
34 | fi | 34 | fi |
35 | [[ "''${i}" -gt 0 ]] || exit 1 | 35 | [[ "''${i}" -gt 0 ]] || exit 1 |
36 | i=$((''${i} - 1)) | 36 | i=$((''${i} - 1)) |
37 | ${pkgs.coreutils}/bin/sleep 0.1 | 37 | ${lib.getExe' pkgs.coreutils "sleep"} 0.1 |
38 | done | 38 | done |
39 | 39 | ||
40 | ${config.systemd.package}/bin/systemd-notify --ready | 40 | ${lib.getExe' config.systemd.package "systemd-notify"} --pid=''${newpid} --ready |
41 | |||
42 | wait "''${pid}" "''${newpid}" | ||
43 | ''; | 41 | ''; |
44 | in { | 42 | in { |
45 | tmpfiles.rules = [ | 43 | tmpfiles.rules = [ |
@@ -48,11 +46,11 @@ in { | |||
48 | ]; | 46 | ]; |
49 | 47 | ||
50 | services = { | 48 | services = { |
51 | sync-keepass = { | 49 | "sync-keepass@" = { |
52 | Service = { | 50 | Service = { |
53 | Type = "oneshot"; | 51 | Type = "oneshot"; |
54 | WorkingDirectory = "~"; | 52 | WorkingDirectory = "~"; |
55 | ExecStart = toString (pkgs.writers.writePython3 "sync-keepass" { | 53 | ExecStart = "${pkgs.writers.writePython3 "sync-keepass" { |
56 | libraries = with pkgs.python3Packages; [ python-dateutil ]; | 54 | libraries = with pkgs.python3Packages; [ python-dateutil ]; |
57 | } '' | 55 | } '' |
58 | import json | 56 | import json |
@@ -61,13 +59,13 @@ in { | |||
61 | from datetime import datetime | 59 | from datetime import datetime |
62 | from dateutil.tz import tzlocal | 60 | from dateutil.tz import tzlocal |
63 | from dateutil.parser import isoparse | 61 | from dateutil.parser import isoparse |
64 | from sys import stderr | 62 | from sys import stderr, argv |
65 | 63 | ||
66 | 64 | ||
67 | remote_fs = 'surtr' | 65 | remote_fs = 'surtr' if argv[1] == 'store.kdbx' else 'mathcloud' |
68 | remote_file = 'store.kdbx' | 66 | remote_file = argv[1] |
69 | target_file = expanduser('~/store.kdbx') | 67 | target_file = expanduser(f'~/{argv[1]}') |
70 | meta_file = expanduser('~/.store.kdbx.json') | 68 | meta_file = expanduser(f'~/.{argv[1]}.json') |
71 | 69 | ||
72 | upload_time = None | 70 | upload_time = None |
73 | our_last_upload_time = None | 71 | our_last_upload_time = None |
@@ -117,22 +115,14 @@ in { | |||
117 | do_upload() | 115 | do_upload() |
118 | elif upload_time is not None and (mod_time is None or upload_time > mod_time) and (our_last_upload_time is None or upload_time > our_last_upload_time): # noqa: E501 | 116 | elif upload_time is not None and (mod_time is None or upload_time > mod_time) and (our_last_upload_time is None or upload_time > our_last_upload_time): # noqa: E501 |
119 | do_download() | 117 | do_download() |
120 | ''); | 118 | ''} \"%I\""; |
121 | Environment = [ "RCLONE_PASSWORD_COMMAND=\"${pkgs.coreutils}/bin/cat ${config.sops.secrets.gkleen-rclone.path}\"" "PATH=${pkgs.rclone}/bin" ]; | 119 | Environment = [ "RCLONE_PASSWORD_COMMAND=\"${pkgs.coreutils}/bin/cat ${config.sops.secrets.gkleen-rclone.path}\"" "PATH=${pkgs.rclone}/bin" ]; |
122 | }; | 120 | }; |
123 | }; | 121 | }; |
124 | emacs = { | 122 | emacs = { |
125 | Unit = { | 123 | Unit = { |
126 | After = ["graphical-session-pre.target"]; | 124 | After = [ "graphical-session.target" ]; |
127 | }; | 125 | BindsTo = [ "graphical-session.target" ]; |
128 | }; | ||
129 | dunst = { | ||
130 | Service = { | ||
131 | ExecStart = lib.mkForce "${cfg.services.dunst.package}/bin/dunst"; | ||
132 | Restart = "always"; | ||
133 | }; | ||
134 | Install = { | ||
135 | WantedBy = ["graphical-session.target"]; | ||
136 | }; | 126 | }; |
137 | }; | 127 | }; |
138 | keepassxc = { | 128 | keepassxc = { |
@@ -144,8 +134,8 @@ in { | |||
144 | Environment = [ "QT_QPA_PLATFORM=wayland" ]; | 134 | Environment = [ "QT_QPA_PLATFORM=wayland" ]; |
145 | }; | 135 | }; |
146 | Unit = { | 136 | Unit = { |
147 | Requires = ["graphical-session-pre.target"]; | 137 | After = [ "graphical-session.target" ]; |
148 | After = ["graphical-session-pre.target"]; | 138 | BindsTo = [ "graphical-session.target" ]; |
149 | }; | 139 | }; |
150 | }; | 140 | }; |
151 | mpris-proxy = { | 141 | mpris-proxy = { |
@@ -154,7 +144,7 @@ in { | |||
154 | Service.ExecStart = "${pkgs.bluez}/bin/mpris-proxy"; | 144 | Service.ExecStart = "${pkgs.bluez}/bin/mpris-proxy"; |
155 | Install.WantedBy = [ "default.target" ]; | 145 | Install.WantedBy = [ "default.target" ]; |
156 | }; | 146 | }; |
157 | "autossh-socks@proxy.mathw0h:8119" = { | 147 | "autossh-socks@proxy.ssh.math.lmu.de:8119" = { |
158 | Service = { | 148 | Service = { |
159 | Type = "notify"; | 149 | Type = "notify"; |
160 | NotifyAccess = "all"; | 150 | NotifyAccess = "all"; |
@@ -162,7 +152,7 @@ in { | |||
162 | Restart = "always"; | 152 | Restart = "always"; |
163 | RestartSec = "23s"; | 153 | RestartSec = "23s"; |
164 | ExecStart = "${autossh-socks-script} \"%I\""; | 154 | ExecStart = "${autossh-socks-script} \"%I\""; |
165 | Environment = [ "SSHPASS_SECRET=gkleen@mathw0g.math.lmu.de" ]; | 155 | Environment = [ "SSHPASS_SECRET=gkleen@ssh.math.lmu.de" ]; |
166 | }; | 156 | }; |
167 | Unit = { | 157 | Unit = { |
168 | StopWhenUnneeded = true; | 158 | StopWhenUnneeded = true; |
@@ -183,6 +173,38 @@ in { | |||
183 | StopWhenUnneeded = true; | 173 | StopWhenUnneeded = true; |
184 | }; | 174 | }; |
185 | }; | 175 | }; |
176 | "autossh-socks@proxy.mathw0h:8123" = { | ||
177 | Service = { | ||
178 | Type = "notify"; | ||
179 | NotifyAccess = "all"; | ||
180 | WorkingDirectory = "~"; | ||
181 | Restart = "always"; | ||
182 | RestartSec = "23s"; | ||
183 | ExecStart = "${autossh-socks-script} \"%I\""; | ||
184 | Environment = [ "SSHPASS_SECRET=gkleen@mathw0h.mathinst.loc" ]; | ||
185 | }; | ||
186 | Unit = { | ||
187 | StopWhenUnneeded = true; | ||
188 | StartLimitInterval = "180s"; | ||
189 | StartLimitBurst = 7; | ||
190 | }; | ||
191 | }; | ||
192 | "autossh-socks@proxy.mathw0e:8125" = { | ||
193 | Service = { | ||
194 | Type = "notify"; | ||
195 | NotifyAccess = "all"; | ||
196 | WorkingDirectory = "~"; | ||
197 | Restart = "always"; | ||
198 | RestartSec = "23s"; | ||
199 | ExecStart = "${autossh-socks-script} \"%I\""; | ||
200 | Environment = [ "SSHPASS_SECRET=gkleen@mathw0e.mathinst.loc" ]; | ||
201 | }; | ||
202 | Unit = { | ||
203 | StopWhenUnneeded = true; | ||
204 | StartLimitInterval = "180s"; | ||
205 | StartLimitBurst = 7; | ||
206 | }; | ||
207 | }; | ||
186 | swayidle = { | 208 | swayidle = { |
187 | Service = { | 209 | Service = { |
188 | RuntimeDirectory = "swayidle"; | 210 | RuntimeDirectory = "swayidle"; |
@@ -193,8 +215,8 @@ in { | |||
193 | WantedBy = ["graphical-session.target"]; | 215 | WantedBy = ["graphical-session.target"]; |
194 | }; | 216 | }; |
195 | Unit = { | 217 | Unit = { |
196 | Requires = ["graphical-session-pre.target"]; | 218 | After = [ "graphical-session.target" ]; |
197 | After = ["graphical-session-pre.target"]; | 219 | PartOf = [ "graphical-session.target" ]; |
198 | }; | 220 | }; |
199 | Service = { | 221 | Service = { |
200 | ExecStart = lib.getExe pkgs.psi-notify; | 222 | ExecStart = lib.getExe pkgs.psi-notify; |
@@ -204,23 +226,10 @@ in { | |||
204 | WatchdogSec = "2s"; | 226 | WatchdogSec = "2s"; |
205 | }; | 227 | }; |
206 | }; | 228 | }; |
207 | polkit-gnome-authentication-agent-1 = { | ||
208 | Install = { | ||
209 | WantedBy = ["graphical-session.target"]; | ||
210 | }; | ||
211 | Unit = { | ||
212 | PartOf = ["graphical-session.target"]; | ||
213 | Requires = ["graphical-session-pre.target"]; | ||
214 | After = ["graphical-session-pre.target"]; | ||
215 | }; | ||
216 | Service = { | ||
217 | ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1"; | ||
218 | Restart = "on-failure"; | ||
219 | }; | ||
220 | }; | ||
221 | gtklock = { | 229 | gtklock = { |
222 | Unit = { | 230 | Unit = { |
223 | Requisite = ["graphical-session.target"]; | 231 | Requisite = ["graphical-session.target"]; |
232 | After = [ "graphical-session.target" ]; | ||
224 | PartOf = ["graphical-session.target"]; | 233 | PartOf = ["graphical-session.target"]; |
225 | }; | 234 | }; |
226 | Service = { | 235 | Service = { |
@@ -228,53 +237,55 @@ in { | |||
228 | RuntimeDirectory = "gtklock"; | 237 | RuntimeDirectory = "gtklock"; |
229 | CacheDirectory = "gtklock"; | 238 | CacheDirectory = "gtklock"; |
230 | ExecStartPre = [ | 239 | ExecStartPre = [ |
231 | "${pkgs.libsForQt5.qt5.qttools.bin}/bin/qdbus org.keepassxc.KeePassXC.MainWindow /keepassxc org.keepassxc.KeePassXC.MainWindow.lockAllDatabases" | 240 | "-${lib.getExe' pkgs.libsForQt5.qt5.qttools.bin "qdbus"} org.keepassxc.KeePassXC.MainWindow /keepassxc org.keepassxc.KeePassXC.MainWindow.lockAllDatabases" |
232 | "${config.systemd.package}/bin/systemctl --user stop gpg-agent.service" | 241 | "-${lib.getExe' config.systemd.package "systemctl"} --user stop gpg-agent.service" |
233 | (pkgs.writeShellScript "generate-css" '' | 242 | "-${lib.getExe pkgs.playerctl} -a pause" |
234 | set -x | 243 | "-${lib.getExe (pkgs.writeShellApplication { |
235 | export PATH="${lib.makeBinPath [cfg.programs.wpaperd.package pkgs.jq pkgs.coreutils pkgs.imagemagick pkgs.findutils]}:$PATH" | 244 | name = "generate-css"; |
236 | 245 | runtimeInputs = with pkgs; [cfg.services.wpaperd.package jq coreutils imagemagick findutils]; | |
237 | declare -A monitors | 246 | text = '' |
238 | monitors=() | 247 | declare -A monitors |
239 | while IFS= read -r entry; do | 248 | monitors=() |
240 | path=$(jq -r ".path" <<<"$entry") | 249 | while IFS= read -r entry; do |
241 | [[ -z "$path" || ! -f "$path" ]] && continue | 250 | path=$(jq -r ".path" <<<"$entry") |
242 | blurred_path="$CACHE_DIRECTORY"/"$(b2sum -l 128 <<<"$path" | cut -d' ' -f1)"."''${path##*.}" | 251 | [[ -z "$path" || ! -f "$path" ]] && continue |
243 | monitor=$(jq -r ".display" <<<"$entry") | 252 | blurred_path="$CACHE_DIRECTORY"/"$(b2sum -l 128 <<<"$path" | cut -d' ' -f1)"."''${path##*.}" |
244 | if [[ ! -f "$blurred_path" ]]; then | 253 | monitor=$(jq -r ".display" <<<"$entry") |
245 | mkdir -p "$(dirname "$blurred_path")" | 254 | if [[ ! -f "$blurred_path" ]]; then |
246 | magick "$path" -filter Gaussian -resize 6.25% -define filter:sigma=2.5 -resize 1600% "$blurred_path" & | 255 | mkdir -p "$(dirname "$blurred_path")" |
247 | fi | 256 | magick "$path" -filter Gaussian -resize 6.25% -define filter:sigma=2.5 -resize 1600% "$blurred_path" & |
248 | monitors+=([$monitor]="$blurred_path") | 257 | fi |
249 | done < <(wpaperctl all-wallpapers -j | jq -c ".[]") | 258 | monitors+=([$monitor]="$blurred_path") |
250 | wait | 259 | done < <(wpaperctl all-wallpapers -j | jq -c ".[]") |
260 | # wait | ||
251 | 261 | ||
252 | cp --no-preserve=mode ${pkgs.writeText "gtklock.css" '' | 262 | cp --no-preserve=mode ${pkgs.writeText "gtklock.css" '' |
253 | #window-box { | 263 | #window-box { |
254 | padding: 64px; | 264 | padding: 64px; |
255 | /* border: 1px solid black; */ | 265 | /* border: 1px solid black; */ |
256 | border-radius: 4px; | 266 | border-radius: 4px; |
257 | box-shadow: rgba(0, 0, 0, 0.8) 0px 4px 12px; | 267 | box-shadow: rgba(0, 0, 0, 0.8) 0px 4px 12px; |
258 | /* background-color: white; */ | 268 | /* background-color: white; */ |
259 | background-color: rgba(0, 0, 0, 0.5); | 269 | background-color: rgba(0, 0, 0, 0.5); |
270 | } | ||
271 | ''} "$RUNTIME_DIRECTORY"/style.css | ||
272 | for monitor in "''${!monitors[@]}"; do | ||
273 | cat >>"$RUNTIME_DIRECTORY"/style.css <<EOF | ||
274 | window#''${monitor} { | ||
275 | background-image: url("''${monitors[$monitor]}"); | ||
276 | background-repeat: no-repeat; | ||
277 | background-size: 100% 100%; | ||
278 | background-origin: content-box; | ||
260 | } | 279 | } |
261 | ''} "$RUNTIME_DIRECTORY"/style.css | 280 | EOF |
262 | for monitor in "''${!monitors[@]}"; do | 281 | done |
263 | cat >>"$RUNTIME_DIRECTORY"/style.css <<EOF | 282 | ''; |
264 | window#''${monitor} { | 283 | })}" |
265 | background-image: url("''${monitors[$monitor]}"); | ||
266 | background-repeat: no-repeat; | ||
267 | background-size: 100% 100%; | ||
268 | background-origin: content-box; | ||
269 | } | ||
270 | EOF | ||
271 | done | ||
272 | '') | ||
273 | ]; | 284 | ]; |
274 | NotifyAccess = "all"; | 285 | NotifyAccess = "all"; |
275 | ExecStart = ''${lib.getExe pkgs.gtklock} -s "''${RUNTIME_DIRECTORY}/style.css" -L ${pkgs.writeShellScript "after-lock" '' | 286 | ExecStart = ''${lib.getExe pkgs.gtklock} -s "''${RUNTIME_DIRECTORY}/style.css" -L ${pkgs.writeShellScript "after-lock" '' |
276 | ${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms off | 287 | ${lib.getExe cfg.programs.niri.package} msg action power-off-monitors |
277 | ${config.systemd.package}/bin/systemd-notify --ready | 288 | ${lib.getExe' config.systemd.package "systemd-notify"} --ready |
278 | ''}''; | 289 | ''}''; |
279 | }; | 290 | }; |
280 | }; | 291 | }; |
@@ -322,30 +333,60 @@ in { | |||
322 | ExecStopPost = "${pkgs.coreutils}/bin/rm -rfv \"$CACHE_DIRECTORY\""; | 333 | ExecStopPost = "${pkgs.coreutils}/bin/rm -rfv \"$CACHE_DIRECTORY\""; |
323 | }; | 334 | }; |
324 | }; | 335 | }; |
325 | wpaperd = { | 336 | # wpaperd = { |
326 | Install = { | 337 | # Install = { |
327 | WantedBy = ["graphical-session.target"]; | 338 | # WantedBy = ["graphical-session.target"]; |
339 | # }; | ||
340 | # Unit = { | ||
341 | # After = [ "graphical-session.target" ]; | ||
342 | # PartOf = [ "graphical-session.target" ]; | ||
343 | # }; | ||
344 | # Service = { | ||
345 | # ExecStart = lib.getExe cfg.services.wpaperd.package; | ||
346 | # Type = "simple"; | ||
347 | # Restart = "always"; | ||
348 | # RestartSec = "2s"; | ||
349 | # }; | ||
350 | # }; | ||
351 | xembed-sni-proxy = { | ||
352 | Unit = { | ||
353 | PartOf = lib.mkForce ["tray.target"]; | ||
354 | BindsTo = ["xwayland-satellite.service"]; | ||
355 | After = ["xwayland-satellite.service"]; | ||
328 | }; | 356 | }; |
357 | }; | ||
358 | poweralertd = { | ||
329 | Unit = { | 359 | Unit = { |
330 | BindsTo = ["graphical-session-pre.target"]; | 360 | After = ["graphical-session.target"]; |
331 | After = ["graphical-session-pre.target"]; | ||
332 | }; | 361 | }; |
333 | Service = { | 362 | }; |
334 | ExecStart = lib.getExe cfg.programs.wpaperd.package; | 363 | network-manager-applet = { |
335 | Type = "simple"; | 364 | Unit = { |
336 | Restart = "always"; | 365 | PartOf = lib.mkForce ["tray.target"]; |
337 | RestartSec = "2s"; | 366 | }; |
367 | }; | ||
368 | udiskie = { | ||
369 | Unit = { | ||
370 | PartOf = lib.mkForce ["tray.target"]; | ||
371 | }; | ||
372 | }; | ||
373 | blueman-applet = { | ||
374 | Unit = { | ||
375 | PartOf = lib.mkForce ["tray.target"]; | ||
376 | }; | ||
377 | Install = { | ||
378 | WantedBy = lib.mkForce ["tray.target"]; | ||
338 | }; | 379 | }; |
339 | }; | 380 | }; |
340 | } // listToAttrs (map ({host, port}: nameValuePair "proxy-to-autossh-socks@${toString port}" { | 381 | } // listToAttrs (map ({host, port}: nameValuePair "proxy-to-autossh-socks@${toString port}" { |
341 | Unit = { | 382 | Unit = { |
342 | Requires = ["autossh-socks@${host}:${toString (port + 1)}.service" "proxy-to-autossh-socks@${toString port}.socket"]; | 383 | BindsTo = ["autossh-socks@${host}:${toString (port + 1)}.service" "proxy-to-autossh-socks@${toString port}.socket"]; |
343 | After = ["autossh-socks@${host}:${toString (port + 1)}.service" "proxy-to-autossh-socks@${toString port}.socket"]; | 384 | After = ["autossh-socks@${host}:${toString (port + 1)}.service" "proxy-to-autossh-socks@${toString port}.socket"]; |
344 | }; | 385 | }; |
345 | Service = { | 386 | Service = { |
346 | ExecStart = "${config.systemd.package}/lib/systemd/systemd-socket-proxyd --exit-idle-time=10s localhost:${toString (port + 1)}"; | 387 | ExecStart = "${config.systemd.package}/lib/systemd/systemd-socket-proxyd --exit-idle-time=60s 127.0.0.1:${toString (port + 1)}"; |
347 | }; | 388 | }; |
348 | }) [{ host = "proxy.mathw0h"; port = 8118; } { host = "proxy.vidhar"; port = 8120; }]); | 389 | }) [{ host = "proxy.ssh.math.lmu.de"; port = 8118; } { host = "proxy.vidhar"; port = 8120; } { host = "proxy.mathw0h"; port = 8122; } { host = "proxy.mathw0e"; port = 8124; }]); |
349 | sockets = listToAttrs (map (port: nameValuePair "proxy-to-autossh-socks@${toString port}" { | 390 | sockets = listToAttrs (map (port: nameValuePair "proxy-to-autossh-socks@${toString port}" { |
350 | Socket = { | 391 | Socket = { |
351 | ListenStream = "%I"; | 392 | ListenStream = "%I"; |
@@ -353,7 +394,7 @@ in { | |||
353 | Install = { | 394 | Install = { |
354 | WantedBy = ["default.target"]; | 395 | WantedBy = ["default.target"]; |
355 | }; | 396 | }; |
356 | }) [8118 8120]) // { | 397 | }) [8118 8120 8122 8124]) // { |
357 | "yt-dlp" = { | 398 | "yt-dlp" = { |
358 | Socket = { | 399 | Socket = { |
359 | SocketMode = "0600"; | 400 | SocketMode = "0600"; |
@@ -367,7 +408,7 @@ in { | |||
367 | }; | 408 | }; |
368 | }; | 409 | }; |
369 | timers = { | 410 | timers = { |
370 | sync-keepass = { | 411 | "sync-keepass@store.kdbx" = { |
371 | Timer = { | 412 | Timer = { |
372 | OnActiveSec = "1m"; | 413 | OnActiveSec = "1m"; |
373 | OnUnitActiveSec = "1m"; | 414 | OnUnitActiveSec = "1m"; |
@@ -377,6 +418,16 @@ in { | |||
377 | WantedBy = ["default.target"]; | 418 | WantedBy = ["default.target"]; |
378 | }; | 419 | }; |
379 | }; | 420 | }; |
421 | "sync-keepass@rz.kdbx" = { | ||
422 | Timer = { | ||
423 | OnActiveSec = "1d"; | ||
424 | OnUnitActiveSec = "1d"; | ||
425 | }; | ||
426 | |||
427 | Install = { | ||
428 | WantedBy = ["default.target"]; | ||
429 | }; | ||
430 | }; | ||
380 | }; | 431 | }; |
381 | targets = { | 432 | targets = { |
382 | graphical-session = { | 433 | graphical-session = { |
@@ -387,6 +438,9 @@ in { | |||
387 | }; | 438 | }; |
388 | tray = { | 439 | tray = { |
389 | Unit = { | 440 | Unit = { |
441 | PartOf = [ "graphical-session.target" ]; | ||
442 | Requires = [ "waybar.service" ]; | ||
443 | After = [ "graphical-session.target" "waybar.service" ]; | ||
390 | Wants = ["blueman-applet.service" "udiskie.service" "network-manager-applet.service"]; | 444 | Wants = ["blueman-applet.service" "udiskie.service" "network-manager-applet.service"]; |
391 | }; | 445 | }; |
392 | }; | 446 | }; |
diff --git a/accounts/gkleen@sif/taffybar/default.nix b/accounts/gkleen@sif/taffybar/default.nix deleted file mode 100644 index 98366d8f..00000000 --- a/accounts/gkleen@sif/taffybar/default.nix +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | { haskellPackages ? (import <nixpkgs> {}).haskellPackages }: | ||
2 | haskellPackages.callCabal2nix "gkleen-sif-taffybar" ./. {} | ||
diff --git a/accounts/gkleen@sif/taffybar/gkleen-sif-taffybar.cabal b/accounts/gkleen@sif/taffybar/gkleen-sif-taffybar.cabal deleted file mode 100644 index e32cb473..00000000 --- a/accounts/gkleen@sif/taffybar/gkleen-sif-taffybar.cabal +++ /dev/null | |||
@@ -1,32 +0,0 @@ | |||
1 | name: gkleen-sif-taffybar | ||
2 | version: 0.0.0 | ||
3 | build-type: Simple | ||
4 | cabal-version: >=1.10 | ||
5 | |||
6 | data-files: taffybar.css | ||
7 | |||
8 | executable taffybar | ||
9 | hs-source-dirs: src | ||
10 | main-is: taffybar.hs | ||
11 | ghc-options: -threaded -rtsopts -with-rtsopts=-N -O2 -Wall | ||
12 | build-depends: base | ||
13 | , containers | ||
14 | , directory | ||
15 | , filepath | ||
16 | , gtk3 | ||
17 | , taffybar | ||
18 | , X11>=1.8 | ||
19 | , transformers | ||
20 | , gi-gtk | ||
21 | , time, time-locale-compat | ||
22 | , text | ||
23 | , HStringTemplate | ||
24 | , gtk-sni-tray | ||
25 | , hslogger | ||
26 | other-modules: Paths_gkleen_sif_taffybar | ||
27 | , System.Taffybar.Widget.Clock | ||
28 | , System.Taffybar.Widget.TooltipBattery | ||
29 | default-language: Haskell2010 | ||
30 | default-extensions: ScopedTypeVariables | ||
31 | , LambdaCase | ||
32 | , NamedFieldPuns \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/Clock.hs b/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/Clock.hs deleted file mode 100644 index e8dc480f..00000000 --- a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/Clock.hs +++ /dev/null | |||
@@ -1,111 +0,0 @@ | |||
1 | {-# LANGUAGE OverloadedStrings #-} | ||
2 | module System.Taffybar.Widget.Clock | ||
3 | ( textClockNew | ||
4 | , textClockNewWith | ||
5 | , defaultClockConfig | ||
6 | , ClockConfig(..) | ||
7 | , ClockUpdateStrategy(..) | ||
8 | ) where | ||
9 | |||
10 | import Control.Monad.IO.Class | ||
11 | import Data.Maybe | ||
12 | import qualified Data.Text as T | ||
13 | import qualified Data.Time.Clock as Clock | ||
14 | import Data.Time.Format | ||
15 | import Data.Time.LocalTime | ||
16 | import qualified Data.Time.Locale.Compat as L | ||
17 | import GI.Gtk | ||
18 | import System.Taffybar.Widget.Generic.PollingLabel | ||
19 | |||
20 | type ClockFormat = L.TimeLocale -> ZonedTime -> T.Text | ||
21 | |||
22 | -- | Create the widget. I recommend passing @Nothing@ for the TimeLocale | ||
23 | -- parameter. The format string can include Pango markup | ||
24 | -- (<http://developer.gnome.org/pango/stable/PangoMarkupFormat.html>). | ||
25 | textClockNew :: | ||
26 | MonadIO m => Maybe L.TimeLocale -> ClockFormat -> Double -> m GI.Gtk.Widget | ||
27 | textClockNew userLocale format interval = | ||
28 | textClockNewWith cfg | ||
29 | where | ||
30 | cfg = defaultClockConfig { clockTimeLocale = userLocale | ||
31 | , clockFormat = format | ||
32 | , clockUpdateStrategy = ConstantInterval interval | ||
33 | } | ||
34 | |||
35 | data ClockUpdateStrategy | ||
36 | = ConstantInterval Double | ||
37 | | RoundedTargetInterval Int Double | ||
38 | deriving (Eq, Ord, Show) | ||
39 | |||
40 | data ClockConfig = ClockConfig | ||
41 | { clockTimeZone :: Maybe TimeZone | ||
42 | , clockTimeLocale :: Maybe L.TimeLocale | ||
43 | , clockFormat :: ClockFormat | ||
44 | , clockUpdateStrategy :: ClockUpdateStrategy | ||
45 | } | ||
46 | |||
47 | -- | A clock configuration that defaults to the current locale | ||
48 | defaultClockConfig :: ClockConfig | ||
49 | defaultClockConfig = ClockConfig | ||
50 | { clockTimeZone = Nothing | ||
51 | , clockTimeLocale = Nothing | ||
52 | , clockFormat = \locale zonedTime -> T.pack $ formatTime locale "%a %b %_d %r" zonedTime | ||
53 | , clockUpdateStrategy = RoundedTargetInterval 5 0.0 | ||
54 | } | ||
55 | |||
56 | systemGetTZ :: IO TimeZone | ||
57 | systemGetTZ = getCurrentTimeZone | ||
58 | |||
59 | -- | A configurable text-based clock widget. It currently allows for | ||
60 | -- a configurable time zone through the 'ClockConfig'. | ||
61 | -- | ||
62 | -- See also 'textClockNew'. | ||
63 | textClockNewWith :: MonadIO m => ClockConfig -> m Widget | ||
64 | textClockNewWith ClockConfig | ||
65 | { clockTimeZone = userZone | ||
66 | , clockTimeLocale = userLocale | ||
67 | , clockFormat = format | ||
68 | , clockUpdateStrategy = updateStrategy | ||
69 | } = liftIO $ do | ||
70 | let getTZ = maybe systemGetTZ return userZone | ||
71 | locale = fromMaybe L.defaultTimeLocale userLocale | ||
72 | |||
73 | let getUserZonedTime = | ||
74 | utcToZonedTime <$> getTZ <*> Clock.getCurrentTime | ||
75 | |||
76 | doTimeFormat = format locale | ||
77 | |||
78 | getRoundedTimeAndNextTarget = do | ||
79 | zonedTime <- getUserZonedTime | ||
80 | return $ case updateStrategy of | ||
81 | ConstantInterval interval -> | ||
82 | (doTimeFormat zonedTime, Nothing, interval) | ||
83 | RoundedTargetInterval roundSeconds offset -> | ||
84 | let roundSecondsDiffTime = fromIntegral roundSeconds | ||
85 | addTheRound = addLocalTime roundSecondsDiffTime | ||
86 | localTime = zonedTimeToLocalTime zonedTime | ||
87 | ourLocalTimeOfDay = localTimeOfDay localTime | ||
88 | seconds = round $ todSec ourLocalTimeOfDay | ||
89 | secondsFactor = seconds `div` roundSeconds | ||
90 | displaySeconds = secondsFactor * roundSeconds | ||
91 | baseLocalTimeOfDay = | ||
92 | ourLocalTimeOfDay { todSec = fromIntegral displaySeconds } | ||
93 | ourLocalTime = | ||
94 | localTime { localTimeOfDay = baseLocalTimeOfDay } | ||
95 | roundedLocalTime = | ||
96 | if seconds `mod` roundSeconds > roundSeconds `div` 2 | ||
97 | then addTheRound ourLocalTime | ||
98 | else ourLocalTime | ||
99 | roundedZonedTime = | ||
100 | zonedTime { zonedTimeToLocalTime = roundedLocalTime } | ||
101 | nextTarget = addTheRound ourLocalTime | ||
102 | amountToWait = realToFrac $ diffLocalTime nextTarget localTime | ||
103 | in (doTimeFormat roundedZonedTime, Nothing, amountToWait - offset) | ||
104 | |||
105 | label <- pollingLabelWithVariableDelay getRoundedTimeAndNextTarget | ||
106 | ebox <- eventBoxNew | ||
107 | containerAdd ebox label | ||
108 | eventBoxSetVisibleWindow ebox False | ||
109 | widgetShowAll ebox | ||
110 | toWidget ebox | ||
111 | |||
diff --git a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/TooltipBattery.hs b/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/TooltipBattery.hs deleted file mode 100644 index 9dc52774..00000000 --- a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/TooltipBattery.hs +++ /dev/null | |||
@@ -1,101 +0,0 @@ | |||
1 | {-# LANGUAGE OverloadedStrings #-} | ||
2 | {-# LANGUAGE ScopedTypeVariables #-} | ||
3 | module System.Taffybar.Widget.TooltipBattery ( batteryIconTooltipNew ) where | ||
4 | |||
5 | import Control.Applicative | ||
6 | import Control.Monad | ||
7 | import Control.Monad.IO.Class | ||
8 | import Control.Monad.Trans.Reader | ||
9 | import Data.Int (Int64) | ||
10 | import qualified Data.Text as T | ||
11 | import GI.Gtk | ||
12 | import Prelude | ||
13 | import StatusNotifier.Tray (scalePixbufToSize) | ||
14 | import System.Taffybar.Context | ||
15 | import System.Taffybar.Information.Battery | ||
16 | import System.Taffybar.Util | ||
17 | import System.Taffybar.Widget.Generic.AutoSizeImage | ||
18 | import System.Taffybar.Widget.Generic.ChannelWidget | ||
19 | import Text.Printf | ||
20 | import Text.StringTemplate | ||
21 | import Data.Function ((&)) | ||
22 | |||
23 | -- | Just the battery info that will be used for display (this makes combining | ||
24 | -- several easier). | ||
25 | data BatteryWidgetInfo = BWI | ||
26 | { seconds :: Maybe Int64 | ||
27 | , percent :: Double | ||
28 | , status :: String | ||
29 | , rate :: Maybe Double | ||
30 | } deriving (Eq, Show) | ||
31 | |||
32 | -- | Format a duration expressed as seconds to hours and minutes | ||
33 | formatDuration :: Int64 -> String | ||
34 | formatDuration secs = let minutes, hours, minutes' :: Int64 | ||
35 | minutes = secs `div` 60 | ||
36 | (hours, minutes') = minutes `divMod` 60 | ||
37 | in printf "%02d:%02d" hours minutes' | ||
38 | |||
39 | getBatteryWidgetInfo :: BatteryInfo -> BatteryWidgetInfo | ||
40 | getBatteryWidgetInfo info = | ||
41 | let battPctNum :: Double | ||
42 | battPctNum = batteryPercentage info | ||
43 | battTime :: Maybe Int64 | ||
44 | battTime = | ||
45 | case batteryState info of | ||
46 | BatteryStateCharging -> Just $ batteryTimeToFull info | ||
47 | BatteryStateDischarging -> Just $ batteryTimeToEmpty info | ||
48 | _ -> Nothing | ||
49 | battStatus :: String | ||
50 | battStatus = | ||
51 | case batteryState info of | ||
52 | BatteryStateCharging -> "↑" | ||
53 | BatteryStateDischarging -> "↓" | ||
54 | BatteryStateEmpty -> "⤓" | ||
55 | BatteryStateFullyCharged -> "⤒" | ||
56 | _ -> "?" | ||
57 | battRate :: Maybe Double | ||
58 | battRate | rawRate < 0.1 = Nothing | ||
59 | | otherwise = Just rawRate | ||
60 | where rawRate = batteryEnergyRate info | ||
61 | in BWI{ seconds = battTime, percent = battPctNum, status = battStatus, rate = battRate } | ||
62 | |||
63 | -- | Given (maybe summarized) battery info and format: provides the string to display | ||
64 | formatBattInfo :: BatteryWidgetInfo -> String -> T.Text | ||
65 | formatBattInfo info fmt = | ||
66 | let tpl = newSTMP fmt | ||
67 | tpl' = tpl | ||
68 | & setManyAttrib [ ("percentage", printf "%.0f" $ percent info) | ||
69 | , ("status", status info) | ||
70 | ] | ||
71 | & setManyAttrib [ ("time", formatDuration <$> seconds info) | ||
72 | , ("rate", printf "%.0f" <$> rate info) | ||
73 | ] | ||
74 | in render tpl' | ||
75 | |||
76 | themeLoadFlags :: [IconLookupFlags] | ||
77 | themeLoadFlags = [IconLookupFlagsGenericFallback, IconLookupFlagsUseBuiltin] | ||
78 | |||
79 | batteryIconTooltipNew :: String -> TaffyIO Widget | ||
80 | batteryIconTooltipNew format = do | ||
81 | DisplayBatteryChanVar (chan, _) <- setupDisplayBatteryChanVar ["IconName", "State", "Percentage", "TimeToFull", "TimeToEmpty", "EnergyRate"] | ||
82 | ctx <- ask | ||
83 | liftIO $ do | ||
84 | image <- imageNew | ||
85 | styleCtx <- widgetGetStyleContext =<< toWidget image | ||
86 | defaultTheme <- iconThemeGetDefault | ||
87 | let getCurrentBatteryIconNameStringTooltip = do | ||
88 | info <- runReaderT getDisplayBatteryInfo ctx | ||
89 | let iconNameString = T.pack $ batteryIconName info | ||
90 | tooltip = formatBattInfo (getBatteryWidgetInfo info) format | ||
91 | return (iconNameString, tooltip) | ||
92 | extractPixbuf info = | ||
93 | fst <$> iconInfoLoadSymbolicForContext info styleCtx | ||
94 | setIconForSize size = do | ||
95 | (name, tooltip) <- getCurrentBatteryIconNameStringTooltip | ||
96 | widgetSetTooltipMarkup image $ Just tooltip | ||
97 | iconThemeLookupIcon defaultTheme name size themeLoadFlags >>= | ||
98 | traverse extractPixbuf >>= | ||
99 | traverse (scalePixbufToSize size OrientationHorizontal) | ||
100 | updateImage <- autoSizeImage image setIconForSize OrientationHorizontal | ||
101 | toWidget =<< channelWidgetNew image chan (const $ postGUIASync updateImage) | ||
diff --git a/accounts/gkleen@sif/taffybar/src/taffybar.hs b/accounts/gkleen@sif/taffybar/src/taffybar.hs deleted file mode 100644 index 67ee942d..00000000 --- a/accounts/gkleen@sif/taffybar/src/taffybar.hs +++ /dev/null | |||
@@ -1,89 +0,0 @@ | |||
1 | {-# LANGUAGE OverloadedStrings #-} | ||
2 | |||
3 | module Main where | ||
4 | |||
5 | import System.Taffybar (startTaffybar) | ||
6 | import System.Taffybar.Context (TaffybarConfig(..)) | ||
7 | import System.Taffybar.Hooks | ||
8 | import System.Taffybar.SimpleConfig hiding (SimpleTaffyConfig(cssPaths)) | ||
9 | import System.Taffybar.Widget | ||
10 | import qualified System.Taffybar.Widget.Clock as MyClock | ||
11 | import System.Taffybar.Widget.TooltipBattery | ||
12 | |||
13 | import Data.Time.Format | ||
14 | import Data.Time.LocalTime | ||
15 | import Data.Time.Calendar.WeekDate | ||
16 | |||
17 | import qualified Data.Text as T | ||
18 | |||
19 | import Control.Exception (SomeException, try) | ||
20 | import Control.Monad.Trans.Reader (mapReaderT) | ||
21 | |||
22 | import Paths_gkleen_sif_taffybar | ||
23 | |||
24 | import System.Log.Logger | ||
25 | |||
26 | |||
27 | main :: IO () | ||
28 | main = do | ||
29 | logger <- getLogger "System.Taffybar" | ||
30 | saveGlobalLogger $ setLevel INFO logger | ||
31 | |||
32 | myCssPath <- getDataFileName "taffybar.css" | ||
33 | startTaffybar taffybarConfig{ cssPaths = pure myCssPath } | ||
34 | |||
35 | |||
36 | taffybarConfig :: TaffybarConfig | ||
37 | taffybarConfig = | ||
38 | let myWorkspacesConfig = | ||
39 | defaultWorkspacesConfig | ||
40 | { maxIcons = Just 0 | ||
41 | , widgetGap = 7 | ||
42 | , showWorkspaceFn = \case | ||
43 | -- Workspace{ workspaceState = Empty } -> False | ||
44 | Workspace{ workspaceName } | workspaceName == "NSP" -> False | ||
45 | _other -> True | ||
46 | , getWindowIconPixbuf = \i d -> either (\(_ :: SomeException) -> Nothing) id <$> mapReaderT try (defaultGetWindowIconPixbuf i d) | ||
47 | , urgentWorkspaceState = True | ||
48 | } | ||
49 | workspaces = workspacesNew myWorkspacesConfig | ||
50 | clock = MyClock.textClockNewWith MyClock.defaultClockConfig | ||
51 | { MyClock.clockUpdateStrategy = MyClock.RoundedTargetInterval 1 0.0 | ||
52 | , MyClock.clockFormat = \tl zt@ZonedTime{ zonedTimeToLocalTime = LocalTime{ localDay } } | ||
53 | -> let date = formatTime tl "%Y-%m-%d" localDay | ||
54 | weekdate = "W" <> show2 woy <> "-" <> show dow | ||
55 | where (_, woy, dow) = toWeekDate localDay | ||
56 | show2 :: Int -> String | ||
57 | show2 x = replicate (2 - length s) '0' ++ s | ||
58 | where s = show x | ||
59 | time = formatTime tl "%H:%M:%S%Ez" zt | ||
60 | in T.intercalate " " $ map T.pack [weekdate, date, time] | ||
61 | } | ||
62 | layout = layoutNew defaultLayoutConfig | ||
63 | windowsW = windowsNew defaultWindowsConfig | ||
64 | { getMenuLabel = truncatedGetMenuLabel 80 | ||
65 | , getActiveLabel = truncatedGetActiveLabel 80 | ||
66 | } | ||
67 | worktime = commandRunnerNew 60 "worktime" [] "worktime" | ||
68 | worktimeToday = commandRunnerNew 60 "worktime" ["today"] "worktime today" | ||
69 | -- See https://github.com/taffybar/gtk-sni-tray#statusnotifierwatcher | ||
70 | -- for a better way to set up the sni tray | ||
71 | -- tray = sniTrayThatStartsWatcherEvenThoughThisIsABadWayToDoIt | ||
72 | tray = sniTrayNew | ||
73 | myConfig = defaultSimpleTaffyConfig | ||
74 | { startWidgets = | ||
75 | workspaces : map (>>= buildContentsBox) [ layout, windowsW ] | ||
76 | , endWidgets = map (>>= buildContentsBox) $ reverse | ||
77 | -- , mpris2New | ||
78 | [ worktime, worktimeToday | ||
79 | , clock | ||
80 | , tray | ||
81 | , batteryIconTooltipNew "$status$ $percentage$%$if(time)$$if(rate)$ ($rate$W $time$)$else$ ($time$)$endif$$elseif(rate)$ ($rate$W)$endif$" | ||
82 | ] | ||
83 | , barPosition = Top | ||
84 | , barPadding = 2 | ||
85 | , barHeight = ExactSize 28 | ||
86 | , widgetSpacing = 10 | ||
87 | } | ||
88 | in withBatteryRefresh $ withLogServer $ | ||
89 | withToggleServer $ toTaffyConfig myConfig | ||
diff --git a/accounts/gkleen@sif/taffybar/taffybar.css b/accounts/gkleen@sif/taffybar/taffybar.css deleted file mode 100644 index 7a297465..00000000 --- a/accounts/gkleen@sif/taffybar/taffybar.css +++ /dev/null | |||
@@ -1,146 +0,0 @@ | |||
1 | @define-color transparent rgba(0.0, 0.0, 0.0, 0.0); | ||
2 | @define-color white #808080; | ||
3 | @define-color gray #202020; | ||
4 | @define-color green #008000; | ||
5 | @define-color yellow #808000; | ||
6 | @define-color blue #000080; | ||
7 | @define-color red #800000; | ||
8 | @define-color black #000000; | ||
9 | /* @define-color taffy-blue #0c7cd5; */ | ||
10 | @define-color taffy-blue @blue; | ||
11 | |||
12 | @define-color active-window-color @white; | ||
13 | @define-color urgent-window-color @taffy-blue; | ||
14 | @define-color font-color @white; | ||
15 | @define-color menu-background-color @black; | ||
16 | @define-color menu-font-color @white; | ||
17 | |||
18 | /* Top level styling */ | ||
19 | |||
20 | .taffy-window * { | ||
21 | /* | ||
22 | This removes any existing styling from UI elements. Taffybar will not | ||
23 | cohere with your gtk theme. | ||
24 | */ | ||
25 | all: unset; | ||
26 | |||
27 | font-family: "Fira Sans", sans-serif; | ||
28 | font-size: 21px; | ||
29 | color: @font-color; | ||
30 | } | ||
31 | |||
32 | .taffy-box { | ||
33 | /* border-radius: 10px; */ | ||
34 | background-color: @black; | ||
35 | } | ||
36 | |||
37 | .inner-pad { | ||
38 | /* padding-bottom: 5px; */ | ||
39 | /* padding-top: 5px; */ | ||
40 | padding-left: 2px; | ||
41 | padding-right: 2px; | ||
42 | } | ||
43 | |||
44 | .contents { | ||
45 | /* padding-bottom: 4px; */ | ||
46 | /* padding-top: 4px; */ | ||
47 | padding-right: 2px; | ||
48 | padding-left: 2px; | ||
49 | transition: background-color .5s; | ||
50 | border-radius: 5px; | ||
51 | } | ||
52 | |||
53 | /* Workspaces styling */ | ||
54 | |||
55 | .workspace-label { | ||
56 | padding-right: 3px; | ||
57 | padding-left: 2px; | ||
58 | font-size: 21px; | ||
59 | } | ||
60 | |||
61 | .workspace-label.active { | ||
62 | color: @green; | ||
63 | } | ||
64 | .workspace-label.visible { | ||
65 | color: @yellow; | ||
66 | } | ||
67 | .workspace-label.empty { | ||
68 | color: @gray; | ||
69 | } | ||
70 | .workspace-label.urgent { | ||
71 | color: @red; | ||
72 | } | ||
73 | |||
74 | .active .contents { | ||
75 | background-color: rgba(0.0, 0.0, 0.0, 0.5); | ||
76 | } | ||
77 | |||
78 | .visible .contents { | ||
79 | background-color: rgba(0.0, 0.0, 0.0, 0.2); | ||
80 | } | ||
81 | |||
82 | .window-icon-container { | ||
83 | transition: opacity .5s, box-shadow .5s; | ||
84 | opacity: 1; | ||
85 | } | ||
86 | |||
87 | /* This gives space for the box-shadow (they look like underlines) that follow. | ||
88 | This will actually affect all widgets, (not just the workspace icons), but | ||
89 | that is what we want since we want the icons to look the same. */ | ||
90 | .auto-size-image, .sni-tray { | ||
91 | padding-top: 3px; | ||
92 | padding-bottom: 3px; | ||
93 | } | ||
94 | |||
95 | .window-icon-container.active { | ||
96 | box-shadow: inset 0 -3px @white; | ||
97 | } | ||
98 | |||
99 | .window-icon-container.urgent { | ||
100 | box-shadow: inset 0 -3px @urgent-window-color; | ||
101 | } | ||
102 | |||
103 | .window-icon-container.inactive .window-icon { | ||
104 | padding: 0px; | ||
105 | } | ||
106 | |||
107 | .window-icon-container.minimized .window-icon { | ||
108 | opacity: .3; | ||
109 | } | ||
110 | |||
111 | .window-icon { | ||
112 | opacity: 1; | ||
113 | transition: opacity .5s; | ||
114 | } | ||
115 | |||
116 | /* Button styling */ | ||
117 | |||
118 | button { | ||
119 | background-color: @transparent; | ||
120 | border-width: 0px; | ||
121 | border-radius: 0px; | ||
122 | } | ||
123 | |||
124 | button:checked, button:hover .Contents:hover { | ||
125 | box-shadow: inset 0 -3px @taffy-blue; | ||
126 | } | ||
127 | |||
128 | /* Menu styling */ | ||
129 | |||
130 | /* The ".taffy-window" prefixed selectors are needed because if they aren't present, | ||
131 | the top level .Taffybar selector takes precedence */ | ||
132 | .taffy-window menuitem *, menuitem * { | ||
133 | color: @menu-font-color; | ||
134 | } | ||
135 | |||
136 | .taffy-window menuitem, menuitem { | ||
137 | background-color: @menu-background-color; | ||
138 | } | ||
139 | |||
140 | .taffy-window menuitem:hover, menuitem:hover { | ||
141 | background-color: @taffy-blue; | ||
142 | } | ||
143 | |||
144 | .taffy-window menuitem:hover > label, menuitem:hover > label { | ||
145 | color: @white; | ||
146 | } | ||
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/utils/sieve-edit.nix b/accounts/gkleen@sif/utils/sieve-edit.nix new file mode 100644 index 00000000..f985a3f6 --- /dev/null +++ b/accounts/gkleen@sif/utils/sieve-edit.nix | |||
@@ -0,0 +1,24 @@ | |||
1 | pkgs@{ lib, resholve, zsh, sieve-connect, sops, ... }: | ||
2 | |||
3 | resholve.writeScriptBin "sieve-edit" { | ||
4 | inputs = with pkgs; [sieve-connect sops]; | ||
5 | interpreter = lib.getExe zsh; | ||
6 | execer = with pkgs; [ | ||
7 | "cannot:${lib.getExe sieve-connect}" | ||
8 | "cannot:${lib.getExe sops}" | ||
9 | ]; | ||
10 | } '' | ||
11 | host=$1; shift | ||
12 | case "$host" in | ||
13 | surtr) | ||
14 | sieve-connect -s surtr.yggdrasil.li -m EXTERNAL --clientkey <(sops decrypt $HOME/projects/machines/hosts/surtr/email/ca/gkleen@sif.key) --clientcert $HOME/projects/machines/hosts/surtr/email/ca/gkleen@sif.crt --edit --remotesieve sieve | ||
15 | ;; | ||
16 | ymir) | ||
17 | sieve-connect -s ymir.yggdrasil.li -u gkleen --edit --remotesieve sieve | ||
18 | ;; | ||
19 | *) | ||
20 | echo "Unknown host: ‘$host’" >&2 | ||
21 | return 2 | ||
22 | ;; | ||
23 | esac | ||
24 | '' | ||
diff --git a/accounts/gkleen@sif/xmonad/.gitignore b/accounts/gkleen@sif/xmonad/.gitignore deleted file mode 100644 index c11891cd..00000000 --- a/accounts/gkleen@sif/xmonad/.gitignore +++ /dev/null | |||
@@ -1,4 +0,0 @@ | |||
1 | **/#*# | ||
2 | **/.stack-work/ | ||
3 | /stack.yaml.lock | ||
4 | /*.cabal | ||
diff --git a/accounts/gkleen@sif/xmonad/default.nix b/accounts/gkleen@sif/xmonad/default.nix deleted file mode 100644 index 8790c12f..00000000 --- a/accounts/gkleen@sif/xmonad/default.nix +++ /dev/null | |||
@@ -1,7 +0,0 @@ | |||
1 | argumentPackages@{ ... }: | ||
2 | |||
3 | let | ||
4 | # defaultPackages = (import ./stackage.nix {}); | ||
5 | # haskellPackages = defaultPackages // argumentPackages; | ||
6 | haskellPackages = argumentPackages; | ||
7 | in haskellPackages.callPackage ./xmonad-yggdrasil.nix {} | ||
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs deleted file mode 100644 index e6accdcc..00000000 --- a/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs +++ /dev/null | |||
@@ -1,127 +0,0 @@ | |||
1 | {-# LANGUAGE DeriveGeneric, OverloadedLists, OverloadedStrings, ViewPatterns, ExistentialQuantification, MultiWayIf #-} | ||
2 | |||
3 | module XMonad.Mpv | ||
4 | ( MpvCommand(..), MpvResponse(..), MpvException(..) | ||
5 | , mpv | ||
6 | , mpvDir | ||
7 | , mpvAll, mpvOne | ||
8 | , mpvResponse | ||
9 | ) where | ||
10 | |||
11 | import Data.Aeson | ||
12 | |||
13 | import Data.Monoid | ||
14 | |||
15 | import Network.Socket hiding (recv) | ||
16 | import Network.Socket.ByteString | ||
17 | |||
18 | import qualified Data.ByteString as BS | ||
19 | import qualified Data.ByteString.Char8 as CBS | ||
20 | import qualified Data.ByteString.Lazy as LBS | ||
21 | |||
22 | import GHC.Generics (Generic) | ||
23 | import Data.Typeable (Typeable) | ||
24 | import Data.String (IsString(..)) | ||
25 | |||
26 | import Control.Exception | ||
27 | |||
28 | import System.IO.Temp (getCanonicalTemporaryDirectory) | ||
29 | |||
30 | import Control.Monad | ||
31 | import Control.Exception (bracket) | ||
32 | import Control.Monad.IO.Class (MonadIO(..)) | ||
33 | |||
34 | import System.FilePath | ||
35 | import System.Directory (getDirectoryContents) | ||
36 | |||
37 | import Data.List | ||
38 | import Data.Either | ||
39 | import Data.Maybe | ||
40 | |||
41 | import Debug.Trace | ||
42 | |||
43 | |||
44 | data MpvCommand | ||
45 | = forall a. ToJSON a => MpvSetProperty String a | ||
46 | | MpvGetProperty String | ||
47 | data MpvResponse | ||
48 | = MpvError String | ||
49 | | MpvSuccess (Maybe Value) | ||
50 | deriving (Read, Show, Generic, Eq) | ||
51 | data MpvException = MpvException String | ||
52 | | MpvNoValue | ||
53 | | MpvNoParse String | ||
54 | deriving (Generic, Typeable, Read, Show) | ||
55 | instance Exception MpvException | ||
56 | |||
57 | |||
58 | instance ToJSON MpvCommand where | ||
59 | toJSON (MpvSetProperty name val) = Array ["set_property", fromString name, toJSON val] | ||
60 | toJSON (MpvGetProperty name) = Array ["get_property", fromString name] | ||
61 | |||
62 | instance FromJSON MpvResponse where | ||
63 | parseJSON = withObject "response object" $ \obj -> do | ||
64 | mval <- obj .:? "data" | ||
65 | err <- obj .: "error" | ||
66 | |||
67 | let ret | ||
68 | | err == "success" = MpvSuccess mval | ||
69 | | otherwise = MpvError err | ||
70 | |||
71 | return ret | ||
72 | |||
73 | mpvSocket :: FilePath -> (Socket -> IO a) -> IO a | ||
74 | mpvSocket sockPath = withSocketsDo . bracket mkSock close | ||
75 | where | ||
76 | mkSock = do | ||
77 | sock <- socket AF_UNIX Stream defaultProtocol | ||
78 | connect sock $ SockAddrUnix (traceId sockPath) | ||
79 | return sock | ||
80 | |||
81 | mpvResponse :: FromJSON v => MpvResponse -> IO v | ||
82 | mpvResponse (MpvError str) = throwIO $ MpvException str | ||
83 | mpvResponse (MpvSuccess Nothing) = throwIO MpvNoValue | ||
84 | mpvResponse (MpvSuccess (Just v)) = case fromJSON v of | ||
85 | Success v' -> return v' | ||
86 | Error str -> throwIO $ MpvNoParse str | ||
87 | |||
88 | mpv :: FilePath -> MpvCommand -> IO MpvResponse | ||
89 | mpv sockPath cmd = mpvSocket sockPath $ \sock -> do | ||
90 | let message = (`BS.append` "\n") . LBS.toStrict . encode $ Object [("command", toJSON cmd)] | ||
91 | traceIO $ show message | ||
92 | sendAll sock message | ||
93 | let recvAll = do | ||
94 | prefix <- recv sock 4096 | ||
95 | if | ||
96 | | (prefix', rest) <- CBS.break (== '\n') prefix | ||
97 | , not (BS.null rest) -> return prefix' | ||
98 | | BS.null prefix -> return prefix | ||
99 | | otherwise -> BS.append prefix <$> recvAll | ||
100 | response <- recvAll | ||
101 | traceIO $ show response | ||
102 | either (ioError . userError) return . traceShowId $ eitherDecodeStrict' response | ||
103 | |||
104 | mpvDir :: Exception e => FilePath -> (FilePath -> [(FilePath, Either e MpvResponse)] -> Maybe MpvCommand) -> IO [(FilePath, Either e MpvResponse)] | ||
105 | mpvDir dir step = do | ||
106 | socks <- filter (".sock" `isSuffixOf`) <$> getDirectoryContents dir | ||
107 | go [] socks | ||
108 | where | ||
109 | go acc [] = return acc | ||
110 | go acc (sock:socks) | ||
111 | | Just cmd <- step sock acc = do | ||
112 | res <- try $ mpv (dir </> sock) cmd | ||
113 | go ((sock, res) : acc) socks | ||
114 | | otherwise = | ||
115 | go acc socks | ||
116 | |||
117 | mpvAll :: FilePath -> MpvCommand -> IO [MpvResponse] | ||
118 | mpvAll dir cmd = do | ||
119 | results <- map snd <$> (mpvDir dir (\_ _ -> Just cmd) :: IO [(FilePath, Either SomeException MpvResponse)]) | ||
120 | mapM (either throwIO return) results | ||
121 | |||
122 | mpvOne :: FilePath -> MpvCommand -> IO (Maybe MpvResponse) | ||
123 | mpvOne dir cmd = listToMaybe . snd . partitionEithers . map snd <$> (mpvDir dir step :: IO [(FilePath, Either SomeException MpvResponse)]) | ||
124 | where | ||
125 | step _ results | ||
126 | | any (isRight . snd) results = Nothing | ||
127 | | otherwise = Just cmd | ||
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs deleted file mode 100644 index 1caefae5..00000000 --- a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs +++ /dev/null | |||
@@ -1,94 +0,0 @@ | |||
1 | module XMonad.Prompt.MyPass | ||
2 | ( | ||
3 | -- * Usages | ||
4 | -- $usages | ||
5 | mkPassPrompt | ||
6 | ) where | ||
7 | |||
8 | import Control.Monad (liftM) | ||
9 | import XMonad.Core | ||
10 | import XMonad.Prompt ( XPrompt | ||
11 | , showXPrompt | ||
12 | , commandToComplete | ||
13 | , nextCompletion | ||
14 | , getNextCompletion | ||
15 | , XPConfig | ||
16 | , mkXPrompt | ||
17 | , searchPredicate) | ||
18 | import System.Directory (getHomeDirectory) | ||
19 | import System.FilePath (takeExtension, dropExtension, combine) | ||
20 | import System.Posix.Env (getEnv) | ||
21 | import XMonad.Util.Run (runProcessWithInput) | ||
22 | |||
23 | -- $usages | ||
24 | -- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@: | ||
25 | -- | ||
26 | -- > import XMonad.Prompt.Pass | ||
27 | -- | ||
28 | -- Then add a keybinding for 'passPrompt', 'passGeneratePrompt' or 'passRemovePrompt': | ||
29 | -- | ||
30 | -- > , ((modMask x , xK_p) , passPrompt xpconfig) | ||
31 | -- > , ((modMask x .|. controlMask, xK_p) , passGeneratePrompt xpconfig) | ||
32 | -- > , ((modMask x .|. controlMask .|. shiftMask, xK_p), passRemovePrompt xpconfig) | ||
33 | -- | ||
34 | -- For detailed instructions on: | ||
35 | -- | ||
36 | -- - editing your key bindings, see "XMonad.Doc.Extending#Editing_key_bindings". | ||
37 | -- | ||
38 | -- - how to setup the password storage, see <http://git.zx2c4.com/password-store/about/> | ||
39 | -- | ||
40 | |||
41 | type Predicate = String -> String -> Bool | ||
42 | |||
43 | getPassCompl :: [String] -> Predicate -> String -> IO [String] | ||
44 | getPassCompl compls p s | ||
45 | | length s <= minL | ||
46 | , all ((> minL) . length) compls = return [] | ||
47 | | otherwise = do return $ filter (p s) compls | ||
48 | where | ||
49 | minL = 3 | ||
50 | |||
51 | type PromptLabel = String | ||
52 | |||
53 | data Pass = Pass PromptLabel | ||
54 | |||
55 | instance XPrompt Pass where | ||
56 | showXPrompt (Pass prompt) = prompt ++ ": " | ||
57 | commandToComplete _ c = c | ||
58 | nextCompletion _ = getNextCompletion | ||
59 | |||
60 | -- | Default password store folder in $HOME/.password-store | ||
61 | -- | ||
62 | passwordStoreFolderDefault :: String -> String | ||
63 | passwordStoreFolderDefault home = combine home ".password-store" | ||
64 | |||
65 | -- | Compute the password store's location. | ||
66 | -- Use the PASSWORD_STORE_DIR environment variable to set the password store. | ||
67 | -- If empty, return the password store located in user's home. | ||
68 | -- | ||
69 | passwordStoreFolder :: IO String | ||
70 | passwordStoreFolder = | ||
71 | getEnv "PASSWORD_STORE_DIR" >>= computePasswordStoreDir | ||
72 | where computePasswordStoreDir Nothing = liftM passwordStoreFolderDefault getHomeDirectory | ||
73 | computePasswordStoreDir (Just storeDir) = return storeDir | ||
74 | |||
75 | -- | A pass prompt factory | ||
76 | -- | ||
77 | mkPassPrompt :: PromptLabel -> (String -> X ()) -> XPConfig -> X () | ||
78 | mkPassPrompt promptLabel passwordFunction xpconfig = do | ||
79 | passwords <- io (passwordStoreFolder >>= getPasswords) | ||
80 | mkXPrompt (Pass promptLabel) xpconfig (getPassCompl passwords $ searchPredicate xpconfig) passwordFunction | ||
81 | |||
82 | -- | Retrieve the list of passwords from the password storage 'passwordStoreDir | ||
83 | getPasswords :: FilePath -> IO [String] | ||
84 | getPasswords passwordStoreDir = do | ||
85 | files <- runProcessWithInput "find" [ | ||
86 | passwordStoreDir, | ||
87 | "-type", "f", | ||
88 | "-name", "*.gpg", | ||
89 | "-printf", "%P\n"] [] | ||
90 | return $ map removeGpgExtension $ lines files | ||
91 | |||
92 | removeGpgExtension :: String -> String | ||
93 | removeGpgExtension file | takeExtension file == ".gpg" = dropExtension file | ||
94 | | otherwise = file | ||
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs deleted file mode 100644 index c268f87d..00000000 --- a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs +++ /dev/null | |||
@@ -1,105 +0,0 @@ | |||
1 | module XMonad.Prompt.MyShell | ||
2 | ( Shell (..) | ||
3 | , shellPrompt | ||
4 | , prompt | ||
5 | , safePrompt | ||
6 | , unsafePrompt | ||
7 | , getCommands | ||
8 | , getShellCompl | ||
9 | , split | ||
10 | ) where | ||
11 | |||
12 | import Codec.Binary.UTF8.String (encodeString) | ||
13 | import Control.Exception as E | ||
14 | import Control.Monad (forM) | ||
15 | import Data.List (isPrefixOf) | ||
16 | import System.Directory (doesDirectoryExist, getDirectoryContents) | ||
17 | import System.Environment (getEnv) | ||
18 | import System.Posix.Files (getFileStatus, isDirectory) | ||
19 | |||
20 | import XMonad hiding (config) | ||
21 | import XMonad.Prompt | ||
22 | import XMonad.Util.Run | ||
23 | |||
24 | econst :: Monad m => a -> IOException -> m a | ||
25 | econst = const . return | ||
26 | |||
27 | data Shell = Shell String | ||
28 | |||
29 | instance XPrompt Shell where | ||
30 | showXPrompt (Shell q) = q | ||
31 | completionToCommand _ = escape | ||
32 | |||
33 | shellPrompt :: String -> XPConfig -> X () | ||
34 | shellPrompt q c = do | ||
35 | cmds <- io getCommands | ||
36 | mkXPrompt (Shell q) c (getShellCompl cmds) spawn | ||
37 | |||
38 | {- $spawns | ||
39 | See safe and unsafeSpawn in "XMonad.Util.Run". | ||
40 | prompt is an alias for safePrompt; | ||
41 | safePrompt and unsafePrompt work on the same principles, but will use | ||
42 | XPrompt to interactively query the user for input; the appearance is | ||
43 | set by passing an XPConfig as the second argument. The first argument | ||
44 | is the program to be run with the interactive input. | ||
45 | You would use these like this: | ||
46 | |||
47 | > , ((modm, xK_b), safePrompt "firefox" greenXPConfig) | ||
48 | > , ((modm .|. shiftMask, xK_c), prompt ("xterm" ++ " -e") greenXPConfig) | ||
49 | |||
50 | Note that you want to use safePrompt for Firefox input, as Firefox | ||
51 | wants URLs, and unsafePrompt for the XTerm example because this allows | ||
52 | you to easily start a terminal executing an arbitrary command, like | ||
53 | 'top'. -} | ||
54 | |||
55 | prompt, unsafePrompt, safePrompt :: String -> FilePath -> XPConfig -> X () | ||
56 | prompt = unsafePrompt | ||
57 | safePrompt q c config = mkXPrompt (Shell q) config (getShellCompl [c]) run | ||
58 | where run = safeSpawn c . return | ||
59 | unsafePrompt q c config = mkXPrompt (Shell q) config (getShellCompl [c]) run | ||
60 | where run a = unsafeSpawn $ c ++ " " ++ a | ||
61 | |||
62 | getShellCompl :: [String] -> String -> IO [String] | ||
63 | getShellCompl cmds s | s == "" || last s == ' ' = return [] | ||
64 | | otherwise = do | ||
65 | f <- fmap lines $ runProcessWithInput "bash" [] ("compgen -A file -- " | ||
66 | ++ s ++ "\n") | ||
67 | files <- case f of | ||
68 | [x] -> do fs <- getFileStatus (encodeString x) | ||
69 | if isDirectory fs then return [x ++ "/"] | ||
70 | else return [x] | ||
71 | _ -> return f | ||
72 | return . uniqSort $ files ++ commandCompletionFunction cmds s | ||
73 | |||
74 | commandCompletionFunction :: [String] -> String -> [String] | ||
75 | commandCompletionFunction cmds str | '/' `elem` str = [] | ||
76 | | otherwise = filter (isPrefixOf str) cmds | ||
77 | |||
78 | getCommands :: IO [String] | ||
79 | getCommands = do | ||
80 | p <- getEnv "PATH" `E.catch` econst [] | ||
81 | let ds = filter (/= "") $ split ':' p | ||
82 | es <- forM ds $ \d -> do | ||
83 | exists <- doesDirectoryExist d | ||
84 | if exists | ||
85 | then getDirectoryContents d | ||
86 | else return [] | ||
87 | return . uniqSort . filter ((/= '.') . head) . concat $ es | ||
88 | |||
89 | split :: Eq a => a -> [a] -> [[a]] | ||
90 | split _ [] = [] | ||
91 | split e l = | ||
92 | f : split e (rest ls) | ||
93 | where | ||
94 | (f,ls) = span (/=e) l | ||
95 | rest s | s == [] = [] | ||
96 | | otherwise = tail s | ||
97 | |||
98 | escape :: String -> String | ||
99 | escape [] = "" | ||
100 | escape (x:xs) | ||
101 | | isSpecialChar x = '\\' : x : escape xs | ||
102 | | otherwise = x : escape xs | ||
103 | |||
104 | isSpecialChar :: Char -> Bool | ||
105 | isSpecialChar = flip elem " &\\@\"'#?$*()[]{};" | ||
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs deleted file mode 100644 index 998c533e..00000000 --- a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs +++ /dev/null | |||
@@ -1,246 +0,0 @@ | |||
1 | module XMonad.Prompt.MySsh | ||
2 | ( -- * Usage | ||
3 | -- $usage | ||
4 | sshPrompt, | ||
5 | Ssh, | ||
6 | Override (..), | ||
7 | mkOverride, | ||
8 | Conn (..), | ||
9 | moshCmd, | ||
10 | moshCmd', | ||
11 | sshCmd, | ||
12 | inTmux, | ||
13 | withEnv | ||
14 | ) where | ||
15 | |||
16 | import XMonad | ||
17 | import XMonad.Util.Run | ||
18 | import XMonad.Prompt | ||
19 | |||
20 | import System.Directory | ||
21 | import System.Environment | ||
22 | import qualified Control.Exception as E | ||
23 | |||
24 | import Control.Monad | ||
25 | import Data.Maybe | ||
26 | |||
27 | import Text.Parsec.String | ||
28 | import Text.Parsec | ||
29 | import Data.Char (isSpace) | ||
30 | |||
31 | econst :: Monad m => a -> E.IOException -> m a | ||
32 | econst = const . return | ||
33 | |||
34 | -- $usage | ||
35 | -- 1. In your @~\/.xmonad\/xmonad.hs@: | ||
36 | -- | ||
37 | -- > import XMonad.Prompt | ||
38 | -- > import XMonad.Prompt.Ssh | ||
39 | -- | ||
40 | -- 2. In your keybindings add something like: | ||
41 | -- | ||
42 | -- > , ((modm .|. controlMask, xK_s), sshPrompt defaultXPConfig) | ||
43 | -- | ||
44 | -- Keep in mind, that if you want to use the completion you have to | ||
45 | -- disable the "HashKnownHosts" option in your ssh_config | ||
46 | -- | ||
47 | -- For detailed instruction on editing the key binding see | ||
48 | -- "XMonad.Doc.Extending#Editing_key_bindings". | ||
49 | |||
50 | data Override = Override | ||
51 | { oUser :: Maybe String | ||
52 | , oHost :: String | ||
53 | , oPort :: Maybe Int | ||
54 | , oCommand :: Conn -> String | ||
55 | } | ||
56 | |||
57 | mkOverride = Override { oUser = Nothing, oHost = "", oPort = Nothing, oCommand = sshCmd } | ||
58 | sshCmd c = concat | ||
59 | [ "ssh -t " | ||
60 | , if isJust $ cUser c then (fromJust $ cUser c) ++ "@" else "" | ||
61 | , cHost c | ||
62 | , if isJust $ cPort c then " -p " ++ (show $ fromJust $ cPort c) else "" | ||
63 | , " -- " | ||
64 | , cCommand c | ||
65 | ] | ||
66 | moshCmd c = concat | ||
67 | [ "mosh " | ||
68 | , if isJust $ cUser c then (fromJust $ cUser c) ++ "@" else "" | ||
69 | , cHost c | ||
70 | , if isJust $ cPort c then " --ssh=\"ssh -p " ++ (show $ fromJust $ cPort c) ++ "\"" else "" | ||
71 | , " -- " | ||
72 | , cCommand c | ||
73 | ] | ||
74 | moshCmd' p c = concat | ||
75 | [ "mosh " | ||
76 | , "--server=" ++ p ++ " " | ||
77 | , if isJust $ cUser c then (fromJust $ cUser c) ++ "@" else "" | ||
78 | , cHost c | ||
79 | , if isJust $ cPort c then " --ssh=\"ssh -p " ++ (show $ fromJust $ cPort c) ++ "\"" else "" | ||
80 | , " -- " | ||
81 | , cCommand c | ||
82 | ] | ||
83 | inTmux Nothing c | ||
84 | | null $ cCommand c = c { cCommand = "tmux new-session" } | ||
85 | | otherwise = c { cCommand = "tmux new-session \"" ++ (cCommand c) ++ "\"" } | ||
86 | inTmux (Just h) c | ||
87 | | null $ cCommand c = c { cCommand = "tmux new-session -As " <> h } | ||
88 | | otherwise = c { cCommand = "tmux new-session \"" ++ (cCommand c) ++ "\"" } | ||
89 | withEnv :: [(String, String)] -> Conn -> Conn | ||
90 | withEnv envs c = c { cCommand = "env" ++ (concat $ map (\(n, v) -> ' ' : (n ++ "=" ++ v)) envs) ++ " " ++ (cCommand c) } | ||
91 | |||
92 | data Conn = Conn | ||
93 | { cUser :: Maybe String | ||
94 | , cHost :: String | ||
95 | , cPort :: Maybe Int | ||
96 | , cCommand :: String | ||
97 | } deriving (Eq, Show, Read) | ||
98 | |||
99 | data Ssh = Ssh | ||
100 | |||
101 | instance XPrompt Ssh where | ||
102 | showXPrompt Ssh = "SSH to: " | ||
103 | commandToComplete _ c = c | ||
104 | nextCompletion _ = getNextCompletion | ||
105 | |||
106 | toConn :: String -> Maybe Conn | ||
107 | toConn = toConn' . parse connParser "(unknown)" | ||
108 | toConn' :: Either ParseError Conn -> Maybe Conn | ||
109 | toConn' (Left _) = Nothing | ||
110 | toConn' (Right a) = Just a | ||
111 | |||
112 | connParser :: Parser Conn | ||
113 | connParser = do | ||
114 | spaces | ||
115 | user' <- optionMaybe $ try $ do | ||
116 | str <- many1 $ satisfy (\c -> (not $ isSpace c) && (c /= '@')) | ||
117 | char '@' | ||
118 | return str | ||
119 | host' <- many1 $ satisfy (not . isSpace) | ||
120 | port' <- optionMaybe $ try $ do | ||
121 | space | ||
122 | string "-p" | ||
123 | spaces | ||
124 | int <- many1 digit | ||
125 | (space >> return ()) <|> eof | ||
126 | return $ (read int :: Int) | ||
127 | spaces | ||
128 | command' <- many anyChar | ||
129 | eof | ||
130 | return $ Conn | ||
131 | { cHost = host' | ||
132 | , cUser = user' | ||
133 | , cPort = port' | ||
134 | , cCommand = command' | ||
135 | } | ||
136 | |||
137 | sshPrompt :: [Override] -> XPConfig -> X () | ||
138 | sshPrompt o c = do | ||
139 | sc <- io sshComplList | ||
140 | mkXPrompt Ssh c (mkComplFunFromList c sc) $ ssh o | ||
141 | |||
142 | ssh :: [Override] -> String -> X () | ||
143 | ssh overrides str = do | ||
144 | let cmd = applyOverrides overrides str | ||
145 | liftIO $ putStr "SSH Command: " | ||
146 | liftIO $ putStrLn cmd | ||
147 | runInTerm "" cmd | ||
148 | |||
149 | applyOverrides :: [Override] -> String -> String | ||
150 | applyOverrides [] str = "ssh " ++ str | ||
151 | applyOverrides (o:os) str = case (applyOverride o str) of | ||
152 | Just str -> str | ||
153 | Nothing -> applyOverrides os str | ||
154 | |||
155 | applyOverride :: Override -> String -> Maybe String | ||
156 | applyOverride o str = let | ||
157 | conn = toConn str | ||
158 | in | ||
159 | if isNothing conn then Nothing else | ||
160 | case (fromJust conn) `matches` o of | ||
161 | True -> Just $ (oCommand o) (fromJust conn) | ||
162 | False -> Nothing | ||
163 | |||
164 | matches :: Conn -> Override -> Bool | ||
165 | a `matches` b = and | ||
166 | [ justBool (cUser a) (oUser b) (==) | ||
167 | , (cHost a) == (oHost b) | ||
168 | , justBool (cPort a) (oPort b) (==) | ||
169 | ] | ||
170 | |||
171 | justBool :: Eq a => Maybe a -> Maybe a -> (a -> a -> Bool) -> Bool | ||
172 | justBool Nothing _ _ = True | ||
173 | justBool _ Nothing _ = True | ||
174 | justBool (Just a) (Just b) match = a `match` b | ||
175 | |||
176 | sshComplList :: IO [String] | ||
177 | sshComplList = uniqSort `fmap` liftM2 (++) sshComplListLocal sshComplListGlobal | ||
178 | |||
179 | sshComplListLocal :: IO [String] | ||
180 | sshComplListLocal = do | ||
181 | h <- getEnv "HOME" | ||
182 | s1 <- sshComplListFile $ h ++ "/.ssh/known_hosts" | ||
183 | s2 <- sshComplListConf $ h ++ "/.ssh/config" | ||
184 | return $ s1 ++ s2 | ||
185 | |||
186 | sshComplListGlobal :: IO [String] | ||
187 | sshComplListGlobal = do | ||
188 | env <- getEnv "SSH_KNOWN_HOSTS" `E.catch` econst "/nonexistent" | ||
189 | fs <- mapM fileExists [ env | ||
190 | , "/usr/local/etc/ssh/ssh_known_hosts" | ||
191 | , "/usr/local/etc/ssh_known_hosts" | ||
192 | , "/etc/ssh/ssh_known_hosts" | ||
193 | , "/etc/ssh_known_hosts" | ||
194 | ] | ||
195 | case catMaybes fs of | ||
196 | [] -> return [] | ||
197 | (f:_) -> sshComplListFile' f | ||
198 | |||
199 | sshComplListFile :: String -> IO [String] | ||
200 | sshComplListFile kh = do | ||
201 | f <- doesFileExist kh | ||
202 | if f then sshComplListFile' kh | ||
203 | else return [] | ||
204 | |||
205 | sshComplListFile' :: String -> IO [String] | ||
206 | sshComplListFile' kh = do | ||
207 | l <- readFile kh | ||
208 | return $ map (getWithPort . takeWhile (/= ',') . concat . take 1 . words) | ||
209 | $ filter nonComment | ||
210 | $ lines l | ||
211 | |||
212 | sshComplListConf :: String -> IO [String] | ||
213 | sshComplListConf kh = do | ||
214 | f <- doesFileExist kh | ||
215 | if f then sshComplListConf' kh | ||
216 | else return [] | ||
217 | |||
218 | sshComplListConf' :: String -> IO [String] | ||
219 | sshComplListConf' kh = do | ||
220 | l <- readFile kh | ||
221 | return $ map (!!1) | ||
222 | $ filter isHost | ||
223 | $ map words | ||
224 | $ lines l | ||
225 | where | ||
226 | isHost ws = take 1 ws == ["Host"] && length ws > 1 | ||
227 | |||
228 | fileExists :: String -> IO (Maybe String) | ||
229 | fileExists kh = do | ||
230 | f <- doesFileExist kh | ||
231 | if f then return $ Just kh | ||
232 | else return Nothing | ||
233 | |||
234 | nonComment :: String -> Bool | ||
235 | nonComment [] = False | ||
236 | nonComment ('#':_) = False | ||
237 | nonComment ('|':_) = False -- hashed, undecodeable | ||
238 | nonComment _ = True | ||
239 | |||
240 | getWithPort :: String -> String | ||
241 | getWithPort ('[':str) = host ++ " -p " ++ port | ||
242 | where (host,p) = break (==']') str | ||
243 | port = case p of | ||
244 | ']':':':x -> x | ||
245 | _ -> "22" | ||
246 | getWithPort str = str | ||
diff --git a/accounts/gkleen@sif/xmonad/package.yaml b/accounts/gkleen@sif/xmonad/package.yaml deleted file mode 100644 index f65137af..00000000 --- a/accounts/gkleen@sif/xmonad/package.yaml +++ /dev/null | |||
@@ -1,31 +0,0 @@ | |||
1 | name: xmonad-yggdrasil | ||
2 | |||
3 | executables: | ||
4 | xmonad: | ||
5 | dependencies: | ||
6 | - base | ||
7 | - xmonad | ||
8 | - xmonad-contrib | ||
9 | - aeson | ||
10 | - bytestring | ||
11 | - text | ||
12 | - temporary | ||
13 | - filepath | ||
14 | - directory | ||
15 | - network | ||
16 | - unix | ||
17 | - utf8-string | ||
18 | - parsec | ||
19 | - process | ||
20 | - mtl | ||
21 | - X11 | ||
22 | - transformers | ||
23 | - containers | ||
24 | - hostname | ||
25 | - libnotify | ||
26 | - taffybar | ||
27 | |||
28 | main: xmonad.hs | ||
29 | source-dirs: | ||
30 | - . | ||
31 | - lib | ||
diff --git a/accounts/gkleen@sif/xmonad/stack.nix b/accounts/gkleen@sif/xmonad/stack.nix deleted file mode 100644 index 17a49e04..00000000 --- a/accounts/gkleen@sif/xmonad/stack.nix +++ /dev/null | |||
@@ -1,17 +0,0 @@ | |||
1 | { ghc, nixpkgs ? import ./nixpkgs.nix {} }: | ||
2 | |||
3 | let | ||
4 | haskellPackages = import ./stackage.nix { inherit nixpkgs; }; | ||
5 | inherit (nixpkgs {}) pkgs; | ||
6 | in pkgs.haskell.lib.buildStackProject { | ||
7 | inherit ghc; | ||
8 | inherit (haskellPackages) stack; | ||
9 | name = "stackenv"; | ||
10 | buildInputs = (with pkgs; | ||
11 | [ xorg.libX11 xorg.libXrandr xorg.libXinerama xorg.libXScrnSaver xorg.libXext xorg.libXft | ||
12 | cairo | ||
13 | glib | ||
14 | ]) ++ (with haskellPackages; | ||
15 | [ | ||
16 | ]); | ||
17 | } | ||
diff --git a/accounts/gkleen@sif/xmonad/stack.yaml b/accounts/gkleen@sif/xmonad/stack.yaml deleted file mode 100644 index b8ed1147..00000000 --- a/accounts/gkleen@sif/xmonad/stack.yaml +++ /dev/null | |||
@@ -1,10 +0,0 @@ | |||
1 | nix: | ||
2 | enable: true | ||
3 | shell-file: stack.nix | ||
4 | |||
5 | resolver: lts-13.21 | ||
6 | |||
7 | packages: | ||
8 | - . | ||
9 | |||
10 | extra-deps: [] | ||
diff --git a/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix b/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix deleted file mode 100644 index 7c853619..00000000 --- a/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix +++ /dev/null | |||
@@ -1,21 +0,0 @@ | |||
1 | { mkDerivation, aeson, base, bytestring, containers, directory | ||
2 | , filepath, hostname, hpack, mtl, network, parsec, process, lib | ||
3 | , temporary, transformers, unix, utf8-string, X11, xmonad | ||
4 | , xmonad-contrib, libnotify, taffybar | ||
5 | }: | ||
6 | mkDerivation { | ||
7 | pname = "xmonad-yggdrasil"; | ||
8 | version = "0.0.0"; | ||
9 | src = ./.; | ||
10 | isLibrary = false; | ||
11 | isExecutable = true; | ||
12 | libraryToolDepends = [ hpack ]; | ||
13 | executableHaskellDepends = [ | ||
14 | aeson base bytestring containers directory filepath hostname mtl | ||
15 | network parsec process temporary transformers unix utf8-string X11 | ||
16 | xmonad xmonad-contrib libnotify taffybar | ||
17 | ]; | ||
18 | preConfigure = "hpack"; | ||
19 | license = "unknown"; | ||
20 | hydraPlatforms = lib.platforms.none; | ||
21 | } | ||
diff --git a/accounts/gkleen@sif/xmonad/xmonad.hs b/accounts/gkleen@sif/xmonad/xmonad.hs deleted file mode 100644 index a44d3bb7..00000000 --- a/accounts/gkleen@sif/xmonad/xmonad.hs +++ /dev/null | |||
@@ -1,939 +0,0 @@ | |||
1 | {-# LANGUAGE TupleSections, ViewPatterns, OverloadedStrings, FlexibleInstances, UndecidableInstances, MultiWayIf, NumDecimals #-} | ||
2 | |||
3 | import XMonad | ||
4 | import XMonad.Hooks.DynamicLog | ||
5 | import XMonad.Hooks.ManageDocks | ||
6 | import XMonad.Util.Run hiding (proc) | ||
7 | import XMonad.Util.Loggers | ||
8 | import XMonad.Util.EZConfig(additionalKeys) | ||
9 | import System.IO | ||
10 | import System.IO.Error | ||
11 | import System.Environment | ||
12 | import Data.Map (Map) | ||
13 | import qualified Data.Map as Map | ||
14 | import qualified XMonad.StackSet as W | ||
15 | import System.Exit | ||
16 | import Control.Monad.State (get) | ||
17 | -- import XMonad.Layout.Spiral | ||
18 | import Data.Ratio | ||
19 | import Data.List | ||
20 | import Data.Char | ||
21 | import Data.Maybe (fromMaybe, listToMaybe, maybeToList, catMaybes, isJust) | ||
22 | import XMonad.Layout.Tabbed | ||
23 | import XMonad.Prompt | ||
24 | import XMonad.Prompt.Input | ||
25 | import XMonad.Util.Scratchpad | ||
26 | import XMonad.Util.NamedScratchpad | ||
27 | import XMonad.Util.Ungrab | ||
28 | import Control.Monad (sequence, liftM, liftM2, join, void) | ||
29 | import XMonad.Util.WorkspaceCompare | ||
30 | import XMonad.Layout.NoBorders | ||
31 | import XMonad.Layout.PerWorkspace | ||
32 | import XMonad.Layout.SimplestFloat | ||
33 | import XMonad.Layout.Renamed | ||
34 | import XMonad.Layout.Reflect | ||
35 | import XMonad.Layout.OnHost | ||
36 | import XMonad.Layout.Combo | ||
37 | import XMonad.Layout.ComboP | ||
38 | import XMonad.Layout.Column | ||
39 | import XMonad.Layout.TwoPane | ||
40 | import XMonad.Layout.IfMax | ||
41 | import XMonad.Layout.LayoutBuilder | ||
42 | import XMonad.Layout.WindowNavigation | ||
43 | import XMonad.Layout.Dwindle | ||
44 | import XMonad.Layout.TrackFloating | ||
45 | import System.Process | ||
46 | import System.Directory (removeFile) | ||
47 | import System.Posix.Files | ||
48 | import System.FilePath ((</>)) | ||
49 | import Control.Concurrent | ||
50 | import System.Posix.Process (getProcessID) | ||
51 | import System.IO.Error | ||
52 | import System.IO | ||
53 | import XMonad.Hooks.ManageHelpers hiding (CW) | ||
54 | import XMonad.Hooks.UrgencyHook as U | ||
55 | import XMonad.Hooks.EwmhDesktops | ||
56 | import XMonad.StackSet (RationalRect (..)) | ||
57 | import Control.Monad (when, filterM, (<=<)) | ||
58 | import Graphics.X11.ExtraTypes.XF86 | ||
59 | import XMonad.Util.Cursor | ||
60 | import XMonad.Actions.Warp | ||
61 | import XMonad.Actions.FloatKeys | ||
62 | import XMonad.Util.SpawnOnce | ||
63 | import System.Directory | ||
64 | import System.FilePath | ||
65 | import XMonad.Actions.CopyWindow | ||
66 | import XMonad.Hooks.ServerMode | ||
67 | import XMonad.Actions.Commands | ||
68 | import XMonad.Actions.CycleWS | ||
69 | import XMonad.Actions.RotSlaves | ||
70 | import XMonad.Actions.UpdatePointer | ||
71 | import XMonad.Prompt.Window | ||
72 | import Data.IORef | ||
73 | import Data.Monoid | ||
74 | import Data.String | ||
75 | import qualified XMonad.Actions.PhysicalScreens as P | ||
76 | |||
77 | import XMonad.Layout.IM | ||
78 | |||
79 | import System.Taffybar.Support.PagerHints (pagerHints) | ||
80 | |||
81 | import XMonad.Prompt.MyShell | ||
82 | import XMonad.Prompt.MyPass | ||
83 | import XMonad.Prompt.MySsh | ||
84 | |||
85 | import XMonad.Mpv | ||
86 | |||
87 | import Network.HostName | ||
88 | |||
89 | import Control.Applicative ((<$>)) | ||
90 | |||
91 | import Libnotify as Notify hiding (appName) | ||
92 | import qualified Libnotify as Notify (appName) | ||
93 | import Libnotify (Notification) | ||
94 | -- import System.Information.Battery | ||
95 | |||
96 | import Data.Int (Int32) | ||
97 | |||
98 | import System.Posix.Process | ||
99 | import System.Posix.Signals | ||
100 | import System.Posix.IO as Posix | ||
101 | import Control.Exception | ||
102 | |||
103 | import System.IO.Unsafe | ||
104 | |||
105 | import Control.Monad.Trans.Class | ||
106 | import Control.Monad.Trans.Maybe | ||
107 | |||
108 | import Data.Fixed (Micro) | ||
109 | |||
110 | import qualified Data.Text as Text | ||
111 | import Data.Ord (comparing) | ||
112 | import Debug.Trace | ||
113 | |||
114 | instance MonadIO m => IsString (m ()) where | ||
115 | fromString = spawn | ||
116 | |||
117 | type KeyMap = Map (ButtonMask, KeySym) (X ()) | ||
118 | |||
119 | data Host = Host | ||
120 | { hName :: HostName | ||
121 | , hManageHook :: ManageHook | ||
122 | , hWsp :: Integer -> WorkspaceId | ||
123 | , hCoWsp :: String -> Maybe WorkspaceId | ||
124 | , hKeysMod :: XConfig Layout -> (KeyMap -> KeyMap) | ||
125 | , hScreens :: [P.PhysicalScreen] | ||
126 | , hKbLayouts :: [(String, Maybe String)] | ||
127 | , hCmds :: X [(String, X ())] | ||
128 | , hKeyUpKeys :: XConfig Layout -> KeyMap | ||
129 | } | ||
130 | |||
131 | defaultHost = Host { hName = "unkown" | ||
132 | , hManageHook = composeOne [manageScratchTerm] | ||
133 | , hWsp = show | ||
134 | , hCoWsp = const Nothing | ||
135 | , hKeysMod = const id | ||
136 | , hScreens = [0,1..] | ||
137 | , hKbLayouts = [ ("us", Just "dvp") | ||
138 | , ("us", Nothing) | ||
139 | , ("de", Nothing) | ||
140 | ] | ||
141 | , hCmds = return [] | ||
142 | , hKeyUpKeys = const Map.empty | ||
143 | } | ||
144 | |||
145 | browser :: String | ||
146 | browser = "env MOZ_USE_XINPUT2=1 firefox" | ||
147 | |||
148 | gray, darkGray, red, green :: String | ||
149 | gray = "#808080" | ||
150 | darkGray = "#202020" | ||
151 | red = "#800000" | ||
152 | green = "#008000" | ||
153 | |||
154 | hostFromName :: HostName -> Host | ||
155 | hostFromName h@("vali") = defaultHost { hName = h | ||
156 | , hManageHook = composeOne $ catMaybes [ Just manageScratchTerm | ||
157 | , assign "web" $ className =? ".dwb-wrapped" | ||
158 | , assign "web" $ className =? "Chromium" | ||
159 | , assign "work" $ className =? "Emacs" | ||
160 | , assign "media" $ className =? "mpv" | ||
161 | ] | ||
162 | , hWsp = hWsp | ||
163 | , hCoWsp = hCoWsp | ||
164 | , hKeysMod = \conf -> Map.union $ (Map.fromList $ join $ map (spawnBindings conf) [ (xK_d, ["chromium", "chromium $(xclip -o)"]) | ||
165 | , (xK_e, ["emacsclient -c"]) | ||
166 | ]) | ||
167 | `Map.union` | ||
168 | ( Map.fromList [ ((XMonad.modMask conf .|. controlMask, xK_Return), scratchpadSpawnActionCustom $ (XMonad.terminal conf) ++ " -name scratchpad -title scratchpad -e tmux new-session -D -s scratch") | ||
169 | ] ) | ||
170 | , hScreens = hScreens defaultHost | ||
171 | } | ||
172 | where | ||
173 | workspaceNames = Map.fromList [ (2, "web") | ||
174 | , (3, "work") | ||
175 | , (10, "media") | ||
176 | ] | ||
177 | hWsp = wspFromMap workspaceNames | ||
178 | hCoWsp = coWspFromMap workspaceNames | ||
179 | assign wsp test = (\wsp -> test -?> doShift wsp) <$> hCoWsp wsp | ||
180 | hostFromName h | ||
181 | | h `elem` ["hel", "sif"] = defaultHost { hName = h | ||
182 | , hManageHook = namedScratchpadManageHook scratchpads <+> composeOne (catMaybes | ||
183 | [ assign "mpv" $ className =? "mpv" | ||
184 | , assign "mpv" $ stringProperty "WM_WINDOW_ROLE" =? "presentation" | ||
185 | , assign "read" $ stringProperty "WM_WINDOW_ROLE" =? "presenter" | ||
186 | , assign "mpv" $ className =? "factorio" | ||
187 | , assign "mpv" $ resource =? "twitch" | ||
188 | , assign "web" $ className =? "chromium-browser" | ||
189 | , assign "web" $ className =? "Google-chrome" | ||
190 | , assign "work" $ (appName =? "Devtools" <&&> className =? "firefox") | ||
191 | , assign "work" $ className =? "Postman" | ||
192 | , assign "web" $ (appName =? "Navigator" <&&> className =? "firefox") | ||
193 | , assign "comm" $ (className =? "Emacs" <&&> title =? "Mail") | ||
194 | , assign "comm" $ className =? "Zulip" | ||
195 | , assign "comm" $ className =? "Element" | ||
196 | , assign "comm" $ className =? "Rocket.Chat" | ||
197 | , assign "comm" $ className =? "Discord" | ||
198 | , assign "comm" $ className =? "Rainbow" | ||
199 | , assign "media" $ resource =? "media" | ||
200 | , assign "monitor" $ className =? "Grafana" | ||
201 | , assign "monitor" $ className =? "Virt-viewer" | ||
202 | , assign "monitor" $ resource =? "htop" | ||
203 | , assign "monitor" $ resource =? "monitor" | ||
204 | , assign "monitor" $ className =? "xfreerdp" | ||
205 | , assign "monitor" $ className =? "org.remmina.Remmina" | ||
206 | , Just $ resource =? "htop" -?> centerFloat | ||
207 | , Just $ (className =? "Scp-dbus-service.py") -?> centerFloat | ||
208 | , Just $ resource =? "log" -?> centerFloat | ||
209 | , assign "work" $ className =? "Alacritty" | ||
210 | , Just $ (appName =? "Edit with Emacs FRAME") -?> centerFloat | ||
211 | , assign' ["work", "uni"] $ (className =? "Emacs" <&&> appName /=? "Edit with Emacs FRAME") | ||
212 | , assign' ["work", "uni"] $ className =? "jetbrains-idea-ce" | ||
213 | , assign "read" $ className =? "llpp" | ||
214 | , assign "read" $ className =? "Evince" | ||
215 | , assign "read" $ className =? "Zathura" | ||
216 | , assign "read" $ className =? "MuPDF" | ||
217 | , assign "read" $ className =? "Xournal" | ||
218 | , assign "read" $ appName =? "libreoffice" | ||
219 | , assign "read" $ appName =? "com-trollworks-gcs-app-GCS" | ||
220 | , assign "read" $ appName =? "Tux.py" | ||
221 | , assign "read" $ className =? "Gnucash" | ||
222 | , assign "comm" $ className =? "Skype" | ||
223 | , assign "comm" $ className =? "Daily" | ||
224 | , assign "comm" $ className =? "Pidgin" | ||
225 | , assign "comm" $ className =? "Thunderbird" | ||
226 | , assign "comm" $ className =? "Slack" | ||
227 | , Just $ (resource =? "xvkbd") -?> doRectFloat $ RationalRect (1 % 8) (3 % 8) (6 % 8) (4 % 8) | ||
228 | , Just $ (stringProperty "_NET_WM_WINDOW_TYPE" =? "_NET_WM_WINDOW_TYPE_DIALOG") -?> doFloat | ||
229 | , Just $ (className =? "Dunst") -?> doFloat | ||
230 | , Just $ (className =? "Xmessage") -?> doCenterFloat | ||
231 | , Just $ (className =? "Nm-openconnect-auth-dialog") -?> centerFloat | ||
232 | , Just $ (className =? "Pinentry") -?> doCenterFloat | ||
233 | , Just $ (className =? "pinentry") -?> doCenterFloat | ||
234 | , Just $ (stringProperty "WM_WINDOW_ROLE" =? "GtkFileChooseDialog") -?> centerFloatSmall | ||
235 | , Just $ (className =? "Nvidia-settings") -?> doCenterFloat | ||
236 | , Just $ fmap ("Minetest" `isInfixOf`) title -?> doIgnore | ||
237 | , Just $ fmap ("Automachef" `isInfixOf`) title -?> doIgnore | ||
238 | , assign "call" $ className =? "zoom" | ||
239 | ]) | ||
240 | , hWsp = hWsp | ||
241 | , hCoWsp = hCoWsp | ||
242 | , hKeysMod = \conf -> Map.union $ (Map.fromList $ join $ map (spawnBindings conf) [ (xK_e, ["emacsclient -c"]) | ||
243 | , (xK_d, [fromString browser, "google-chrome" {- , "notmuch-links" -}]) | ||
244 | , (xK_c, [ inputPrompt xPConfigMonospace "dc" ?+ dc ]) | ||
245 | , (xK_g, ["pidgin"]) | ||
246 | , (xK_s, ["skype"]) | ||
247 | -- , (xK_p, [mkPassPrompt "Type password" pwType xPConfig, mkPassPrompt "Show password" pwShow xPConfig, mkPassPrompt "Copy password" pwClip xPConfig]) | ||
248 | , (xK_w, ["sudo rewacom"]) | ||
249 | , (xK_y, [ "tmux new-window -dt media /var/media/link.hs $(xclip -o)" | ||
250 | , "tmux new-window -dt media /var/media/download.hs $(xclip -o)" | ||
251 | , "tmux new-window -dt media /var/media/download.hs $(xclip -o -selection clipboard)" | ||
252 | ]) | ||
253 | , (xK_l, [ "tmux new-window -dt media mpv $(xclip -o)" | ||
254 | , "tmux new-window -dt media mpv $(xclip -o -selection clipboard)" | ||
255 | , "alacritty --class media -e tmuxp load /var/media" | ||
256 | ]) | ||
257 | {- , (xK_m, [ "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e '(notmuch)'" | ||
258 | , "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e '(notmuch-mua-new-mail)'" | ||
259 | , "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e \"(browse-url-mail \"$(xclip -o)\")\"" | ||
260 | ]) -} | ||
261 | , (xK_Return, ["keynav start,windowzoom", "keynav start"]) | ||
262 | , (xK_t, [inputPrompt xPConfigMonospace "fuzzytime timer" ?+ fuzzytime, fuzzytime "unset", work_fuzzytime]) | ||
263 | , (xK_a, [inputPrompt xPConfigMonospace "adjmix" ?+ adjmix]) | ||
264 | , (xK_s, [ inputPromptWithCompl xPConfigMonospace "start synergy" synergyCompl ?+ synergyStart | ||
265 | , inputPromptWithCompl xPConfigMonospace "stop synergy" synergyCompl ?+ synergyStop | ||
266 | ]) | ||
267 | , (xK_h, [ "alacritty --class htop -e htop" | ||
268 | , "alacritty --class log -e journalctl -xef" | ||
269 | ]) | ||
270 | , (xK_x, [ "autorandr -c" | ||
271 | , "autorandr -fl def" | ||
272 | ]) | ||
273 | , (xK_z, [ "zulip -- --force-device-scale-factor=2" | ||
274 | ]) | ||
275 | ]) | ||
276 | `Map.union` | ||
277 | ( Map.fromList [ ((XMonad.modMask conf .|. controlMask, xK_Return), namedScratchpadAction scratchpads "term") | ||
278 | , ((XMonad.modMask conf .|. controlMask, xK_a), namedScratchpadAction scratchpads "pavucontrol") | ||
279 | , ((XMonad.modMask conf .|. controlMask, xK_o), namedScratchpadAction scratchpads "easyeffects") | ||
280 | , ((XMonad.modMask conf .|. controlMask .|. shiftMask, xK_o), namedScratchpadAction scratchpads "helvum") | ||
281 | , ((XMonad.modMask conf .|. controlMask, xK_w), namedScratchpadAction scratchpads "alarms") | ||
282 | , ((XMonad.modMask conf .|. controlMask, xK_b), namedScratchpadAction scratchpads "blueman") | ||
283 | , ((XMonad.modMask conf .|. controlMask, xK_p), namedScratchpadAction scratchpads "keepassxc") | ||
284 | , ((XMonad.modMask conf .|. controlMask, xK_t), namedScratchpadAction scratchpads "toggl") | ||
285 | , ((XMonad.modMask conf .|. controlMask, xK_e), namedScratchpadAction scratchpads "emacs") | ||
286 | , ((XMonad.modMask conf .|. controlMask, xK_m), namedScratchpadAction scratchpads "calendar") | ||
287 | , ((XMonad.modMask conf .|. controlMask, xK_f), namedScratchpadAction scratchpads "music") | ||
288 | , ((XMonad.modMask conf .|. mod1Mask, xK_Up), rotate U) | ||
289 | , ((XMonad.modMask conf .|. mod1Mask, xK_Down), rotate D) | ||
290 | , ((XMonad.modMask conf .|. mod1Mask, xK_Left), rotate L) | ||
291 | , ((XMonad.modMask conf .|. mod1Mask, xK_Right), rotate R) | ||
292 | , ((controlMask, xK_space ), "dunstctl close" ) | ||
293 | , ((controlMask .|. shiftMask, xK_space ), "dunstctl close-all" ) | ||
294 | , ((controlMask, xK_period), "dunstctl context" ) | ||
295 | , ((controlMask, xK_comma ), "dunstctl history-pop") | ||
296 | -- , ((XMonad.modMask conf .|. shiftMask, xK_a), startMute "hel") | ||
297 | ] ) | ||
298 | , hKeyUpKeys = \conf -> Map.fromList [ -- ((XMonad.modMask conf .|. shiftMask, xK_a), stopMute "hel") | ||
299 | ] | ||
300 | , hScreens = hScreens defaultHost | ||
301 | , hCmds = return [ ("prev-workspace", prevWS) | ||
302 | , ("next-workspace", nextWS) | ||
303 | , ("prev-window", rotAllDown) | ||
304 | , ("next-window", rotAllUp) | ||
305 | , ("banish", banishScreen LowerRight) | ||
306 | , ("update-gpg-tty", safeSpawn "gpg-connect-agent" ["UPDATESTARTUPTTY", "/bye"]) | ||
307 | , ("rescreen", rescreen) | ||
308 | , ("repanel", do | ||
309 | spawn "nm-applet" | ||
310 | spawn "blueman-applet" | ||
311 | spawn "pasystray" | ||
312 | spawn "kdeconnect-indicator" | ||
313 | spawn "dunst -print" | ||
314 | spawn "udiskie" | ||
315 | spawn "autocutsel -s PRIMARY" | ||
316 | spawn "autocutsel -s CLIPBOARD" | ||
317 | ) | ||
318 | , ("pause", mediaMpv $ MpvSetProperty "pause" True) | ||
319 | , ("unpause", mediaMpv $ MpvSetProperty "pause" False) | ||
320 | , ("exit", io $ exitWith ExitSuccess) | ||
321 | ] | ||
322 | } | ||
323 | where | ||
324 | withGdkScale act = void . xfork $ setEnv "GDK_SCALE" "2" >> act | ||
325 | workspaceNames = Map.fromList [ (1, "comm") | ||
326 | , (2, "web") | ||
327 | , (3, "work") | ||
328 | , (4, "read") | ||
329 | , (5, "monitor") | ||
330 | , (6, "uni") | ||
331 | , (8, "call") | ||
332 | , (9, "media") | ||
333 | , (10, "mpv") | ||
334 | ] | ||
335 | scratchpads = [ NS "term" "alacritty --class scratchpad --title scratchpad -e tmux new-session -AD -s scratch" (resource =? "scratchpad") centerFloat | ||
336 | , NS "pavucontrol" "pavucontrol" (resource =? "pavucontrol") centerFloat | ||
337 | , NS "helvum" "helvum" (resource =? "helvum") centerFloat | ||
338 | , NS "easyeffects" "easyeffects" (resource =? "easyeffects") centerFloat | ||
339 | , NS "alarms" "alarm-clock-applet" (className =? "Alarm-clock-applet" <&&> title =? "Alarms") centerFloat | ||
340 | , NS "blueman" "blueman-manager" (className =? ".blueman-manager-wrapped") centerFloat | ||
341 | , NS "keepassxc" "keepassxc" (className =? "KeePassXC") centerFloat | ||
342 | , NS "toggl" "toggldesktop" (className =? "Toggl Desktop") centerFloat | ||
343 | , NS "calendar" "minetime -- --force-device-scale-factor=1.6" (className =? "MineTime") centerFloat | ||
344 | , NS "emacs" "emacsclient -c -F \"'(title . \\\"Scratchpad\\\")\"" (className =? "Emacs" <&&> title =? "Scratchpad") centerFloat | ||
345 | , NS "music" "ytmdesktop" (className =? "youtube-music-desktop-app") centerFloat | ||
346 | ] | ||
347 | centerFloat = customFloating $ RationalRect (1 % 16) (1 % 16) (7 % 8) (7 % 8) | ||
348 | centerFloatSmall = customFloating $ RationalRect (1 % 4) (1 % 4) (1 % 2) (1 % 2) | ||
349 | hWsp = wspFromMap workspaceNames | ||
350 | hCoWsp = coWspFromMap workspaceNames | ||
351 | assign wsp test = (\wsp -> test -?> doShift wsp) <$> hCoWsp wsp | ||
352 | assign' :: [String] -> Query Bool -> Maybe MaybeManageHook | ||
353 | assign' wsps test = do | ||
354 | wsIds <- mapM hCoWsp wsps | ||
355 | return $ test -?> go wsIds | ||
356 | where | ||
357 | go :: [WorkspaceId] -> ManageHook | ||
358 | go wsps = do | ||
359 | visWsps <- liftX $ (\wset -> W.tag . W.workspace <$> W.current wset : W.visible wset) <$> gets windowset | ||
360 | case (filter (`elem` visWsps) wsps, wsps) of | ||
361 | (wsp : _, _) -> doShift wsp | ||
362 | (_, wsp : _) -> doShift wsp | ||
363 | ([], []) -> return mempty | ||
364 | rotate rot = do | ||
365 | safeSpawn "xrandr" ["--output", "eDP-1", "--rotate", xrandrDir] | ||
366 | mapM_ rotTouch touchscreens | ||
367 | where | ||
368 | xrandrDir = case rot of | ||
369 | U -> "normal" | ||
370 | L -> "left" | ||
371 | R -> "right" | ||
372 | D -> "inverted" | ||
373 | matrix = case rot of | ||
374 | U -> [ [ 1, 0, 0] | ||
375 | , [ 0, 1, 0] | ||
376 | , [ 0, 0, 1] | ||
377 | ] | ||
378 | L -> [ [ 0, -1, 1] | ||
379 | , [ 1, 0, 0] | ||
380 | , [ 0, 0, 1] | ||
381 | ] | ||
382 | R -> [ [ 0, 1, 0] | ||
383 | , [-1, 0, 1] | ||
384 | , [ 0, 0, 1] | ||
385 | ] | ||
386 | D -> [ [-1, 0, 1] | ||
387 | , [ 0, -1, 1] | ||
388 | , [ 0, 0, 1] | ||
389 | ] | ||
390 | touchscreens = [ "Wacom Co.,Ltd. Pen and multitouch sensor Finger touch" | ||
391 | , "Wacom Co.,Ltd. Pen and multitouch sensor Pen stylus" | ||
392 | , "Wacom Co.,Ltd. Pen and multitouch sensor Pen eraser" | ||
393 | ] | ||
394 | rotTouch screen = do | ||
395 | safeSpawn "xinput" $ ["set-prop", screen, "Coordinate Transformation Matrix"] ++ map (\n -> show n ++ ",") (concat matrix) | ||
396 | safeSpawn "xinput" ["map-to-output", screen, "eDP-1"] | ||
397 | withPw f label = io . void . forkProcess $ do | ||
398 | uninstallSignalHandlers | ||
399 | void $ createSession | ||
400 | (dropWhileEnd isSpace -> pw) <- readCreateProcess (proc "pass" ["show", label]) "" | ||
401 | void $ f pw | ||
402 | pwType :: String -> X () | ||
403 | pwType = withPw $ readCreateProcess (proc "xdotool" ["type", "--clearmodifiers", "--file", "-"]) | ||
404 | pwClip label = safeSpawn "pass" ["show", "--clip", label] | ||
405 | pwShow :: String -> X () | ||
406 | pwShow = withPw $ \pw -> do | ||
407 | xmessage <- fromMaybe "xmessage" <$> liftIO (lookupEnv "XMONAD_XMESSAGE") | ||
408 | readCreateProcess (proc xmessage ["-file", "-"]) pw | ||
409 | fuzzytime str = safeSpawn "fuzzytime" $ "timer" : words str | ||
410 | work_fuzzytime = io . void . forkProcess $ do | ||
411 | readCreateProcess (proc "worktime" []) "" >>= safeSpawn "fuzzytime" . ("timer" : ) . pure | ||
412 | adjmix str = safeSpawn "adjmix" $ words str | ||
413 | dc expr = void . xfork $ do | ||
414 | result <- readProcess "dc" [] $ expr ++ "f" | ||
415 | let | ||
416 | (first : rest) = filter (not . null) $ lines result | ||
417 | notification = Notify.summary first <> Notify.body (unlines rest) <> Notify.timeout Infinite <> Notify.urgency Normal <> Notify.appName "dc" | ||
418 | void $ Notify.display notification | ||
419 | synergyCompl = mkComplFunFromList' xPConfigMonospace ["mathw86"] | ||
420 | synergyStart host = safeSpawn "systemctl" ["--user", "start", "synergy-rtunnel@" ++ host ++ ".service"] | ||
421 | synergyStop host = safeSpawn "systemctl" ["--user", "stop", "synergy-rtunnel@" ++ host ++ ".service"] | ||
422 | |||
423 | hostFromName _ = defaultHost | ||
424 | |||
425 | -- muteRef :: IORef (Maybe (String, Notification)) | ||
426 | -- {-# NOINLINE muteRef #-} | ||
427 | -- muteRef = unsafePerformIO $ newIORef Nothing | ||
428 | |||
429 | -- startMute, stopMute :: String -> X () | ||
430 | -- startMute sink = liftIO $ do | ||
431 | -- muted <- isJust <$> readIORef muteRef | ||
432 | -- when (not muted) $ do | ||
433 | -- let | ||
434 | -- notification = Notify.summary "Muted" <> Notify.timeout Infinite <> Notify.urgency Normal | ||
435 | -- level = "0.0dB" | ||
436 | -- -- level <- runProcessWithInput "ssh" ["bragi", "cat", "/dev/shm/mix/" ++ sink ++ "/level"] "" | ||
437 | -- -- callProcess "ssh" ["bragi", "adjmix", "-t", sink, "-o", "0"] | ||
438 | -- hPutStrLn stderr "Mute" | ||
439 | -- writeIORef muteRef . Just . (level, ) =<< Notify.display notification | ||
440 | -- stopMute sink = liftIO $ do | ||
441 | -- let | ||
442 | -- unmute (Just (level, notification)) = do | ||
443 | -- hPutStrLn stderr "Unmute" | ||
444 | -- -- callProcess "ssh" ["bragi", "adjmix", "-t", sink, "-o", level] | ||
445 | -- Notify.close notification | ||
446 | -- unmute Nothing = return () | ||
447 | -- muted <- isJust <$> readIORef muteRef | ||
448 | -- when muted . join . atomicModifyIORef muteRef $ (Nothing, ) . unmute | ||
449 | |||
450 | wspFromMap workspaceNames = \i -> case Map.lookup i workspaceNames of | ||
451 | Just str -> show i ++ " " ++ str | ||
452 | Nothing -> show i | ||
453 | |||
454 | coWspFromMap workspaceNames = \str -> case filter ((== str) . snd) $ Map.toList workspaceNames of | ||
455 | [] -> Nothing | ||
456 | [(i, _)] -> Just $ wspFromMap workspaceNames i | ||
457 | _ -> Nothing | ||
458 | |||
459 | spawnModifiers = [0, controlMask, shiftMask .|. controlMask] | ||
460 | spawnBindings :: XConfig layout -> (KeySym, [X ()]) -> [((KeyMask, KeySym), X ())] | ||
461 | spawnBindings conf (k, cmds) = zipWith (\m cmd -> ((modm .|. mod1Mask .|. m, k), cmd)) spawnModifiers cmds | ||
462 | where | ||
463 | modm = XMonad.modMask conf | ||
464 | |||
465 | manageScratchTerm = (resource =? "scratchpad" <||> resource =? "keysetup") -?> doRectFloat $ RationalRect (1 % 16) (1 % 16) (7 % 8) (7 % 8) | ||
466 | |||
467 | tabbedLayout t = renamed [Replace "Tabbed"] $ reflectHoriz $ t CustomShrink $ tabbedTheme | ||
468 | tabbedLayoutHoriz t = renamed [Replace "Tabbed Horiz"] $ reflectVert $ t CustomShrink $ tabbedTheme | ||
469 | tabbedTheme = def | ||
470 | { activeColor = "black" | ||
471 | , inactiveColor = "black" | ||
472 | , urgentColor = "black" | ||
473 | , activeBorderColor = gray | ||
474 | , inactiveBorderColor = darkGray | ||
475 | , urgentBorderColor = red | ||
476 | , activeTextColor = gray | ||
477 | , inactiveTextColor = gray | ||
478 | , urgentTextColor = gray | ||
479 | , decoHeight = 32 | ||
480 | , fontName = "xft:Fira Sans:pixelsize=21" | ||
481 | } | ||
482 | |||
483 | main :: IO () | ||
484 | main = do | ||
485 | arguments <- either (const []) id <$> tryIOError getArgs | ||
486 | case arguments of | ||
487 | ["--command", s] -> do | ||
488 | d <- openDisplay "" | ||
489 | rw <- rootWindow d $ defaultScreen d | ||
490 | a <- internAtom d "XMONAD_COMMAND" False | ||
491 | m <- internAtom d s False | ||
492 | allocaXEvent $ \e -> do | ||
493 | setEventType e clientMessage | ||
494 | setClientMessageEvent e rw a 32 m currentTime | ||
495 | sendEvent d rw False structureNotifyMask e | ||
496 | sync d False | ||
497 | _ -> do | ||
498 | -- batteryMon <- xfork $ monitorBattery Nothing Nothing | ||
499 | hostname <- getHostName | ||
500 | let | ||
501 | host = hostFromName hostname | ||
502 | setEnv "HOST" hostname | ||
503 | let myConfig = withHostUrgency . ewmhFullscreen . ewmh . pagerHints $ docks def | ||
504 | { manageHook = hManageHook host | ||
505 | , terminal = "alacritty" | ||
506 | , layoutHook = smartBorders . avoidStruts $ windowNavigation layout' | ||
507 | , logHook = do | ||
508 | dynamicLogString xmobarPP' >>= writeProps | ||
509 | updatePointer (99 % 100, 98 % 100) (0, 0) | ||
510 | , modMask = mod4Mask | ||
511 | , keys = \conf -> hKeysMod host conf $ myKeys' conf host | ||
512 | , workspaces = take (length numKeys) $ map wsp [1..] | ||
513 | , startupHook = setDefaultCursor xC_left_ptr | ||
514 | , normalBorderColor = darkGray | ||
515 | , focusedBorderColor = gray | ||
516 | , handleEventHook = serverModeEventHookCmd' (hCmds host) <+> keyUpEventHook | ||
517 | } | ||
518 | writeProps str = do | ||
519 | let encodeCChar = map $ fromIntegral . fromEnum | ||
520 | atoms = [ "_XMONAD_WORKSPACES" | ||
521 | , "_XMONAD_LAYOUT" | ||
522 | , "_XMONAD_TITLE" | ||
523 | ] | ||
524 | (flip mapM_) (zip atoms (lines str)) $ \(atom', content) -> do | ||
525 | ustring <- getAtom "UTF8_STRING" | ||
526 | atom <- getAtom atom' | ||
527 | withDisplay $ \dpy -> io $ do | ||
528 | root <- rootWindow dpy $ defaultScreen dpy | ||
529 | changeProperty8 dpy root atom ustring propModeReplace $ encodeCChar content | ||
530 | sync dpy True | ||
531 | wsp = hWsp host | ||
532 | -- We can´t define per-host layout modifiers because we lack dependent types | ||
533 | layout' = onHost "skadhi" ( onWorkspace (wsp 1) (Full ||| withIM (1%5) (Title "Buddy List") tabbedLayout') $ | ||
534 | onWorkspace (wsp 10) Full $ | ||
535 | onWorkspace (wsp 2) (Full ||| tabbedLayout') $ | ||
536 | onWorkspace (wsp 5) tabbedLayout' $ | ||
537 | onWorkspace (wsp 8) (withIM (1%5) (Title "Friends") tabbedLayout') $ | ||
538 | defaultLayouts | ||
539 | ) $ | ||
540 | onHost "vali" ( onWorkspace (wsp 2) (Full ||| tabbedLayout' ||| combineTwo (TwoPane 0.01 0.57) Full tabbedLayout') $ | ||
541 | onWorkspace (wsp 3) workLayouts $ | ||
542 | defaultLayouts | ||
543 | ) $ | ||
544 | onHost "hel" ( onWorkspace (wsp 1) (withIM (1 % 8) (Title "Buddy List") $ trackFloating tabbedLayout') $ | ||
545 | onWorkspace (wsp 2) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $ | ||
546 | onWorkspace (wsp 3) workLayouts $ | ||
547 | onWorkspace (wsp 6) workLayouts $ | ||
548 | onWorkspace (wsp 4) (tabbedLayout' ||| tabbedLayoutHoriz' ||| Dwindle R CW 1 (5 % 100)) $ | ||
549 | onWorkspace (wsp 5) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $ | ||
550 | onWorkspace (wsp 10) (tabbedLayout''' ||| combineTwoP (TwoPane (1 % 100) (3 % 4)) tabbedLayout''' tabbedLayout''' (ClassName "mpv") ||| Dwindle R CW 1 (5 % 100)) $ | ||
551 | defaultLayouts | ||
552 | ) $ | ||
553 | onHost "sif" ( onWorkspace (wsp 1) (withIM (1 % 8) (Title "Buddy List") $ trackFloating tabbedLayout') $ | ||
554 | onWorkspace (wsp 2) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $ | ||
555 | onWorkspace (wsp 3) workLayouts $ | ||
556 | onWorkspace (wsp 6) workLayouts $ | ||
557 | onWorkspace (wsp 4) (tabbedLayout' ||| tabbedLayoutHoriz' ||| Dwindle R CW 1 (5 % 100)) $ | ||
558 | onWorkspace (wsp 5) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $ | ||
559 | onWorkspace (wsp 8) tabbedLayout''' $ | ||
560 | onWorkspace (wsp 10) (tabbedLayout''' ||| combineTwoP (TwoPane (1 % 100) (3 % 4)) tabbedLayout''' tabbedLayout''' (ClassName "mpv") ||| Dwindle R CW 1 (5 % 100)) $ | ||
561 | defaultLayouts | ||
562 | ) $ | ||
563 | defaultLayouts | ||
564 | -- tabbedLayout''' = renamed [Replace "Tabbed'"] $ IfMax 1 (noBorders Full) (tabbedLayout tabbedBottomAlways) | ||
565 | tabbedLayout''' = tabbedLayout tabbedBottom | ||
566 | tabbedLayout' = tabbedLayout tabbedBottomAlways | ||
567 | tabbedLayoutHoriz' = tabbedLayoutHoriz tabbedLeftAlways | ||
568 | defaultLayouts = {- spiralWithDir East CW (1 % 2) -} Dwindle R CW 1 (5 % 100) ||| tabbedLayout' ||| Full | ||
569 | -- workLayouts = {- spiralWithDir East CW (1 % 2) -} Dwindle R CW (2 % 1) (5 % 100) ||| tabbedLayout' ||| Full | ||
570 | workLayouts = tabbedLayout' ||| (renamed [Replace "Combined"] $ combineTwoP (TwoPane (1 % 100) (1891 % 2560)) tabbedLayout''' (Column 1.6) (ClassName "Postman" `Or` ClassName "Emacs" `Or` ClassName "jetbrains-idea-ce" `Or` (Resource "Devtools" `And` ClassName "Firefox"))) ||| Full ||| Dwindle R CW 1 (5 % 100) | ||
571 | sqrtTwo = approxRational (sqrt 2) (1 / 2560) | ||
572 | xmobarPP' = xmobarPP { ppTitle = shorten 80 | ||
573 | , ppSort = (liftM2 (.)) getSortByIndex $ return scratchpadFilterOutWorkspace | ||
574 | , ppUrgent = wrap "(" ")" . xmobarColor "#800000" "" | ||
575 | , ppHiddenNoWindows = xmobarColor "#202020" "" . wrap "(" ")" | ||
576 | , ppVisible = wrap "(" ")" . xmobarColor "#808000" "" | ||
577 | , ppCurrent = wrap "(" ")" . xmobarColor "#008000" "" | ||
578 | , ppHidden = wrap "(" ")" | ||
579 | , ppWsSep = " " | ||
580 | , ppSep = "\n" | ||
581 | } | ||
582 | withHostUrgency = case hostname of | ||
583 | "sif" -> withUrgencyHookC urgencyHook' $ def { suppressWhen = U.Never, remindWhen = Every 2 } | ||
584 | _ -> id | ||
585 | urgencyHook' window = do | ||
586 | let blinkLight = (lightHigh >> threadDelay 0.5e6) `finally` lightLow | ||
587 | where | ||
588 | lightHigh = | ||
589 | writeFile "/sys/class/leds/input0::capslock/brightness" =<< readFile "/sys/class/leds/input0::capslock/max_brightness" | ||
590 | lightLow = writeFile "/sys/class/leds/input0::capslock/brightness" "0" | ||
591 | runQuery ((resource =? "comm" <||> resource =? "Pidgin" <||> className =? "Gajim" <||> className =? "Skype" <||> className =? "Thunderbird") --> void (xfork blinkLight)) window | ||
592 | urgencyHook (BorderUrgencyHook { urgencyBorderColor = red }) window | ||
593 | shutdown :: SomeException -> IO a | ||
594 | shutdown e = do | ||
595 | let pids = [ -- batteryMon | ||
596 | ] | ||
597 | mapM_ (signalProcess sigTERM) pids | ||
598 | mapM_ (getProcessStatus False False) pids | ||
599 | throw e | ||
600 | keyUpEventHook :: Event -> X All | ||
601 | keyUpEventHook event = handle event >> return (All True) | ||
602 | where | ||
603 | handle (KeyEvent { ev_event_type = t, ev_state = m, ev_keycode = code }) | ||
604 | | t == keyRelease = withDisplay $ \dpy -> do | ||
605 | s <- io $ keycodeToKeysym dpy code 0 | ||
606 | mClean <- cleanMask m | ||
607 | ks <- asks $ hKeyUpKeys host . config | ||
608 | userCodeDef () $ whenJust (Map.lookup (mClean, s) ks) id | ||
609 | | otherwise = return () | ||
610 | handle _ = return () | ||
611 | handle shutdown $ launch myConfig =<< getDirectories | ||
612 | |||
613 | secs :: Int -> Int | ||
614 | secs = (* 1000000) | ||
615 | |||
616 | -- monitorBattery :: Maybe BatteryContext -> Maybe Notification -> IO () | ||
617 | -- monitorBattery Nothing n = do | ||
618 | -- ctx <- batteryContextNew | ||
619 | -- case ctx of | ||
620 | -- Nothing -> threadDelay (secs 10) >> monitorBattery Nothing n | ||
621 | -- Just _ -> monitorBattery ctx n | ||
622 | -- monitorBattery ctx@(Just ctx') n = do | ||
623 | -- batInfo <- getBatteryInfo ctx' | ||
624 | -- case batInfo of | ||
625 | -- Nothing -> threadDelay (secs 1) >> monitorBattery ctx n | ||
626 | -- Just batInfo -> do | ||
627 | -- let n' | ||
628 | -- | batteryState batInfo == BatteryStateDischarging | ||
629 | -- , timeLeft <= 1200 | ||
630 | -- , timeLeft > 0 = Just $ summary "Discharging" <> hint "value" percentage <> urgency u <> body (duz timeLeft ++ "left") | ||
631 | -- | otherwise = Nothing | ||
632 | -- u | ||
633 | -- | timeLeft <= 600 = Critical | ||
634 | -- | timeLeft <= 1800 = Normal | ||
635 | -- | otherwise = Low | ||
636 | -- timeLeft = batteryTimeToEmpty batInfo | ||
637 | -- percentage :: Int32 | ||
638 | -- percentage = round $ batteryPercentage batInfo | ||
639 | -- ts = [("s", 60), ("m", 60), ("h", 24), ("d", 365), ("y", 1)] | ||
640 | -- duz ms = ss | ||
641 | -- where (ss, _) = foldl (\(ss, x) (s, y) -> ((if rem x y > 0 then show (rem x y) ++ s ++ " " else "") ++ ss , quot x y)) ("", ms) ts | ||
642 | -- case n' of | ||
643 | -- Just n' -> Notify.display (maybe mempty reuse n <> Notify.appName "monitorBattery" <> n') >>= (\n -> threadDelay (secs 2) >> monitorBattery ctx (Just n)) | ||
644 | -- Nothing -> threadDelay (secs 30) >> monitorBattery ctx n | ||
645 | |||
646 | disableTouchpad, disableTrackpoint, enableTrackpoint, enableTouchpad :: X () | ||
647 | enableTouchpad = safeSpawn "xinput" ["enable", "SynPS/2 Synaptics TouchPad"] | ||
648 | disableTouchpad = safeSpawn "xinput" ["disable", "SynPS/2 Synaptics TouchPad"] | ||
649 | enableTrackpoint = safeSpawn "xinput" ["enable", "TPPS/2 IBM TrackPoint"] | ||
650 | disableTrackpoint = safeSpawn "xinput" ["disable", "TPPS/2 IBM TrackPoint"] | ||
651 | |||
652 | isDisabled :: String -> X Bool | ||
653 | isDisabled str = do | ||
654 | out <- runProcessWithInput "xinput" ["list", str] "" | ||
655 | return $ "disabled" `isInfixOf` out | ||
656 | |||
657 | |||
658 | spawnKeychain :: X () | ||
659 | spawnKeychain = do | ||
660 | home <- liftIO getHomeDirectory | ||
661 | let keys = (map ((home </>) . (".ssh/" ++)) ["id", "id-rsa"]) ++ ["6B13AA67"] | ||
662 | liftIO (maybe (return ()) (setEnv "SSH_ASKPASS") =<< findAskpass) | ||
663 | safeSpawn "keychain" . (["--agents", "gpg,ssh"] ++)=<< liftIO (filterM doesFileExist keys) | ||
664 | where | ||
665 | findAskpass = filter `liftM` readFile "/etc/zshrc" | ||
666 | filter = listToMaybe . catMaybes . map (stripPrefix "export SSH_ASKPASS=") . lines | ||
667 | |||
668 | assimilateKeychain :: X () | ||
669 | assimilateKeychain = liftIO $ assimilateKeychain' >> return () | ||
670 | assimilateKeychain' = tryIOError $ do | ||
671 | -- pid <- getProcessID | ||
672 | -- tmpDir <- lookupEnv "TMPDIR" | ||
673 | -- let tmpDir' = fromMaybe "/tmp" tmpDir | ||
674 | -- tmpFile = tmpDir' </> "xmonad-keychain" ++ (show pid) ++ ".env" | ||
675 | env <- runProcessWithInput "sh" ["-c", "eval $(keychain --eval --noask --agents gpg,ssh); env"] "" -- > " ++ tmpFile] "" | ||
676 | -- env <- readFile tmpFile | ||
677 | let envVars = Map.fromList $ map (\(k, v) -> (k, tail' v)) $ map (span (/= '=')) $ envLines | ||
678 | envVars' = Map.filterWithKey (\k _ -> k `elem` transfer) envVars | ||
679 | transfer = ["SSH_AUTH_SOCK", "SSH_AGENT_PID", "GPG_AGENT_INFO"] | ||
680 | envLines = filter (elem '=') $ lines env :: [String] | ||
681 | sequence $ map (\(k, c) -> setEnv k c) $ Map.toList envVars' | ||
682 | -- removeFile tmpFile | ||
683 | where | ||
684 | tail' [] = [] | ||
685 | tail' (x:xs) = xs | ||
686 | |||
687 | |||
688 | numKeys = [xK_parenleft, xK_parenright, xK_braceright, xK_plus, xK_braceleft, xK_bracketright, xK_bracketleft, xK_exclam, xK_equal, xK_asterisk] | ||
689 | |||
690 | instance Shrinker CustomShrink where | ||
691 | shrinkIt _ "" = [""] | ||
692 | shrinkIt s cs | ||
693 | | length cs >= 4 = cs : shrinkIt s ((reverse . drop 4 . reverse $ cs) ++ "...") | ||
694 | | otherwise = cs : shrinkIt s (init cs) | ||
695 | |||
696 | xPConfig, xPConfigMonospace :: XPConfig | ||
697 | xPConfig = def | ||
698 | { font = "xft:Fira Sans:pixelsize=21" | ||
699 | , height = 32 | ||
700 | , bgColor = "black" | ||
701 | , fgColor = gray | ||
702 | , fgHLight = green | ||
703 | , bgHLight = "black" | ||
704 | , borderColor = gray | ||
705 | , searchPredicate = (\needle haystack -> all (`isInfixOf` map toLower haystack) . map (map toLower) $ words needle) | ||
706 | , position = Top | ||
707 | } | ||
708 | xPConfigMonospace = xPConfig { font = "xft:Fira Code:pixelsize=21" } | ||
709 | |||
710 | sshOverrides host = map (\h -> mkOverride { oHost = h, oCommand = moshCmd . inTmux host} ) | ||
711 | [ "odin" | ||
712 | , "ymir" | ||
713 | , "surtr" | ||
714 | , "vidhar" | ||
715 | , "srv02.uniworx.de" | ||
716 | ] | ||
717 | ++ | ||
718 | map (\h -> mkOverride { oHost = h, oCommand = moshCmd' "/run/current-system/sw/bin/mosh-server" . withEnv [("TERM", "xterm")] . inTmux host} ) | ||
719 | [ "bragi", "bragi.asgard.yggdrasil" | ||
720 | ] | ||
721 | ++ | ||
722 | map (\h -> mkOverride { oHost = h, oCommand = sshCmd . inTmux host } ) | ||
723 | [ "uni2work-dev1", "srv01.uniworx.de" | ||
724 | ] | ||
725 | ++ | ||
726 | map (\h -> mkOverride { oHost = h, oCommand = sshCmd . withEnv [("TERM", "xterm")] . inTmux host } ) | ||
727 | [ "remote.cip.ifi.lmu.de" | ||
728 | , "uniworx3", "uniworx4", "uniworx5", "uniworxdb2" | ||
729 | , "testworx" | ||
730 | ] | ||
731 | |||
732 | backlight :: (Rational -> Rational) -> X () | ||
733 | backlight f = void . xfork . liftIO $ do | ||
734 | [ _device | ||
735 | , _class | ||
736 | , read . Text.unpack -> currentBright | ||
737 | , _currentPercentage | ||
738 | , read . Text.unpack -> maximumBright | ||
739 | ] <- Text.splitOn "," . Text.pack <$> readProcess "brightnessctl" ["-m"] "" | ||
740 | let current = currentBright % maximumBright | ||
741 | new' = f current * fromIntegral maximumBright | ||
742 | new :: Integer | ||
743 | new | floor new' < 0 = 0 | ||
744 | | ceiling new' > maximumBright = maximumBright | ||
745 | | new' >= maximumBright % 2 = ceiling new' | ||
746 | | otherwise = floor new' | ||
747 | callProcess "brightnessctl" ["-m", "s", show new] | ||
748 | |||
749 | cycleThrough :: [Rational] -> (Rational -> Rational) | ||
750 | cycleThrough opts current = fromMaybe currentOpt $ listToMaybe next' | ||
751 | where currentOpt = minimumBy (comparing $ abs . subtract current) opts | ||
752 | (_, _ : next') = break (== currentOpt) opts | ||
753 | |||
754 | cycleKbLayout :: [(String, Maybe String)] -> X () | ||
755 | cycleKbLayout [] = return () | ||
756 | cycleKbLayout layouts = liftIO $ do | ||
757 | next <- (getNext . extract) `liftM` runProcessWithInput "setxkbmap" ["-query"] "" | ||
758 | let | ||
759 | args = case next of | ||
760 | (l, Just v) -> [l, v] | ||
761 | (l, Nothing) -> [l] | ||
762 | safeSpawn "setxkbmap" args | ||
763 | where | ||
764 | extract :: String -> Maybe (String, Maybe String) | ||
765 | extract str = listToMaybe $ do | ||
766 | ["layout:", l] <- str' | ||
767 | [(l, Just v) | ["variant:", v] <- str'] ++ pure (l, Nothing) | ||
768 | where | ||
769 | str' = map words $ lines str | ||
770 | getNext :: Maybe (String, Maybe String) -> (String, Maybe String) | ||
771 | getNext = maybe (head layouts) getNext' | ||
772 | getNext' x = case elemIndex x layouts of | ||
773 | Nothing -> getNext Nothing | ||
774 | Just i -> layouts !! ((i + 1) `mod` length layouts) | ||
775 | |||
776 | mpvAll' :: MpvCommand -> IO [MpvResponse] | ||
777 | mpvAll' = mpvAll "/var/media/.mpv-ipc" | ||
778 | |||
779 | mpvOne' :: MpvCommand -> IO (Maybe MpvResponse) | ||
780 | mpvOne' = mpvOne "/var/media/.mpv-ipc" | ||
781 | |||
782 | mediaMpv :: MpvCommand -> X () | ||
783 | mediaMpv cmd = void . xfork $ print =<< mpvAll' cmd | ||
784 | |||
785 | mediaMpvTogglePause :: X () | ||
786 | mediaMpvTogglePause = void . xfork $ do | ||
787 | paused <- mapM mpvResponse <=< mpvAll' $ MpvGetProperty "pause" | ||
788 | if | ||
789 | | and paused -> print <=< mpvAll' $ MpvSetProperty "pause" False | ||
790 | | otherwise -> print <=< mpvOne' $ MpvSetProperty "pause" True | ||
791 | |||
792 | myKeys' conf host = Map.fromList $ | ||
793 | -- launch a terminal | ||
794 | [ ((modm, xK_Return), spawn $ (XMonad.terminal conf) ++ " -e tmux") | ||
795 | , ((modm .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf) | ||
796 | |||
797 | -- launch dmenu | ||
798 | --, ((modm, xK_d ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"") | ||
799 | , ((modm, xK_d ), shellPrompt "Run: " xPConfigMonospace) | ||
800 | , ((modm .|. shiftMask, xK_d ), prompt "Run in Terminal: " ("alacritty" ++ " -e") xPConfigMonospace) | ||
801 | , ((modm, xK_at ), sshPrompt (sshOverrides . Just $ hName host) xPConfigMonospace) | ||
802 | |||
803 | -- close focused window | ||
804 | , ((modm .|. shiftMask, xK_q ), kill) | ||
805 | , ((modm .|. controlMask .|. shiftMask, xK_q ), spawn "xkill") | ||
806 | |||
807 | -- Rotate through the available layout algorithms | ||
808 | , ((modm, xK_space ), sendMessage NextLayout) | ||
809 | |||
810 | -- Reset the layouts on the current workspace to default | ||
811 | , ((modm .|. controlMask, xK_r ), (setLayout $ XMonad.layoutHook conf) >> refresh) | ||
812 | |||
813 | -- Resize viewed windows to the correct size | ||
814 | , ((modm, xK_r ), refresh) | ||
815 | |||
816 | -- Move focus to the next window | ||
817 | , ((modm, xK_t ), windows W.focusDown) | ||
818 | |||
819 | -- Move focus to the previous window | ||
820 | , ((modm, xK_n ), windows W.focusUp ) | ||
821 | |||
822 | -- Move focus to the master window | ||
823 | , ((modm, xK_m ), windows W.focusMaster ) | ||
824 | |||
825 | -- Swap the focused window and the master window | ||
826 | , ((modm .|. shiftMask, xK_m ), windows W.swapMaster) | ||
827 | |||
828 | -- Swap the focused window with the next window | ||
829 | , ((modm .|. shiftMask, xK_t ), windows W.swapDown ) | ||
830 | |||
831 | -- Swap the focused window with the previous window | ||
832 | , ((modm .|. shiftMask, xK_n ), windows W.swapUp ) | ||
833 | |||
834 | -- Swap the focused window with the previous window | ||
835 | , ((modm .|. shiftMask .|. controlMask, xK_m), sendMessage SwapWindow) | ||
836 | |||
837 | , ((modm, xK_Right), sendMessage $ Go R) | ||
838 | , ((modm, xK_Left ), sendMessage $ Go L) | ||
839 | , ((modm, xK_Up ), sendMessage $ Go U) | ||
840 | , ((modm, xK_Down ), sendMessage $ Go D) | ||
841 | , ((modm .|. shiftMask , xK_Right), sendMessage $ Move R) | ||
842 | , ((modm .|. shiftMask , xK_Left ), sendMessage $ Move L) | ||
843 | , ((modm .|. shiftMask , xK_Up ), sendMessage $ Move U) | ||
844 | , ((modm .|. shiftMask , xK_Down ), sendMessage $ Move D) | ||
845 | -- , ((modm .|. controlMask, xK_Right), withFocused $ keysMoveWindow (10, 0)) | ||
846 | -- , ((modm .|. controlMask, xK_Left ), withFocused $ keysMoveWindow (-10, 0)) | ||
847 | -- , ((modm .|. controlMask, xK_Up ), withFocused $ keysMoveWindow (0, -10)) | ||
848 | -- , ((modm .|. controlMask, xK_Down ), withFocused $ keysMoveWindow (0, 10)) | ||
849 | -- Shrink the master area | ||
850 | , ((modm, xK_h ), sendMessage Shrink) | ||
851 | |||
852 | -- Expand the master area | ||
853 | , ((modm, xK_s ), sendMessage Expand) | ||
854 | |||
855 | -- Push window back into tiling | ||
856 | , ((modm .|. shiftMask, xK_space ), withFocused $ windows . W.sink) | ||
857 | , ((modm, xK_BackSpace), focusUrgent) | ||
858 | , ((modm .|. shiftMask, xK_BackSpace), clearUrgents) | ||
859 | |||
860 | -- Increment the number of windows in the master area | ||
861 | , ((modm , xK_comma ), sendMessage (IncMasterN 1)) | ||
862 | |||
863 | -- Deincrement the number of windows in the master area | ||
864 | , ((modm , xK_period), sendMessage (IncMasterN (-1))) | ||
865 | |||
866 | , ((0, xF86XK_AudioRaiseVolume), safeSpawn "pamixer" ["-i", "2"]) | ||
867 | , ((0, xF86XK_AudioLowerVolume), safeSpawn "pamixer" ["-d", "2"]) | ||
868 | , ((0, xF86XK_AudioMute), safeSpawn "pamixer" ["-t"]) | ||
869 | , ((0, xF86XK_AudioPause), mediaMpv $ MpvSetProperty "pause" False) | ||
870 | , ((0, {-xF86XK_AudioMicMute-} 269025202), safeSpawn "pulseaudio-ctl" ["mute-input"]) | ||
871 | , ((0, xF86XK_AudioPlay), mediaMpvTogglePause) | ||
872 | , ((0, xK_Print), do | ||
873 | home <- liftIO getHomeDirectory | ||
874 | unGrab | ||
875 | safeSpawn "scrot" ["-s", "-F", home </> "screenshots" </> "%Y-%m-%dT%H:%M:%S.png", "-e", "xclip -selection clipboard -t image/png -i $f"] | ||
876 | ) | ||
877 | , ((modm .|. mod1Mask, xK_space), mediaMpvTogglePause) | ||
878 | |||
879 | -- , ((0, xF86XK_MonBrightnessDown), backlight . cycleThrough $ reverse brCycle) | ||
880 | -- , ((0, xF86XK_MonBrightnessUp ), backlight $ cycleThrough brCycle) | ||
881 | , ((modm .|. shiftMask , xK_b), backlight . cycleThrough $ reverse brCycle) | ||
882 | , ((modm .|. shiftMask .|. controlMask, xK_b), backlight $ cycleThrough brCycle) | ||
883 | |||
884 | , ((modm , xK_Escape), cycleKbLayout (hKbLayouts host)) | ||
885 | , ((modm .|. controlMask, xK_Escape), safeSpawn "setxkbmap" $ fst (head $ hKbLayouts host) : maybeToList (snd . head $ hKbLayouts host)) | ||
886 | |||
887 | -- Toggle the status bar gap | ||
888 | -- Use this binding with avoidStruts from Hooks.ManageDocks. | ||
889 | -- See also the statusBar function from Hooks.DynamicLog. | ||
890 | -- | ||
891 | , ((modm , xK_b ), sendMessage ToggleStruts) | ||
892 | |||
893 | , ((modm .|. shiftMask, xK_p ), safeSpawn "playerctl" ["-a", "pause"]) | ||
894 | |||
895 | -- Quit xmonad | ||
896 | , ((modm .|. shiftMask, xK_e ), io (exitWith ExitSuccess)) | ||
897 | |||
898 | -- Restart xmonad | ||
899 | -- , ((modm .|. shiftMask .|. controlMask, xK_r ), void . xfork $ recompile False >>= flip when (safeSpawn "xmonad" ["--restart"])) | ||
900 | , ((modm .|. shiftMask, xK_r ), void . liftIO $ executeFile "xmonad" True [] Nothing) | ||
901 | , ((modm .|. shiftMask, xK_l ), void . xfork $ do | ||
902 | sessId <- getEnv "XDG_SESSION_ID" | ||
903 | safeSpawn "loginctl" ["lock-session", sessId] | ||
904 | ) | ||
905 | , ((modm .|. shiftMask, xK_s ), safeSpawn "systemctl" ["suspend"]) | ||
906 | , ((modm .|. shiftMask, xK_h ), inputPromptWithCompl xPConfigMonospace "systemctl" powerActCompl ?+ powerAct) | ||
907 | , ((modm, xK_v ), windows copyToAll) -- @@ Make focused window always visible | ||
908 | , ((modm .|. shiftMask, xK_v ), killAllOtherCopies) -- @@ Toggle window state back | ||
909 | , ((modm .|. shiftMask, xK_g ), windowPrompt xPConfig Goto wsWindows) | ||
910 | , ((modm , xK_g ), windowPrompt xPConfig Bring allWindows) | ||
911 | ] | ||
912 | ++ | ||
913 | |||
914 | -- | ||
915 | -- mod-[1..9], Switch to workspace N | ||
916 | -- | ||
917 | -- mod-[1..9], Switch to workspace N | ||
918 | -- mod-shift-[1..9], Move client to workspace N | ||
919 | -- | ||
920 | [((m .|. modm, k), windows $ f i) | ||
921 | | (i, k) <- zip (XMonad.workspaces conf) $ numKeys | ||
922 | , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)] | ||
923 | ] | ||
924 | ++ | ||
925 | [((m .|. modm .|. controlMask, k), void . runMaybeT $ | ||
926 | MaybeT (P.getScreen def i) >>= MaybeT . screenWorkspace >>= lift . windows . f | ||
927 | ) | ||
928 | | (i, k) <- zip (hScreens host) [xK_g, xK_c, xK_r, xK_l] | ||
929 | , (f, m) <- [(W.view, 0), (W.shift, shiftMask)] | ||
930 | ] | ||
931 | where | ||
932 | modm = XMonad.modMask conf | ||
933 | |||
934 | brCycle = [0, 1 % 500, 1 % 250, 1 % 100, 1 % 10, 1 % 4, 1 % 2, 3 % 4, 1] | ||
935 | |||
936 | powerActWords = ["poweroff", "reboot", "hibernate", "suspend"] | ||
937 | powerActCompl = mkComplFunFromList' xPConfigMonospace powerActWords | ||
938 | powerAct act | act `elem` powerActWords = safeSpawn "systemctl" $ pure act | ||
939 | | otherwise = return () | ||
diff --git a/accounts/gkleen@sif/zshrc b/accounts/gkleen@sif/zshrc index e3f675a1..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 | } |
@@ -306,20 +288,7 @@ done < <(find ~/projects ~/uni -regextype posix-extended -maxdepth 2 -type d -re | |||
306 | sed -zr 's|(.*/([0-9]{2}[ws])/(.+))|\1 \2 \3|' | \ | 288 | sed -zr 's|(.*/([0-9]{2}[ws])/(.+))|\1 \2 \3|' | \ |
307 | sort -z -r -k2 | sort -z -s -k3 | uniq -z -f 2) | 289 | sort -z -r -k2 | sort -z -s -k3 | uniq -z -f 2) |
308 | 290 | ||
309 | alias '..'='cd ..' | ||
310 | alias rzadm=$'tmpdir -i sh -c \'mkdir adm; sshfs gkleen@mgmt01:/adm adm\'' | 291 | alias rzadm=$'tmpdir -i sh -c \'mkdir adm; sshfs gkleen@mgmt01:/adm adm\'' |
311 | alias mathcloud=$'tmpdir -i rclone mount --daemon mathcloud:// .' | 292 | alias mathcloud=$'tmpdir -i rclone mount --daemon mathcloud:// .' |
312 | alias -g L='| less' | ||
313 | alias -g S='&> /dev/null' | ||
314 | alias -g G='| grep' | ||
315 | alias -g B='&> /dev/null &' | ||
316 | alias -g BB='&> /dev/null &!' | ||
317 | 293 | ||
318 | export DEFAULT_USER=gkleen | 294 | export DEFAULT_USER=gkleen |
319 | |||
320 | bindkey -e | ||
321 | bindkey ';5C' emacs-forward-word | ||
322 | bindkey ';5D' emacs-backward-word | ||
323 | bindkey '^[[1;5C' emacs-forward-word | ||
324 | bindkey '^[[1;5D' emacs-backward-word | ||
325 | bindkey '^H' backward-kill-word | ||
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": { |
@@ -202,11 +202,11 @@ | |||
202 | "flake-registry": { | 202 | "flake-registry": { |
203 | "flake": false, | 203 | "flake": false, |
204 | "locked": { | 204 | "locked": { |
205 | "lastModified": 1734450202, | 205 | "lastModified": 1744623129, |
206 | "narHash": "sha256-/3gigrEBFORQs6a8LL5twoHs7biu08y/8Xc5aQmk3b0=", | 206 | "narHash": "sha256-nlQTQrHqM+ywXN0evDXnYEV6z6WWZB5BFQ2TkXsduKw=", |
207 | "owner": "NixOS", | 207 | "owner": "NixOS", |
208 | "repo": "flake-registry", | 208 | "repo": "flake-registry", |
209 | "rev": "02fe640c9e117dd9d6a34efc7bcb8bd09c08111d", | 209 | "rev": "1322f33d5836ae757d2e6190239252cf8402acf6", |
210 | "type": "github" | 210 | "type": "github" |
211 | }, | 211 | }, |
212 | "original": { | 212 | "original": { |
@@ -322,11 +322,11 @@ | |||
322 | ] | 322 | ] |
323 | }, | 323 | }, |
324 | "locked": { | 324 | "locked": { |
325 | "lastModified": 1736014120, | 325 | "lastModified": 1746904907, |
326 | "narHash": "sha256-ZrI+mcuQfal5IfT4HsxVEiiFNAgV4qYh+B4/NyXxpAs=", | 326 | "narHash": "sha256-XYo6bwc7xwo4lO6a/D2ttYRN4yDmsAjyt5O1E0vOLDg=", |
327 | "owner": "gkleen", | 327 | "owner": "gkleen", |
328 | "repo": "home-manager", | 328 | "repo": "home-manager", |
329 | "rev": "99e8412a18eb7e0731aa2b77abeed00d6d1863ad", | 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": { |
@@ -359,11 +359,11 @@ | |||
359 | }, | 359 | }, |
360 | "impermanence": { | 360 | "impermanence": { |
361 | "locked": { | 361 | "locked": { |
362 | "lastModified": 1736688610, | 362 | "lastModified": 1737831083, |
363 | "narHash": "sha256-1Zl9xahw399UiZSJ9Vxs1W4WRFjO1SsNdVZQD4nghz0=", | 363 | "narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=", |
364 | "owner": "nix-community", | 364 | "owner": "nix-community", |
365 | "repo": "impermanence", | 365 | "repo": "impermanence", |
366 | "rev": "c64bed13b562fc3bb454b48773d4155023ac31b7", | 366 | "rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170", |
367 | "type": "github" | 367 | "type": "github" |
368 | }, | 368 | }, |
369 | "original": { | 369 | "original": { |
@@ -389,17 +389,19 @@ | |||
389 | "inputs": { | 389 | "inputs": { |
390 | "niri-stable": "niri-stable", | 390 | "niri-stable": "niri-stable", |
391 | "niri-unstable": "niri-unstable", | 391 | "niri-unstable": "niri-unstable", |
392 | "nixpkgs": "nixpkgs_2", | 392 | "nixpkgs": [ |
393 | "nixpkgs" | ||
394 | ], | ||
393 | "nixpkgs-stable": "nixpkgs-stable_2", | 395 | "nixpkgs-stable": "nixpkgs-stable_2", |
394 | "xwayland-satellite-stable": "xwayland-satellite-stable", | 396 | "xwayland-satellite-stable": "xwayland-satellite-stable", |
395 | "xwayland-satellite-unstable": "xwayland-satellite-unstable" | 397 | "xwayland-satellite-unstable": "xwayland-satellite-unstable" |
396 | }, | 398 | }, |
397 | "locked": { | 399 | "locked": { |
398 | "lastModified": 1736840959, | 400 | "lastModified": 1747638609, |
399 | "narHash": "sha256-po6B6ZkwtYI1BNIm5BR+JZ0HHKKNAnB+Dlr6BXCat3U=", | 401 | "narHash": "sha256-rPTN667tMqC1IQYgsnotVfXbVNbOzScxn0ontMkkSPk=", |
400 | "owner": "sodiboo", | 402 | "owner": "sodiboo", |
401 | "repo": "niri-flake", | 403 | "repo": "niri-flake", |
402 | "rev": "d775188e23f94b2f73e939414b808fc2b9ebad73", | 404 | "rev": "af697f3a8665c8d0770485a2e659ddde88430e3b", |
403 | "type": "github" | 405 | "type": "github" |
404 | }, | 406 | }, |
405 | "original": { | 407 | "original": { |
@@ -412,16 +414,16 @@ | |||
412 | "niri-stable": { | 414 | "niri-stable": { |
413 | "flake": false, | 415 | "flake": false, |
414 | "locked": { | 416 | "locked": { |
415 | "lastModified": 1736614405, | 417 | "lastModified": 1740117926, |
416 | "narHash": "sha256-AJ1rlgNOPb3/+DbS5hkhm21t6Oz8IgqLllwmZt0lyzk=", | 418 | "narHash": "sha256-mTTHA0RAaQcdYe+9A3Jx77cmmyLFHmRoZdd8RpWa+m8=", |
417 | "owner": "YaLTeR", | 419 | "owner": "YaLTeR", |
418 | "repo": "niri", | 420 | "repo": "niri", |
419 | "rev": "e05bc269e678ecf828b96ae79c991c13b00b38a5", | 421 | "rev": "b94a5db8790339cf9134873d8b490be69e02ac71", |
420 | "type": "github" | 422 | "type": "github" |
421 | }, | 423 | }, |
422 | "original": { | 424 | "original": { |
423 | "owner": "YaLTeR", | 425 | "owner": "YaLTeR", |
424 | "ref": "v25.01", | 426 | "ref": "v25.02", |
425 | "repo": "niri", | 427 | "repo": "niri", |
426 | "type": "github" | 428 | "type": "github" |
427 | } | 429 | } |
@@ -429,11 +431,11 @@ | |||
429 | "niri-unstable": { | 431 | "niri-unstable": { |
430 | "flake": false, | 432 | "flake": false, |
431 | "locked": { | 433 | "locked": { |
432 | "lastModified": 1736836910, | 434 | "lastModified": 1747635487, |
433 | "narHash": "sha256-jpyL3/lVeqbcXVOHoSPgXgIbJ9vZtiCDMSvZL0UyCgQ=", | 435 | "narHash": "sha256-za7ctGh4MaW1h5Drm1WtwNZxiXvQK9yXZAeeIyY9b2Q=", |
434 | "owner": "YaLTeR", | 436 | "owner": "YaLTeR", |
435 | "repo": "niri", | 437 | "repo": "niri", |
436 | "rev": "36076d5279f349a32814dea91ca8f4dee61d5b08", | 438 | "rev": "3f2b7e63ba15cf33475116d32e8b7d22208a8438", |
437 | "type": "github" | 439 | "type": "github" |
438 | }, | 440 | }, |
439 | "original": { | 441 | "original": { |
@@ -470,11 +472,11 @@ | |||
470 | ] | 472 | ] |
471 | }, | 473 | }, |
472 | "locked": { | 474 | "locked": { |
473 | "lastModified": 1736652904, | 475 | "lastModified": 1747540584, |
474 | "narHash": "sha256-8uolHABgroXqzs03QdulHp8H9e5kWQZnnhcda1MKbBM=", | 476 | "narHash": "sha256-cxCQ413JTUuRv9Ygd8DABJ1D6kuB/nTfQqC0Lu9C0ls=", |
475 | "owner": "Mic92", | 477 | "owner": "Mic92", |
476 | "repo": "nix-index-database", | 478 | "repo": "nix-index-database", |
477 | "rev": "271e5bd7c57e1f001693799518b10a02d1123b12", | 479 | "rev": "ec179dd13fb7b4c6844f55be91436f7857226dce", |
478 | "type": "github" | 480 | "type": "github" |
479 | }, | 481 | }, |
480 | "original": { | 482 | "original": { |
@@ -484,6 +486,27 @@ | |||
484 | "type": "github" | 486 | "type": "github" |
485 | } | 487 | } |
486 | }, | 488 | }, |
489 | "nix-monitored": { | ||
490 | "inputs": { | ||
491 | "nixpkgs": [ | ||
492 | "nixpkgs" | ||
493 | ] | ||
494 | }, | ||
495 | "locked": { | ||
496 | "lastModified": 1745680380, | ||
497 | "narHash": "sha256-Z8PknjkmIr/8ZCH+dmc2Pc+UltiOr7/oKg37PXuVvuU=", | ||
498 | "owner": "ners", | ||
499 | "repo": "nix-monitored", | ||
500 | "rev": "60f3baa4701d58eab86c2d1d9c3d7e820074d461", | ||
501 | "type": "github" | ||
502 | }, | ||
503 | "original": { | ||
504 | "owner": "ners", | ||
505 | "ref": "master", | ||
506 | "repo": "nix-monitored", | ||
507 | "type": "github" | ||
508 | } | ||
509 | }, | ||
487 | "nixVirt": { | 510 | "nixVirt": { |
488 | "inputs": { | 511 | "inputs": { |
489 | "nixpkgs": [ | 512 | "nixpkgs": [ |
@@ -491,11 +514,11 @@ | |||
491 | ] | 514 | ] |
492 | }, | 515 | }, |
493 | "locked": { | 516 | "locked": { |
494 | "lastModified": 1736736253, | 517 | "lastModified": 1747637556, |
495 | "narHash": "sha256-GrktftEfXmmdKOU0yz3QXckDz1ncZ+f4KLU8XnYKYuA=", | 518 | "narHash": "sha256-AYd1nE+BLWTZS8J0eFQ7kuNiuE+XjhhndoXinj7en/M=", |
496 | "owner": "AshleyYakeley", | 519 | "owner": "AshleyYakeley", |
497 | "repo": "NixVirt", | 520 | "repo": "NixVirt", |
498 | "rev": "9063243af5e6674359a0ff7cec57f02eeacf0cea", | 521 | "rev": "a7d3d3ae8b9a0cf3ec3cf504bb593df0618a6dbc", |
499 | "type": "github" | 522 | "type": "github" |
500 | }, | 523 | }, |
501 | "original": { | 524 | "original": { |
@@ -506,11 +529,11 @@ | |||
506 | }, | 529 | }, |
507 | "nixos-hardware": { | 530 | "nixos-hardware": { |
508 | "locked": { | 531 | "locked": { |
509 | "lastModified": 1736441705, | 532 | "lastModified": 1747129300, |
510 | "narHash": "sha256-OL7leZ6KBhcDF3nEKe4aZVfIm6xQpb1Kb+mxySIP93o=", | 533 | "narHash": "sha256-L3clA5YGeYCF47ghsI7Tcex+DnaaN/BbQ4dR2wzoiKg=", |
511 | "owner": "NixOS", | 534 | "owner": "NixOS", |
512 | "repo": "nixos-hardware", | 535 | "repo": "nixos-hardware", |
513 | "rev": "8870dcaff63dfc6647fb10648b827e9d40b0a337", | 536 | "rev": "e81fd167b33121269149c57806599045fd33eeed", |
514 | "type": "github" | 537 | "type": "github" |
515 | }, | 538 | }, |
516 | "original": { | 539 | "original": { |
@@ -628,11 +651,11 @@ | |||
628 | }, | 651 | }, |
629 | "nixpkgs-stable_2": { | 652 | "nixpkgs-stable_2": { |
630 | "locked": { | 653 | "locked": { |
631 | "lastModified": 1736684107, | 654 | "lastModified": 1747485343, |
632 | "narHash": "sha256-vH5mXxEvZeoGNkqKoCluhTGfoeXCZ1seYhC2pbMN0sg=", | 655 | "narHash": "sha256-YbsZyuRE1tobO9sv0PUwg81QryYo3L1F3R3rF9bcG38=", |
633 | "owner": "NixOS", | 656 | "owner": "NixOS", |
634 | "repo": "nixpkgs", | 657 | "repo": "nixpkgs", |
635 | "rev": "635e887b48521e912a516625eee7df6cf0eba9c1", | 658 | "rev": "9b5ac7ad45298d58640540d0323ca217f32a6762", |
636 | "type": "github" | 659 | "type": "github" |
637 | }, | 660 | }, |
638 | "original": { | 661 | "original": { |
@@ -676,11 +699,11 @@ | |||
676 | }, | 699 | }, |
677 | "nixpkgs_2": { | 700 | "nixpkgs_2": { |
678 | "locked": { | 701 | "locked": { |
679 | "lastModified": 1736701207, | 702 | "lastModified": 1747542820, |
680 | "narHash": "sha256-jG/+MvjVY7SlTakzZ2fJ5dC3V1PrKKrUEOEE30jrOKA=", | 703 | "narHash": "sha256-GaOZntlJ6gPPbbkTLjbd8BMWaDYafhuuYRNrxCGnPJw=", |
681 | "owner": "NixOS", | 704 | "owner": "NixOS", |
682 | "repo": "nixpkgs", | 705 | "repo": "nixpkgs", |
683 | "rev": "ed4a395ea001367c1f13d34b1e01aa10290f67d6", | 706 | "rev": "292fa7d4f6519c074f0a50394dbbe69859bb6043", |
684 | "type": "github" | 707 | "type": "github" |
685 | }, | 708 | }, |
686 | "original": { | 709 | "original": { |
@@ -692,22 +715,6 @@ | |||
692 | }, | 715 | }, |
693 | "nixpkgs_3": { | 716 | "nixpkgs_3": { |
694 | "locked": { | 717 | "locked": { |
695 | "lastModified": 1736798957, | ||
696 | "narHash": "sha256-qwpCtZhSsSNQtK4xYGzMiyEDhkNzOCz/Vfu4oL2ETsQ=", | ||
697 | "owner": "NixOS", | ||
698 | "repo": "nixpkgs", | ||
699 | "rev": "9abb87b552b7f55ac8916b6fc9e5cb486656a2f3", | ||
700 | "type": "github" | ||
701 | }, | ||
702 | "original": { | ||
703 | "owner": "NixOS", | ||
704 | "ref": "nixos-unstable", | ||
705 | "repo": "nixpkgs", | ||
706 | "type": "github" | ||
707 | } | ||
708 | }, | ||
709 | "nixpkgs_4": { | ||
710 | "locked": { | ||
711 | "lastModified": 1681303793, | 718 | "lastModified": 1681303793, |
712 | "narHash": "sha256-JEdQHsYuCfRL2PICHlOiH/2ue3DwoxUX7DJ6zZxZXFk=", | 719 | "narHash": "sha256-JEdQHsYuCfRL2PICHlOiH/2ue3DwoxUX7DJ6zZxZXFk=", |
713 | "owner": "NixOS", | 720 | "owner": "NixOS", |
@@ -762,11 +769,11 @@ | |||
762 | "treefmt-nix": "treefmt-nix" | 769 | "treefmt-nix": "treefmt-nix" |
763 | }, | 770 | }, |
764 | "locked": { | 771 | "locked": { |
765 | "lastModified": 1736774291, | 772 | "lastModified": 1743690424, |
766 | "narHash": "sha256-1rEUm7R93L8rltgyBzon2/lzIN2udC/Kd8nyvuDN6ps=", | 773 | "narHash": "sha256-cX98bUuKuihOaRp8dNV1Mq7u6/CQZWTPth2IJPATBXc=", |
767 | "owner": "nix-community", | 774 | "owner": "nix-community", |
768 | "repo": "poetry2nix", | 775 | "repo": "poetry2nix", |
769 | "rev": "499221030113adc5dea05886a1d7aa1cc3a315d1", | 776 | "rev": "ce2369db77f45688172384bbeb962bc6c2ea6f94", |
770 | "type": "github" | 777 | "type": "github" |
771 | }, | 778 | }, |
772 | "original": { | 779 | "original": { |
@@ -829,7 +836,7 @@ | |||
829 | "flake-compat": "flake-compat_4", | 836 | "flake-compat": "flake-compat_4", |
830 | "flake-utils": "flake-utils_2", | 837 | "flake-utils": "flake-utils_2", |
831 | "gitignore": "gitignore_3", | 838 | "gitignore": "gitignore_3", |
832 | "nixpkgs": "nixpkgs_4", | 839 | "nixpkgs": "nixpkgs_3", |
833 | "nixpkgs-stable": "nixpkgs-stable_4" | 840 | "nixpkgs-stable": "nixpkgs-stable_4" |
834 | }, | 841 | }, |
835 | "locked": { | 842 | "locked": { |
@@ -872,6 +879,52 @@ | |||
872 | "type": "gitlab" | 879 | "type": "gitlab" |
873 | } | 880 | } |
874 | }, | 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 | }, | ||
875 | "root": { | 928 | "root": { |
876 | "inputs": { | 929 | "inputs": { |
877 | "backup-utils": "backup-utils", | 930 | "backup-utils": "backup-utils", |
@@ -885,16 +938,20 @@ | |||
885 | "impermanence": "impermanence", | 938 | "impermanence": "impermanence", |
886 | "niri-flake": "niri-flake", | 939 | "niri-flake": "niri-flake", |
887 | "nix-index-database": "nix-index-database", | 940 | "nix-index-database": "nix-index-database", |
941 | "nix-monitored": "nix-monitored", | ||
888 | "nixVirt": "nixVirt", | 942 | "nixVirt": "nixVirt", |
889 | "nixos-hardware": "nixos-hardware", | 943 | "nixos-hardware": "nixos-hardware", |
890 | "nixpkgs": "nixpkgs_3", | 944 | "nixpkgs": "nixpkgs_2", |
891 | "nixpkgs-eostre": "nixpkgs-eostre", | 945 | "nixpkgs-eostre": "nixpkgs-eostre", |
892 | "nixpkgs-pgbackrest": "nixpkgs-pgbackrest", | 946 | "nixpkgs-pgbackrest": "nixpkgs-pgbackrest", |
893 | "nixpkgs-stable": "nixpkgs-stable_3", | 947 | "nixpkgs-stable": "nixpkgs-stable_3", |
894 | "nvfetcher": "nvfetcher", | 948 | "nvfetcher": "nvfetcher", |
895 | "poetry2nix": "poetry2nix", | 949 | "poetry2nix": "poetry2nix", |
896 | "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", | ||
897 | "sops-nix": "sops-nix", | 953 | "sops-nix": "sops-nix", |
954 | "uv2nix": "uv2nix", | ||
898 | "waybar": "waybar" | 955 | "waybar": "waybar" |
899 | } | 956 | } |
900 | }, | 957 | }, |
@@ -905,11 +962,11 @@ | |||
905 | ] | 962 | ] |
906 | }, | 963 | }, |
907 | "locked": { | 964 | "locked": { |
908 | "lastModified": 1736808430, | 965 | "lastModified": 1747603214, |
909 | "narHash": "sha256-wlgdf/n7bJMLBheqt1jmPoxJFrUP6FByKQFXuM9YvIk=", | 966 | "narHash": "sha256-lAblXm0VwifYCJ/ILPXJwlz0qNY07DDYdLD+9H+Wc8o=", |
910 | "owner": "Mic92", | 967 | "owner": "Mic92", |
911 | "repo": "sops-nix", | 968 | "repo": "sops-nix", |
912 | "rev": "553c7cb22fed19fd60eb310423fdc93045c51ba8", | 969 | "rev": "8d215e1c981be3aa37e47aeabd4e61bb069548fd", |
913 | "type": "github" | 970 | "type": "github" |
914 | }, | 971 | }, |
915 | "original": { | 972 | "original": { |
@@ -970,6 +1027,29 @@ | |||
970 | "type": "github" | 1027 | "type": "github" |
971 | } | 1028 | } |
972 | }, | 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 | }, | ||
973 | "waybar": { | 1053 | "waybar": { |
974 | "inputs": { | 1054 | "inputs": { |
975 | "flake-compat": [ | 1055 | "flake-compat": [ |
@@ -980,16 +1060,16 @@ | |||
980 | ] | 1060 | ] |
981 | }, | 1061 | }, |
982 | "locked": { | 1062 | "locked": { |
983 | "lastModified": 1734278650, | 1063 | "lastModified": 1747383113, |
984 | "narHash": "sha256-z9FiyHDbKC2nwfd/qsHCxLBEogzQj73zo85lW3zIlzY=", | 1064 | "narHash": "sha256-/YW7eOKU3gsNplxvUDpEj1LiXtcCENSFpS1c8kXSDWw=", |
985 | "owner": "gkleen", | 1065 | "owner": "gkleen", |
986 | "repo": "Waybar", | 1066 | "repo": "Waybar", |
987 | "rev": "5432f9c1697a8d2d3e1264a5ce820d7eac26e2c6", | 1067 | "rev": "919036587381595f15010ac95644992fe6d7343d", |
988 | "type": "github" | 1068 | "type": "github" |
989 | }, | 1069 | }, |
990 | "original": { | 1070 | "original": { |
991 | "owner": "gkleen", | 1071 | "owner": "gkleen", |
992 | "ref": "feat/privacy-ignore", | 1072 | "ref": "feat/niri-urgency", |
993 | "repo": "Waybar", | 1073 | "repo": "Waybar", |
994 | "type": "github" | 1074 | "type": "github" |
995 | } | 1075 | } |
@@ -997,16 +1077,16 @@ | |||
997 | "xwayland-satellite-stable": { | 1077 | "xwayland-satellite-stable": { |
998 | "flake": false, | 1078 | "flake": false, |
999 | "locked": { | 1079 | "locked": { |
1000 | "lastModified": 1730166465, | 1080 | "lastModified": 1739246919, |
1001 | "narHash": "sha256-nq7bouXQXaaPPo/E+Jbq+wNHnatD4dY8OxSrRqzvy6s=", | 1081 | "narHash": "sha256-/hBM43/Gd0/tW+egrhlWgOIISeJxEs2uAOIYVpfDKeU=", |
1002 | "owner": "Supreeeme", | 1082 | "owner": "Supreeeme", |
1003 | "repo": "xwayland-satellite", | 1083 | "repo": "xwayland-satellite", |
1004 | "rev": "a713cf46cb7db84a0d1b57c3a397c610cad3cf98", | 1084 | "rev": "44590a416d4a3e8220e19e29e0b6efe64a80315d", |
1005 | "type": "github" | 1085 | "type": "github" |
1006 | }, | 1086 | }, |
1007 | "original": { | 1087 | "original": { |
1008 | "owner": "Supreeeme", | 1088 | "owner": "Supreeeme", |
1009 | "ref": "v0.5", | 1089 | "ref": "v0.5.1", |
1010 | "repo": "xwayland-satellite", | 1090 | "repo": "xwayland-satellite", |
1011 | "type": "github" | 1091 | "type": "github" |
1012 | } | 1092 | } |
@@ -1014,11 +1094,11 @@ | |||
1014 | "xwayland-satellite-unstable": { | 1094 | "xwayland-satellite-unstable": { |
1015 | "flake": false, | 1095 | "flake": false, |
1016 | "locked": { | 1096 | "locked": { |
1017 | "lastModified": 1736487362, | 1097 | "lastModified": 1747111562, |
1018 | "narHash": "sha256-4kGoOA7FgK9N2mzS+TFEn41kUUNY6KwdiA/0rqlr868=", | 1098 | "narHash": "sha256-GAqhWoxaBIk0tgoecZPa8gTHDHxNc0JtlwWHZN2iOOo=", |
1019 | "owner": "Supreeeme", | 1099 | "owner": "Supreeeme", |
1020 | "repo": "xwayland-satellite", | 1100 | "repo": "xwayland-satellite", |
1021 | "rev": "8f55e27f63a749881c4bbfbb6b1da028342a91d1", | 1101 | "rev": "ec9ff64c1e0cbec42710b580b7c0f759b1694e72", |
1022 | "type": "github" | 1102 | "type": "github" |
1023 | }, | 1103 | }, |
1024 | "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/privacy-ignore"; | 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"; |
@@ -189,6 +204,19 @@ | |||
189 | owner = "sodiboo"; | 204 | owner = "sodiboo"; |
190 | repo = "niri-flake"; | 205 | repo = "niri-flake"; |
191 | ref = "main"; | 206 | ref = "main"; |
207 | inputs = { | ||
208 | nixpkgs.follows = "nixpkgs"; | ||
209 | # niri-unstable.url = "github:gkleen/niri"; | ||
210 | }; | ||
211 | }; | ||
212 | nix-monitored = { | ||
213 | type = "github"; | ||
214 | owner = "ners"; | ||
215 | repo = "nix-monitored"; | ||
216 | ref = "master"; | ||
217 | inputs = { | ||
218 | nixpkgs.follows = "nixpkgs"; | ||
219 | }; | ||
192 | }; | 220 | }; |
193 | }; | 221 | }; |
194 | 222 | ||
@@ -275,9 +303,10 @@ | |||
275 | mkAccountModule = dir: path: accountName: | 303 | mkAccountModule = dir: path: accountName: |
276 | let | 304 | let |
277 | userName = accountUserName accountName; | 305 | userName = accountUserName accountName; |
306 | hostName = accountHostName accountName; | ||
278 | in overrideModule | 307 | in overrideModule |
279 | (import (dir + "/${path}")) | 308 | (import (dir + "/${path}")) |
280 | (inputs: inputs // { inherit userName; }) | 309 | (inputs: inputs // { inherit userName hostName; }) |
281 | (outputs: { _file = dir + "/${path}"; } | 310 | (outputs: { _file = dir + "/${path}"; } |
282 | // outputs | 311 | // outputs |
283 | // { imports = [self.nixosModules.users.${userName} or ({...}: { imports = defaultUserProfiles userName; })] ++ (outputs.imports or []); }); | 312 | // { imports = [self.nixosModules.users.${userName} or ({...}: { imports = defaultUserProfiles userName; })] ++ (outputs.imports or []); }); |
@@ -293,7 +322,7 @@ | |||
293 | forAllUsers = genAttrs (unique (map accountUserName (attrNames self.nixosModules.accounts))); | 322 | forAllUsers = genAttrs (unique (map accountUserName (attrNames self.nixosModules.accounts))); |
294 | 323 | ||
295 | activateNixosConfigurations = forAllSystems (system: _pkgs: filterAttrs (_n: v: v != null) (mapAttrs' (hostName: nixosConfig: nameValuePair "${hostName}-activate" (if system == nixosConfig.config.nixpkgs.system then { type = "app"; program = "${nixosConfig.config.system.build.toplevel}/bin/switch-to-configuration"; } else null)) self.nixosConfigurations)); | 324 | activateNixosConfigurations = forAllSystems (system: _pkgs: filterAttrs (_n: v: v != null) (mapAttrs' (hostName: nixosConfig: nameValuePair "${hostName}-activate" (if system == nixosConfig.config.nixpkgs.system then { type = "app"; program = "${nixosConfig.config.system.build.toplevel}/bin/switch-to-configuration"; } else null)) self.nixosConfigurations)); |
296 | startVMs = forAllSystems (system: pkgs: mapAttrs' (hostName: nixosConfig: nameValuePair "run-${hostName}-vm" { type = "app"; program = "${nixosConfig.config.system.build.vm}/bin/run-${hostName}-vm"; }) (nixImport rec { dir = ./hosts; _import = mkNixosConfiguration [ { config.virtualisation.host.pkgs = pkgs; } ] dir; })); | 325 | # startVMs = forAllSystems (system: pkgs: mapAttrs' (hostName: nixosConfig: nameValuePair "run-${hostName}-vm" { type = "app"; program = "${nixosConfig.config.system.build.vm}/bin/run-${hostName}-vm"; }) (nixImport rec { dir = ./hosts; _import = mkNixosConfiguration [ { config.virtualisation.host.pkgs = pkgs; } ] dir; })); |
297 | activateHomeManagerConfigurations = forAllSystems (system: _pkgs: filterAttrs (_n: v: v != null) (listToAttrs (concatLists (mapAttrsToList (hostName: nixosConfig: mapAttrsToList (userName: userCfg: nameValuePair "${userName}@${hostName}-activate" (if system == nixosConfig.config.nixpkgs.system then { type = "app"; program = "${userCfg.home.activationPackage}/activate"; } else null)) nixosConfig.config.home-manager.users) self.nixosConfigurations)))); | 326 | activateHomeManagerConfigurations = forAllSystems (system: _pkgs: filterAttrs (_n: v: v != null) (listToAttrs (concatLists (mapAttrsToList (hostName: nixosConfig: mapAttrsToList (userName: userCfg: nameValuePair "${userName}@${hostName}-activate" (if system == nixosConfig.config.nixpkgs.system then { type = "app"; program = "${userCfg.home.activationPackage}/activate"; } else null)) nixosConfig.config.home-manager.users) self.nixosConfigurations)))); |
298 | installerShells = system: pkgs: mapAttrs (installerName: config: pkgs.callPackage ./installer/shell.nix { | 327 | installerShells = system: pkgs: mapAttrs (installerName: config: pkgs.callPackage ./installer/shell.nix { |
299 | inherit system installerName config; | 328 | inherit system installerName config; |
@@ -330,18 +359,23 @@ | |||
330 | nixosConfigurations = installerNixosConfigurations // nixImport rec { dir = ./hosts; _import = mkNixosConfiguration [] dir; }; | 359 | nixosConfigurations = installerNixosConfigurations // nixImport rec { dir = ./hosts; _import = mkNixosConfiguration [] dir; }; |
331 | 360 | ||
332 | homeModules = nixImport rec { dir = ./home-modules; }; | 361 | homeModules = nixImport rec { dir = ./home-modules; }; |
333 | homeConfigurations = listToAttrs (concatLists (mapAttrsToList (hostname: nixosConfig: mapAttrsToList (username: configuration: nameValuePair "${username}@${hostname}" { inherit (configuration.home) activationPackage; inherit (configuration) home-files; }) nixosConfig.config.home-manager.users) self.nixosConfigurations)); | 362 | homeConfigurations = listToAttrs (concatLists (mapAttrsToList (hostname: nixosConfig: mapAttrsToList (username: nameValuePair "${username}@${hostname}") nixosConfig.config.home-manager.users) self.nixosConfigurations)); |
334 | 363 | ||
335 | overlays = mapAttrs (_name: path: mkOverlay path) overlayPaths; | 364 | overlays = mapAttrs (_name: path: mkOverlay path) overlayPaths; |
336 | 365 | ||
337 | 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); }); |
338 | 367 | ||
339 | # packages = mapAttrs (_name: filterAttrs (_name: isDerivation)) packages; | 368 | # packages = mapAttrs (_name: filterAttrs (_name: isDerivation)) packages; |
340 | # packages' = mapAttrs (_name: filterAttrs (_name: value: !(isDerivation value))) packages; | 369 | # packages' = mapAttrs (_name: filterAttrs (_name: value: !(isDerivation value))) packages; |
341 | 370 | ||
342 | legacyPackages = forAllSystems (system: systemPkgs: systemPkgs.override { overlays = attrValues self.overlays; }); | 371 | legacyPackages = forAllSystems (system: systemPkgs: systemPkgs.override { overlays = attrValues self.overlays; }); |
343 | 372 | ||
344 | apps = foldr recursiveUpdate {} [startVMs activateNixosConfigurations activateHomeManagerConfigurations]; | 373 | apps = foldr recursiveUpdate {} [ |
374 | #startVMs | ||
375 | activateNixosConfigurations activateHomeManagerConfigurations | ||
376 | ]; | ||
377 | |||
378 | lib = nixImport rec { dir = ./lib; _import = name: _base: import (dir + "/${name}") inputs; }; | ||
345 | 379 | ||
346 | 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); |
347 | 381 | ||
@@ -366,10 +400,10 @@ | |||
366 | # path = activateHomeManager (self.nixosConfigurations.${hostname}.config.nixpkgs.system) usercfg.home; | 400 | # path = activateHomeManager (self.nixosConfigurations.${hostname}.config.nixpkgs.system) usercfg.home; |
367 | # }) self.nixosConfigurations.${hostname}.config.home-manager.users); | 401 | # }) self.nixosConfigurations.${hostname}.config.home-manager.users); |
368 | }) (nixImport { dir = ./hosts; _import = (_path: name: name); }); | 402 | }) (nixImport { dir = ./hosts; _import = (_path: name: name); }); |
369 | 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 {}; |
370 | 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); |
371 | 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)); |
372 | 406 | ||
373 | checks = builtins.mapAttrs (system: deployLib: deployLib.deployChecks self.deploy) deploy-rs.lib; | 407 | # checks = builtins.mapAttrs (system: deployLib: deployLib.deployChecks self.deploy) deploy-rs.lib; |
374 | }; | 408 | }; |
375 | } | 409 | } |
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/home-modules/pandoc/default.nix b/home-modules/pandoc/default.nix new file mode 100644 index 00000000..1d16b621 --- /dev/null +++ b/home-modules/pandoc/default.nix | |||
@@ -0,0 +1,27 @@ | |||
1 | { pkgs, lib, config, ... }: | ||
2 | |||
3 | let | ||
4 | cfg = config.programs.pandoc; | ||
5 | in { | ||
6 | options.programs.pandoc = { | ||
7 | germanAbbreviations = lib.mkEnableOption "importing german abbreviations" // { default = true; }; | ||
8 | extraAbbreviations = lib.mkOption { | ||
9 | type = lib.types.listOf lib.types.str; | ||
10 | default = []; | ||
11 | }; | ||
12 | }; | ||
13 | |||
14 | config = lib.mkIf cfg.enable { | ||
15 | xdg.dataFile = lib.mkIf (cfg.germanAbbreviations || cfg.extraAbbreviations != []) { | ||
16 | "pandoc/abbreviations".source = pkgs.runCommand "pandoc-abbreviations" { | ||
17 | buildInputs = [ pkgs.coreutils ]; | ||
18 | } '' | ||
19 | cat \ | ||
20 | <(${lib.getExe' cfg.finalPackage "pandoc"} --print-default-data-file=abbreviations) \ | ||
21 | ${lib.optionalString cfg.germanAbbreviations ./german_abbreviations.txt} \ | ||
22 | ${lib.optionalString (cfg.extraAbbreviations != []) (pkgs.writeText "abbrevs.txt" (lib.concatStringsSep "\n" cfg.extraAbbreviations))} \ | ||
23 | | sort | uniq >$out | ||
24 | ''; | ||
25 | }; | ||
26 | }; | ||
27 | } | ||
diff --git a/home-modules/pandoc/german_abbreviations.txt b/home-modules/pandoc/german_abbreviations.txt new file mode 100644 index 00000000..fa4c9c87 --- /dev/null +++ b/home-modules/pandoc/german_abbreviations.txt | |||
@@ -0,0 +1,1423 @@ | |||
1 | &c. | ||
2 | A. | ||
3 | a. | ||
4 | a.a.O. | ||
5 | A.C.A.B. | ||
6 | a.D. | ||
7 | a.d.D. | ||
8 | a.g.O. | ||
9 | Abb. | ||
10 | abchas. | ||
11 | abds. | ||
12 | Abf. | ||
13 | Abfr. | ||
14 | Abg. | ||
15 | abgek. | ||
16 | abh. | ||
17 | Abh. | ||
18 | Abk. | ||
19 | ABl. | ||
20 | Abl. | ||
21 | Abm. | ||
22 | abn. | ||
23 | Abn. | ||
24 | abr. | ||
25 | Abr. | ||
26 | Abs. | ||
27 | abs. | ||
28 | Abschn. | ||
29 | Abst. | ||
30 | Abt. | ||
31 | abulg. | ||
32 | abw. | ||
33 | abwert. | ||
34 | abzgl. | ||
35 | accel. | ||
36 | accresc. | ||
37 | Add. | ||
38 | Adj. | ||
39 | adj. | ||
40 | Adr. | ||
41 | adv. | ||
42 | Adv. | ||
43 | adyg. | ||
44 | ae. | ||
45 | aengl. | ||
46 | afghan. | ||
47 | afr. | ||
48 | afranz. | ||
49 | afranzös. | ||
50 | afries. | ||
51 | afrik. | ||
52 | afrk. | ||
53 | afrs. | ||
54 | afrz. | ||
55 | afränk. | ||
56 | ags. | ||
57 | ahd. | ||
58 | Ahd. | ||
59 | aind. | ||
60 | air. | ||
61 | akad. | ||
62 | Akk. | ||
63 | akkad. | ||
64 | akt. | ||
65 | alb. | ||
66 | alban. | ||
67 | alem. | ||
68 | alemann. | ||
69 | all. | ||
70 | allg. | ||
71 | allj. | ||
72 | allm. | ||
73 | alltagsspr. | ||
74 | alphanum. | ||
75 | Alt. | ||
76 | altai. | ||
77 | altengl. | ||
78 | altfranz. | ||
79 | altfranzös. | ||
80 | altfrz. | ||
81 | altgr. | ||
82 | althochdt. | ||
83 | altis. | ||
84 | altisländ. | ||
85 | altpreuß. | ||
86 | altröm. | ||
87 | alttest. | ||
88 | alëut. | ||
89 | am. | ||
90 | amer. | ||
91 | amerik. | ||
92 | amerikan. | ||
93 | amhar. | ||
94 | amt. | ||
95 | amtl. | ||
96 | Amtm. | ||
97 | Amtsbl. | ||
98 | Amtsdt. | ||
99 | Amtsspr. | ||
100 | an. | ||
101 | anal. | ||
102 | anat. | ||
103 | Anat. | ||
104 | anatom. | ||
105 | andalus. | ||
106 | ang. | ||
107 | angelsächs. | ||
108 | Angest. | ||
109 | angest. | ||
110 | angloamerik. | ||
111 | anglofrz. | ||
112 | angloind. | ||
113 | Anh. | ||
114 | Ank. | ||
115 | Ankl. | ||
116 | Anl. | ||
117 | anl. | ||
118 | Anm. | ||
119 | Anm.d.Red. | ||
120 | Ann. | ||
121 | ann. | ||
122 | annamit. | ||
123 | anord. | ||
124 | Anord. | ||
125 | anschl. | ||
126 | Anschl. | ||
127 | Anschr. | ||
128 | antarkt. | ||
129 | Anthrop. | ||
130 | anthrop. | ||
131 | Anw. | ||
132 | aobd. | ||
133 | apl. | ||
134 | Apostr. | ||
135 | App. | ||
136 | Apr. | ||
137 | apreuß. | ||
138 | ar. | ||
139 | arab. | ||
140 | aragon. | ||
141 | aram. | ||
142 | aran. | ||
143 | architekt. | ||
144 | archäol. | ||
145 | arg. | ||
146 | argent. | ||
147 | arkt. | ||
148 | armen. | ||
149 | Art. | ||
150 | Art.-Nr. | ||
151 | Artt. | ||
152 | as. | ||
153 | aserbaidsch. | ||
154 | aslaw. | ||
155 | assyr. | ||
156 | astron. | ||
157 | asächs. | ||
158 | At.-Gew. | ||
159 | attr. | ||
160 | Attr. | ||
161 | Aufl. | ||
162 | Aug. | ||
163 | Ausg. | ||
164 | ausgen. | ||
165 | Aussch. | ||
166 | ausschl. | ||
167 | Ausspr. | ||
168 | Ausst. | ||
169 | austral. | ||
170 | awar. | ||
171 | awest. | ||
172 | Az. | ||
173 | aztek. | ||
174 | b. | ||
175 | B. | ||
176 | Ba.-Wü. | ||
177 | bab. | ||
178 | babyl. | ||
179 | bair. | ||
180 | Bakt. | ||
181 | Bal. | ||
182 | balt. | ||
183 | baltoslaw. | ||
184 | Bankw. | ||
185 | bas. | ||
186 | baschk. | ||
187 | bask. | ||
188 | Bat. | ||
189 | bauf. | ||
190 | Bauw. | ||
191 | bay. | ||
192 | bayer. | ||
193 | bayr. | ||
194 | BayVBl. | ||
195 | Bd. | ||
196 | Bde. | ||
197 | Bed. | ||
198 | Begr. | ||
199 | begr. | ||
200 | beif. | ||
201 | beil. | ||
202 | Beil. | ||
203 | Bem. | ||
204 | ben. | ||
205 | berbersprachl. | ||
206 | Bergb. | ||
207 | berlin. | ||
208 | Berufsbez. | ||
209 | bes. | ||
210 | besch. | ||
211 | Beschl. | ||
212 | best. | ||
213 | Best.-Nr. | ||
214 | Betr. | ||
215 | betr. | ||
216 | Betriebswiss. | ||
217 | Bev. | ||
218 | Bez. | ||
219 | bez. | ||
220 | bezw. | ||
221 | Bf. | ||
222 | bfn. | ||
223 | Bg. | ||
224 | bgld. | ||
225 | Bgld. | ||
226 | Bhf. | ||
227 | Bib. | ||
228 | bibl. | ||
229 | bildl. | ||
230 | bildungsspr. | ||
231 | Biol. | ||
232 | biol. | ||
233 | Bj. | ||
234 | bl. | ||
235 | Bl. | ||
236 | Blk. | ||
237 | Bln. | ||
238 | Bodenk. | ||
239 | bot. | ||
240 | Bot. | ||
241 | Br.-M. | ||
242 | Br.-Mstr. | ||
243 | bras. | ||
244 | bret. | ||
245 | breton. | ||
246 | brit. | ||
247 | Brm. | ||
248 | brn. | ||
249 | Bruchz. | ||
250 | bsd. | ||
251 | Bsp. | ||
252 | bsplsw. | ||
253 | bspw. | ||
254 | BT-Drs. | ||
255 | Btl. | ||
256 | btto. | ||
257 | Bttr. | ||
258 | Buchw. | ||
259 | buddh. | ||
260 | bulg. | ||
261 | bulgar. | ||
262 | burjat. | ||
263 | burmes. | ||
264 | Bw. | ||
265 | byzant. | ||
266 | Bz. | ||
267 | bzb. | ||
268 | bzgl. | ||
269 | bzw. | ||
270 | böhm. | ||
271 | Börsenw. | ||
272 | C. | ||
273 | ca. | ||
274 | Carp. | ||
275 | Cb. | ||
276 | cf. | ||
277 | chakass. | ||
278 | chald. | ||
279 | chant. | ||
280 | chem. | ||
281 | Chem. | ||
282 | chilen. | ||
283 | chin. | ||
284 | Chr. | ||
285 | christl. | ||
286 | chron. | ||
287 | Chron. | ||
288 | Co. | ||
289 | Comp. | ||
290 | cresc. | ||
291 | D. | ||
292 | Dankb. | ||
293 | dankwtw. | ||
294 | das. | ||
295 | dass. | ||
296 | Dat. | ||
297 | dbzgl. | ||
298 | ders. | ||
299 | des. | ||
300 | desgl. | ||
301 | Dez. | ||
302 | dgl. | ||
303 | Di. | ||
304 | dial. | ||
305 | dichter. | ||
306 | dies. | ||
307 | dim. | ||
308 | Dim. | ||
309 | Dimin. | ||
310 | dimin. | ||
311 | Dipl. | ||
312 | Dipl.-Bibl. | ||
313 | Dipl.-Ing. | ||
314 | Dipl.-Kff. | ||
315 | Dipl.-Kffr. | ||
316 | Dipl.-Kfm. | ||
317 | Dipl.-Kfr. | ||
318 | Dipl.-Psych. | ||
319 | Dir. | ||
320 | Diss. | ||
321 | Do. | ||
322 | do. | ||
323 | Do.-Gge. | ||
324 | dominikan. | ||
325 | dor. | ||
326 | Doz. | ||
327 | Dr. | ||
328 | Drchf. | ||
329 | Drcks. | ||
330 | Dres. | ||
331 | Drs. | ||
332 | Drucks. | ||
333 | dt. | ||
334 | Dtl. | ||
335 | dto. | ||
336 | Dtzd. | ||
337 | dz. | ||
338 | Dz. | ||
339 | dän. | ||
340 | E. | ||
341 | ebd. | ||
342 | Ed. | ||
343 | ed. | ||
344 | ehem. | ||
345 | eidg. | ||
346 | eig. | ||
347 | eigtl. | ||
348 | Einf. | ||
349 | einh. | ||
350 | Einl. | ||
351 | einschl. | ||
352 | Einw. | ||
353 | Eisenb. | ||
354 | Elektrot. | ||
355 | elektrotechn. | ||
356 | em. | ||
357 | engl. | ||
358 | entspr. | ||
359 | erb. | ||
360 | erf. | ||
361 | erg. | ||
362 | Erg. | ||
363 | erk. | ||
364 | Erl. | ||
365 | erm. | ||
366 | Ers.-D. | ||
367 | ersch. | ||
368 | erschl. | ||
369 | Erschl. | ||
370 | Erschl.-Geb. | ||
371 | Erschw. | ||
372 | erschw. | ||
373 | Erstauff. | ||
374 | Erstausg. | ||
375 | Ertr. | ||
376 | Erw. | ||
377 | Erw.-Bldg. | ||
378 | erwähnw. | ||
379 | Erzb. | ||
380 | erzg. | ||
381 | erzgeb. | ||
382 | eskim. | ||
383 | estn. | ||
384 | etc. | ||
385 | Etg. | ||
386 | etrusk. | ||
387 | etw. | ||
388 | eur. | ||
389 | europ. | ||
390 | ev. | ||
391 | evang. | ||
392 | evtl. | ||
393 | Ew. | ||
394 | ewen. | ||
395 | ewenk. | ||
396 | exkl. | ||
397 | Expl. | ||
398 | Ez. | ||
399 | f. | ||
400 | F. | ||
401 | Fa. | ||
402 | fachspr. | ||
403 | Fachspr. | ||
404 | Fag. | ||
405 | Fam. | ||
406 | fam. | ||
407 | Febr. | ||
408 | fem. | ||
409 | ff. | ||
410 | Fig. | ||
411 | fig. | ||
412 | finanzmath. | ||
413 | finn. | ||
414 | finnougr. | ||
415 | Flgh. | ||
416 | fläm. | ||
417 | Fn. | ||
418 | fnhd. | ||
419 | folg. | ||
420 | Forts. | ||
421 | Fortstzg. | ||
422 | Fr. | ||
423 | fr. | ||
424 | fragm. | ||
425 | franz. | ||
426 | französ. | ||
427 | Frdf. | ||
428 | frdl. | ||
429 | frdsprlg. | ||
430 | Frfr. | ||
431 | frfr. | ||
432 | Frh. | ||
433 | Frhf. | ||
434 | Frhr. | ||
435 | fries. | ||
436 | friesl. | ||
437 | Frk. | ||
438 | Frl. | ||
439 | Frm. | ||
440 | frnhd. | ||
441 | Frspr. | ||
442 | frstl. | ||
443 | Frt. | ||
444 | frtr. | ||
445 | Frwk. | ||
446 | frz. | ||
447 | fränk. | ||
448 | frühnhd. | ||
449 | Fs. | ||
450 | Fsch. | ||
451 | Fschr. | ||
452 | Fsm. | ||
453 | Ftm. | ||
454 | Fut. | ||
455 | fut. | ||
456 | färö. | ||
457 | förml. | ||
458 | g. | ||
459 | Ga. | ||
460 | gall. | ||
461 | galloroman. | ||
462 | Gart. | ||
463 | gaskogn. | ||
464 | gbd. | ||
465 | Gbd. | ||
466 | Gbf. | ||
467 | GBl. | ||
468 | Gbl. | ||
469 | geb. | ||
470 | Geb. | ||
471 | Geb.-T. | ||
472 | Gebr. | ||
473 | gebr. | ||
474 | ged. | ||
475 | gef. | ||
476 | geg. | ||
477 | gegr. | ||
478 | geh. | ||
479 | gek. | ||
480 | gel. | ||
481 | geleg. | ||
482 | gem. | ||
483 | gemeingerm. | ||
484 | gen. | ||
485 | Gen. | ||
486 | geod. | ||
487 | geogr. | ||
488 | geograf. | ||
489 | geograph. | ||
490 | geol. | ||
491 | geolog. | ||
492 | geophys. | ||
493 | georg. | ||
494 | gep. | ||
495 | ger. | ||
496 | germ. | ||
497 | Ges. | ||
498 | ges. | ||
499 | gesch. | ||
500 | gespr. | ||
501 | gest. | ||
502 | get. | ||
503 | Gew. | ||
504 | gew. | ||
505 | gez. | ||
506 | Gfsch. | ||
507 | Gft. | ||
508 | gg. | ||
509 | ggb. | ||
510 | ggbfs. | ||
511 | ggez. | ||
512 | ggf. | ||
513 | ggfls. | ||
514 | ggfs. | ||
515 | Ggs. | ||
516 | ggü. | ||
517 | Ghzg. | ||
518 | Ghzgt. | ||
519 | glchz. | ||
520 | Gld. | ||
521 | Glde. | ||
522 | gldg. | ||
523 | Gldr. | ||
524 | Gled. | ||
525 | gleichbed. | ||
526 | gleichn. | ||
527 | gleichz. | ||
528 | Glfl. | ||
529 | gls. | ||
530 | gltd. | ||
531 | gltg. | ||
532 | glz. | ||
533 | gm. | ||
534 | got. | ||
535 | gr. | ||
536 | Gr. | ||
537 | Gramm. | ||
538 | grammat. | ||
539 | graph. | ||
540 | grch. | ||
541 | Grchl. | ||
542 | Grdb. | ||
543 | Grdf. | ||
544 | Grdfl. | ||
545 | Grdg. | ||
546 | Grdl. | ||
547 | Grdr. | ||
548 | grds. | ||
549 | Grdst. | ||
550 | griech. | ||
551 | Grz. | ||
552 | grönländ. | ||
553 | Gstb. | ||
554 | Gt. | ||
555 | gyn. | ||
556 | gynäk. | ||
557 | gäl. | ||
558 | H.-I. | ||
559 | H.-Qu. | ||
560 | hait. | ||
561 | Handw. | ||
562 | Hbf. | ||
563 | hd. | ||
564 | Hd.-Bibl. | ||
565 | Hdb. | ||
566 | hdbr. | ||
567 | Hdbr. | ||
568 | hdl. | ||
569 | Hdl. | ||
570 | Hdlbg. | ||
571 | hebr. | ||
572 | hess. | ||
573 | hethit. | ||
574 | Hf. | ||
575 | Hg. | ||
576 | hg. | ||
577 | hindust. | ||
578 | hinr. | ||
579 | hins. | ||
580 | Hinw. | ||
581 | hist. | ||
582 | HJber. | ||
583 | Hkl. | ||
584 | hl. | ||
585 | hochd. | ||
586 | hochspr. | ||
587 | Hom. | ||
588 | hor. | ||
589 | Hpfl. | ||
590 | hptpl. | ||
591 | hpts. | ||
592 | Hptst. | ||
593 | hptw. | ||
594 | Hptw. | ||
595 | HQu. | ||
596 | Hr. | ||
597 | HReg. | ||
598 | Hrn. | ||
599 | Hrsg. | ||
600 | hrsg. | ||
601 | Hs. | ||
602 | Hs.-Nr. | ||
603 | hschr. | ||
604 | Hschr. | ||
605 | HSt. | ||
606 | Hubbr. | ||
607 | Hubr. | ||
608 | Hw. | ||
609 | Hyaz. | ||
610 | hydr. | ||
611 | hydrol. | ||
612 | Hzm. | ||
613 | i. | ||
614 | I.E. | ||
615 | i.g.O. | ||
616 | i.Tr. | ||
617 | iber. | ||
618 | ibid. | ||
619 | ide. | ||
620 | Ident. | ||
621 | ident. | ||
622 | idg. | ||
623 | ie. | ||
624 | illyr. | ||
625 | Imkerspr. | ||
626 | imp. | ||
627 | Imp. | ||
628 | in. | ||
629 | Ind. | ||
630 | ind. | ||
631 | indef. | ||
632 | indekl. | ||
633 | indian. | ||
634 | indiff. | ||
635 | indir. | ||
636 | indiv. | ||
637 | indog. | ||
638 | indogerm. | ||
639 | indogerman. | ||
640 | indoiran. | ||
641 | indon. | ||
642 | indones. | ||
643 | Inf. | ||
644 | inf. | ||
645 | Ing. | ||
646 | Inh. | ||
647 | inkl. | ||
648 | inn. | ||
649 | Ins. | ||
650 | insb. | ||
651 | insbes. | ||
652 | int. | ||
653 | intern. | ||
654 | intrans. | ||
655 | ir. | ||
656 | iran. | ||
657 | iron. | ||
658 | isl. | ||
659 | islam. | ||
660 | isländ. | ||
661 | it. | ||
662 | ital. | ||
663 | italien. | ||
664 | j. | ||
665 | J. | ||
666 | Jahrh. | ||
667 | jakut. | ||
668 | Jan. | ||
669 | jap. | ||
670 | japan. | ||
671 | jav. | ||
672 | jem. | ||
673 | jemen. | ||
674 | Jg. | ||
675 | jgdfr. | ||
676 | Jh. | ||
677 | Jhd. | ||
678 | Jhdt. | ||
679 | Jhg. | ||
680 | Jhs. | ||
681 | jidd. | ||
682 | jmd. | ||
683 | jmdm. | ||
684 | jmdn. | ||
685 | jmds. | ||
686 | journ. | ||
687 | jr. | ||
688 | Jr. | ||
689 | Jt. | ||
690 | Jtsd. | ||
691 | jugendspr. | ||
692 | jugendsprachl. | ||
693 | jugoslaw. | ||
694 | Jul. | ||
695 | jun. | ||
696 | Jun. | ||
697 | jur. | ||
698 | Juw. | ||
699 | jägersprachl. | ||
700 | jährl. | ||
701 | Jän. | ||
702 | jüd. | ||
703 | k.u.k. | ||
704 | K.Ö.St.V. | ||
705 | kalm. | ||
706 | kanad. | ||
707 | Kap. | ||
708 | karib. | ||
709 | kastil. | ||
710 | katal. | ||
711 | katalan. | ||
712 | kath. | ||
713 | kaufm. | ||
714 | kaukas. | ||
715 | kelt. | ||
716 | Kgr. | ||
717 | Kh. | ||
718 | kindersprachl. | ||
719 | kirchenlat. | ||
720 | kirchenslaw. | ||
721 | kirchl. | ||
722 | kirg. | ||
723 | Kl. | ||
724 | klass. | ||
725 | klass.-lat. | ||
726 | klimatol. | ||
727 | kol. | ||
728 | Komm. | ||
729 | Konj. | ||
730 | Konv. | ||
731 | Kop. | ||
732 | kop. | ||
733 | kopt. | ||
734 | korean. | ||
735 | Kr. | ||
736 | kreol. | ||
737 | kret. | ||
738 | Krh. | ||
739 | Krhs. | ||
740 | Krim.-Ob.-Insp. | ||
741 | krimgot. | ||
742 | kriminaltechn. | ||
743 | Krkhs. | ||
744 | kroat. | ||
745 | Krs. | ||
746 | Ks. | ||
747 | Kto. | ||
748 | Kto.-Nr. | ||
749 | kuban. | ||
750 | kurd. | ||
751 | Kurzw. | ||
752 | Kw. | ||
753 | l. | ||
754 | L.-Abg. | ||
755 | lab. | ||
756 | LAbg. | ||
757 | ladin. | ||
758 | landsch. | ||
759 | Landw. | ||
760 | langfr. | ||
761 | langj. | ||
762 | langob. | ||
763 | langobard. | ||
764 | lapp. | ||
765 | lat. | ||
766 | latein. | ||
767 | latinis. | ||
768 | lautl. | ||
769 | lautm. | ||
770 | lbd. | ||
771 | lbdg. | ||
772 | Ldkr. | ||
773 | led. | ||
774 | leg. | ||
775 | lett. | ||
776 | lfd. | ||
777 | Lfg. | ||
778 | Lfm. | ||
779 | Lfrg. | ||
780 | Lg. | ||
781 | lgfr. | ||
782 | Lgft. | ||
783 | lgj. | ||
784 | lig. | ||
785 | ling. | ||
786 | lit. | ||
787 | LL.M. | ||
788 | lrh. | ||
789 | lt. | ||
790 | ltd. | ||
791 | luth. | ||
792 | luxemb. | ||
793 | Lz. | ||
794 | m. | ||
795 | M. | ||
796 | M.-Schr. | ||
797 | m.a.W. | ||
798 | ma. | ||
799 | MA. | ||
800 | Mag. | ||
801 | malai. | ||
802 | marinespr. | ||
803 | marx. | ||
804 | mask. | ||
805 | math. | ||
806 | Math. | ||
807 | max. | ||
808 | Max. | ||
809 | mazedon. | ||
810 | mbl. | ||
811 | Mbl. | ||
812 | MBl. | ||
813 | Mbll. | ||
814 | md. | ||
815 | mdal. | ||
816 | mdj. | ||
817 | mdl. | ||
818 | mdls. | ||
819 | Mdt. | ||
820 | me. | ||
821 | mech. | ||
822 | meckl. | ||
823 | med. | ||
824 | melanes. | ||
825 | mengl. | ||
826 | Merc. | ||
827 | met. | ||
828 | meteorol. | ||
829 | meton. | ||
830 | mex. | ||
831 | mexik. | ||
832 | mfr. | ||
833 | mfranz. | ||
834 | mfrk. | ||
835 | mfrz. | ||
836 | mfränk. | ||
837 | mgl. | ||
838 | Mgl. | ||
839 | mglw. | ||
840 | mhd. | ||
841 | mhdt. | ||
842 | Mi. | ||
843 | mi. | ||
844 | Mia. | ||
845 | milit. | ||
846 | Mill. | ||
847 | min. | ||
848 | Min. | ||
849 | mind. | ||
850 | Mio. | ||
851 | mir. | ||
852 | Mitgl. | ||
853 | mitteld. | ||
854 | mitteldt. | ||
855 | mittelhochdt. | ||
856 | Mittw. | ||
857 | Mitw. | ||
858 | mlat. | ||
859 | Mme. | ||
860 | Mmes. | ||
861 | mnd. | ||
862 | mndd. | ||
863 | mniederd. | ||
864 | mnl. | ||
865 | Mo. | ||
866 | mod. | ||
867 | mong. | ||
868 | Mrd. | ||
869 | Mrz. | ||
870 | Mschr. | ||
871 | Msgr. | ||
872 | Msp. | ||
873 | mtl. | ||
874 | mundartl. | ||
875 | musik. | ||
876 | MwSt. | ||
877 | Myth. | ||
878 | Mz. | ||
879 | männl. | ||
880 | möbl. | ||
881 | n. | ||
882 | Nachf. | ||
883 | nachm. | ||
884 | nat. | ||
885 | nationalsoz. | ||
886 | natsoz. | ||
887 | Nbf. | ||
888 | Nbfl. | ||
889 | Nchf. | ||
890 | nd. | ||
891 | ndd. | ||
892 | ndrl. | ||
893 | neapolit. | ||
894 | Neub. | ||
895 | neunorweg. | ||
896 | neutest. | ||
897 | neutr. | ||
898 | Nfl. | ||
899 | ngl. | ||
900 | ngr. | ||
901 | nhbr. | ||
902 | nhd. | ||
903 | nicar. | ||
904 | niederd. | ||
905 | niederdt. | ||
906 | niederl. | ||
907 | niederld. | ||
908 | niem. | ||
909 | niger. | ||
910 | nihil. | ||
911 | nl. | ||
912 | nlat. | ||
913 | nmtl. | ||
914 | Nom. | ||
915 | nord. | ||
916 | nordamerik. | ||
917 | nordd. | ||
918 | norddt. | ||
919 | nordgerm. | ||
920 | nordostd. | ||
921 | nordostdt. | ||
922 | nordwestd. | ||
923 | nordwestdt. | ||
924 | norm. | ||
925 | norw. | ||
926 | norweg. | ||
927 | Nov. | ||
928 | Nr. | ||
929 | ntw. | ||
930 | Ntw. | ||
931 | Nutzfl. | ||
932 | nw. | ||
933 | näml. | ||
934 | nö. | ||
935 | nördl. | ||
936 | o. | ||
937 | O.K. | ||
938 | ob. | ||
939 | Ob. | ||
940 | Obb. | ||
941 | obb. | ||
942 | obd. | ||
943 | Oberlaus. | ||
944 | obers. | ||
945 | obersächs. | ||
946 | obj. | ||
947 | od. | ||
948 | offiz. | ||
949 | Offz. | ||
950 | Ofr. | ||
951 | ofrs. | ||
952 | Okt. | ||
953 | op. | ||
954 | Orch.-Bes. | ||
955 | org. | ||
956 | Orig. | ||
957 | orn. | ||
958 | orth. | ||
959 | Ortskl. | ||
960 | Osch. | ||
961 | osk. | ||
962 | osman. | ||
963 | ostd. | ||
964 | ostdt. | ||
965 | ostfr. | ||
966 | ostfrz. | ||
967 | ostgerm. | ||
968 | ostidg. | ||
969 | ostmdt. | ||
970 | ostmitteld. | ||
971 | ostniederd. | ||
972 | ostpr. | ||
973 | ostpreuß. | ||
974 | ostw. | ||
975 | osö. | ||
976 | Ouv. | ||
977 | oz. | ||
978 | Oz. | ||
979 | oö. | ||
980 | OÖ. | ||
981 | P. | ||
982 | p. | ||
983 | P.S. | ||
984 | pa. | ||
985 | palästin. | ||
986 | par. | ||
987 | parag. | ||
988 | Paragr. | ||
989 | Parl. | ||
990 | Part. | ||
991 | pass. | ||
992 | Pat. | ||
993 | pej. | ||
994 | pers. | ||
995 | peruan. | ||
996 | Pet. | ||
997 | Pf. | ||
998 | Pfd. | ||
999 | Pfg. | ||
1000 | Pfl. | ||
1001 | pharm. | ||
1002 | philos. | ||
1003 | Philos. | ||
1004 | phonolog. | ||
1005 | phryg. | ||
1006 | Phys. | ||
1007 | phys. | ||
1008 | phöniz. | ||
1009 | Pi. | ||
1010 | pik. | ||
1011 | Pkt. | ||
1012 | Pl. | ||
1013 | Plur. | ||
1014 | poet. | ||
1015 | Pol. | ||
1016 | pol. | ||
1017 | polit. | ||
1018 | poln. | ||
1019 | polynes. | ||
1020 | port. | ||
1021 | portug. | ||
1022 | Pos. | ||
1023 | pos. | ||
1024 | pp. | ||
1025 | ppa. | ||
1026 | preuß. | ||
1027 | Priv.-Doz. | ||
1028 | Prof. | ||
1029 | prot. | ||
1030 | Prot. | ||
1031 | prov. | ||
1032 | Prov. | ||
1033 | prov.-fr. | ||
1034 | provenz. | ||
1035 | Proz. | ||
1036 | Proz.-Bev. | ||
1037 | präd. | ||
1038 | prähist. | ||
1039 | Präs. | ||
1040 | Psych. | ||
1041 | psych. | ||
1042 | Päd. | ||
1043 | Q. | ||
1044 | q.v. | ||
1045 | Qmstr. | ||
1046 | Qt. | ||
1047 | qu. | ||
1048 | Qu. | ||
1049 | quadr. | ||
1050 | Quadr. | ||
1051 | qual. | ||
1052 | Qual. | ||
1053 | quant. | ||
1054 | Quant. | ||
1055 | Quar. | ||
1056 | Quart. | ||
1057 | Quat. | ||
1058 | quitt. | ||
1059 | Quitt. | ||
1060 | Quäst. | ||
1061 | r. | ||
1062 | r.-k. | ||
1063 | Rab. | ||
1064 | rad. | ||
1065 | Raff. | ||
1066 | Rak. | ||
1067 | Randb. | ||
1068 | Randbem. | ||
1069 | rat. | ||
1070 | Rat. | ||
1071 | Rb. | ||
1072 | rd. | ||
1073 | RdErl. | ||
1074 | Rdf. | ||
1075 | refl. | ||
1076 | Reg. | ||
1077 | Reg.-Bez. | ||
1078 | Regt. | ||
1079 | Rel. | ||
1080 | rel. | ||
1081 | relig. | ||
1082 | Rep. | ||
1083 | resp. | ||
1084 | Rg.-Präs. | ||
1085 | RGBl. | ||
1086 | rglm. | ||
1087 | Rgstr. | ||
1088 | Rgt. | ||
1089 | Rh. | ||
1090 | rh. | ||
1091 | rhein. | ||
1092 | rheinhess. | ||
1093 | rhet. | ||
1094 | rhfrk. | ||
1095 | Rhj. | ||
1096 | Rhld. | ||
1097 | Rhs. | ||
1098 | Ri. | ||
1099 | Richtl. | ||
1100 | rip. | ||
1101 | rk. | ||
1102 | roman. | ||
1103 | rotw. | ||
1104 | Rr. | ||
1105 | rrh. | ||
1106 | Rspr. | ||
1107 | Rtn. | ||
1108 | Rtt. | ||
1109 | rumän. | ||
1110 | russ. | ||
1111 | Rvj. | ||
1112 | rzp. | ||
1113 | rätorom. | ||
1114 | röm. | ||
1115 | röm.-kath. | ||
1116 | S. | ||
1117 | s. | ||
1118 | S.-Wk. | ||
1119 | Sa. | ||
1120 | Sachs. | ||
1121 | san. | ||
1122 | sanskr. | ||
1123 | Sat. | ||
1124 | sat. | ||
1125 | Sb. | ||
1126 | Sbd. | ||
1127 | sc. | ||
1128 | scherzh. | ||
1129 | Schill. | ||
1130 | schles. | ||
1131 | schott. | ||
1132 | schr. | ||
1133 | schriftl. | ||
1134 | Schussw. | ||
1135 | schwed. | ||
1136 | schweiz. | ||
1137 | Schwg. | ||
1138 | Schwp. | ||
1139 | schwäb. | ||
1140 | scil. | ||
1141 | Sdp. | ||
1142 | sek. | ||
1143 | sem. | ||
1144 | semit. | ||
1145 | sen. | ||
1146 | Sep. | ||
1147 | Sept. | ||
1148 | serb. | ||
1149 | serbokroat. | ||
1150 | Sg. | ||
1151 | sibir. | ||
1152 | Sing. | ||
1153 | singhal. | ||
1154 | Sir. | ||
1155 | sizilian. | ||
1156 | skand. | ||
1157 | slaw. | ||
1158 | slow. | ||
1159 | slowak. | ||
1160 | slowen. | ||
1161 | So. | ||
1162 | sod. | ||
1163 | sof. | ||
1164 | sog. | ||
1165 | sogen. | ||
1166 | sogl. | ||
1167 | soldatenspr. | ||
1168 | solv. | ||
1169 | somal. | ||
1170 | sorb. | ||
1171 | Sout. | ||
1172 | soz. | ||
1173 | soziol. | ||
1174 | span. | ||
1175 | spez. | ||
1176 | sportspr. | ||
1177 | Spr. | ||
1178 | sprachwiss. | ||
1179 | Spvg. | ||
1180 | Spvgg. | ||
1181 | spätahd. | ||
1182 | spätgriech. | ||
1183 | spätlat. | ||
1184 | spätmhd. | ||
1185 | Sr. | ||
1186 | ssp. | ||
1187 | St. | ||
1188 | St.-Nr. | ||
1189 | staatl. | ||
1190 | Std. | ||
1191 | stdl. | ||
1192 | stellv. | ||
1193 | Stellv. | ||
1194 | Stk. | ||
1195 | Str. | ||
1196 | str. | ||
1197 | Stud. | ||
1198 | stud. | ||
1199 | subsp. | ||
1200 | Subst. | ||
1201 | sumer. | ||
1202 | svw. | ||
1203 | Swk. | ||
1204 | syn. | ||
1205 | Syn. | ||
1206 | syr. | ||
1207 | sächs. | ||
1208 | südafrik. | ||
1209 | südd. | ||
1210 | süddt. | ||
1211 | südl. | ||
1212 | südostdt. | ||
1213 | südwestd. | ||
1214 | Süßw. | ||
1215 | Tab. | ||
1216 | Tabl. | ||
1217 | Taf. | ||
1218 | tamil. | ||
1219 | tatar. | ||
1220 | techn. | ||
1221 | Tel. | ||
1222 | telef. | ||
1223 | Temp. | ||
1224 | Terr. | ||
1225 | tessin. | ||
1226 | test. | ||
1227 | Tfx. | ||
1228 | tgl. | ||
1229 | Tgt. | ||
1230 | thrak. | ||
1231 | thür. | ||
1232 | thüring. | ||
1233 | Ti. | ||
1234 | tib. | ||
1235 | tirol. | ||
1236 | Tlr. | ||
1237 | tochar. | ||
1238 | trans. | ||
1239 | tsch. | ||
1240 | tschech. | ||
1241 | tschechoslowak. | ||
1242 | Tsd. | ||
1243 | tun. | ||
1244 | Tun. | ||
1245 | tunes. | ||
1246 | Tunes. | ||
1247 | tungus. | ||
1248 | turkotat. | ||
1249 | typogr. | ||
1250 | tägl. | ||
1251 | türk. | ||
1252 | u. | ||
1253 | u.a. | ||
1254 | Ubr. | ||
1255 | ue. | ||
1256 | ugr. | ||
1257 | ugs. | ||
1258 | ukrain. | ||
1259 | umbr. | ||
1260 | umg. | ||
1261 | unang. | ||
1262 | unbefl. | ||
1263 | Unf. | ||
1264 | unf. | ||
1265 | unfol. | ||
1266 | unfr. | ||
1267 | ung. | ||
1268 | ungar. | ||
1269 | ungebr. | ||
1270 | ungel. | ||
1271 | ungen. | ||
1272 | unges. | ||
1273 | ungl. | ||
1274 | Uni-Kl. | ||
1275 | Univ. | ||
1276 | unv. | ||
1277 | unverantw. | ||
1278 | unverb. | ||
1279 | unverbr. | ||
1280 | unverd. | ||
1281 | unverg. | ||
1282 | unverh. | ||
1283 | unverk. | ||
1284 | unverp. | ||
1285 | unversch. | ||
1286 | unverz. | ||
1287 | unverzgl. | ||
1288 | unvollst. | ||
1289 | unvorb. | ||
1290 | unvors. | ||
1291 | unzerbr. | ||
1292 | urgerm. | ||
1293 | urkdl. | ||
1294 | urspr. | ||
1295 | ursprüngl. | ||
1296 | Urt. | ||
1297 | usf. | ||
1298 | USt-IdNr. | ||
1299 | usw. | ||
1300 | uvm. | ||
1301 | v. | ||
1302 | va. | ||
1303 | Ver. | ||
1304 | Verf. | ||
1305 | Verg. | ||
1306 | vergl. | ||
1307 | Vergl. | ||
1308 | verh. | ||
1309 | Vers. | ||
1310 | vers. | ||
1311 | vert. | ||
1312 | Vfg. | ||
1313 | vgbl. | ||
1314 | vgl. | ||
1315 | Vgl. | ||
1316 | vh. | ||
1317 | viell. | ||
1318 | vj. | ||
1319 | Vj. | ||
1320 | vl. | ||
1321 | vlat. | ||
1322 | vlt. | ||
1323 | vmtl. | ||
1324 | volkst. | ||
1325 | Vors. | ||
1326 | vrstl. | ||
1327 | vrt. | ||
1328 | vs. | ||
1329 | vsl. | ||
1330 | vt. | ||
1331 | vulg. | ||
1332 | vulgärlat. | ||
1333 | Vwz. | ||
1334 | vzk. | ||
1335 | w. | ||
1336 | W. | ||
1337 | Wa. | ||
1338 | wal. | ||
1339 | wehrtgl. | ||
1340 | weibl. | ||
1341 | Weis. | ||
1342 | weißruss. | ||
1343 | werkt. | ||
1344 | westd. | ||
1345 | westdt. | ||
1346 | Westf. | ||
1347 | westfäl. | ||
1348 | westgerm. | ||
1349 | westl. | ||
1350 | westmitteld. | ||
1351 | westmitteldt. | ||
1352 | Wf. | ||
1353 | wf. | ||
1354 | Wfl. | ||
1355 | wg. | ||
1356 | wh. | ||
1357 | Whg. | ||
1358 | winzerspr. | ||
1359 | wirtschaftl. | ||
1360 | wiss. | ||
1361 | Wkst. | ||
1362 | Wkstf. | ||
1363 | wkts. | ||
1364 | wld. | ||
1365 | Wr. | ||
1366 | Ws. | ||
1367 | Wtb. | ||
1368 | Ww. | ||
1369 | Wwe. | ||
1370 | Wz. | ||
1371 | Xerogr. | ||
1372 | Xerok. | ||
1373 | Xyl. | ||
1374 | y. | ||
1375 | Y. | ||
1376 | yd. | ||
1377 | Yd. | ||
1378 | Yds. | ||
1379 | yds. | ||
1380 | Z. | ||
1381 | z.B. | ||
1382 | za. | ||
1383 | Zf. | ||
1384 | Zgm. | ||
1385 | zgs. | ||
1386 | zgst. | ||
1387 | zgw. | ||
1388 | Zi. | ||
1389 | Ziff. | ||
1390 | zit. | ||
1391 | Zit. | ||
1392 | zk. | ||
1393 | Zk. | ||
1394 | Zool. | ||
1395 | zool. | ||
1396 | Zssg. | ||
1397 | Zssgn. | ||
1398 | Ztr. | ||
1399 | Zub. | ||
1400 | zur. | ||
1401 | zus. | ||
1402 | zw. | ||
1403 | Zz. | ||
1404 | zz. | ||
1405 | zzgl. | ||
1406 | zzt. | ||
1407 | ägypt. | ||
1408 | Ökol. | ||
1409 | ökol. | ||
1410 | ökon. | ||
1411 | ökum. | ||
1412 | örtl. | ||
1413 | österr. | ||
1414 | Österr. | ||
1415 | östl. | ||
1416 | übers. | ||
1417 | übertr. | ||
1418 | überw. | ||
1419 | Überw. | ||
1420 | übl. | ||
1421 | üblw. | ||
1422 | Übn. | ||
1423 | übsch. | ||
diff --git a/home-modules/pandoc/german_abbreviations.txt.gup b/home-modules/pandoc/german_abbreviations.txt.gup new file mode 100755 index 00000000..abcab1da --- /dev/null +++ b/home-modules/pandoc/german_abbreviations.txt.gup | |||
@@ -0,0 +1,35 @@ | |||
1 | #!/usr/bin/env nix | ||
2 | #!nix shell --impure --expr `` | ||
3 | #!nix with (import (builtins.getFlake ''nixpkgs'') {}); | ||
4 | #!nix python3.withPackages (ps: with ps; [ requests ]) | ||
5 | #!nix `` --command python3 | ||
6 | |||
7 | import requests | ||
8 | import json | ||
9 | import sys | ||
10 | import re | ||
11 | import subprocess | ||
12 | |||
13 | def wiki_cont(url, params): | ||
14 | continue_params = None | ||
15 | while True: | ||
16 | req_params = params | ||
17 | if continue_params is not None: | ||
18 | req_params |= continue_params | ||
19 | json_data = requests.get(url, req_params).json() | ||
20 | if "query" in json_data: | ||
21 | yield json_data["query"] | ||
22 | if "continue" not in json_data: | ||
23 | break | ||
24 | else: | ||
25 | continue_params = json_data["continue"] | ||
26 | |||
27 | out_re = re.compile(r"[^ ]*[^ 0-9][^ ]*\.") | ||
28 | |||
29 | subprocess.run(["gup", "--always"], check=True) | ||
30 | |||
31 | with open(sys.argv[1], 'w') as out: | ||
32 | for query in wiki_cont("https://de.wiktionary.org/w/api.php", {"action": "query", "list": "categorymembers", "cmtitle": "Kategorie:Abkürzung_(Deutsch)", "format": "json"}): | ||
33 | for item in map(lambda i: i["title"], query["categorymembers"]): | ||
34 | if out_re.fullmatch(item): | ||
35 | print(item, file=out) | ||
diff --git a/hosts/sif/default.nix b/hosts/sif/default.nix index 09d43109..6214569a 100644 --- a/hosts/sif/default.nix +++ b/hosts/sif/default.nix | |||
@@ -12,9 +12,8 @@ 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 | 15 | ./email ./libvirt ./greetd |
16 | tmpfs-root bcachefs initrd-all-crypto-modules default-locale openssh rebuild-machines | 16 | tmpfs-root bcachefs initrd-all-crypto-modules default-locale openssh rebuild-machines niri-unstable networkmanager |
17 | networkmanager | ||
18 | flakeInputs.nixos-hardware.nixosModules.lenovo-thinkpad-p1 | 17 | flakeInputs.nixos-hardware.nixosModules.lenovo-thinkpad-p1 |
19 | flakeInputs.impermanence.nixosModules.impermanence | 18 | flakeInputs.impermanence.nixosModules.impermanence |
20 | flakeInputs.nixVirt.nixosModules.default | 19 | flakeInputs.nixVirt.nixosModules.default |
@@ -27,9 +26,6 @@ in { | |||
27 | allowUnfree = true; | 26 | allowUnfree = true; |
28 | pulseaudio = true; | 27 | pulseaudio = true; |
29 | }; | 28 | }; |
30 | extraOverlays = [ | ||
31 | flakeInputs.niri-flake.overlays.niri | ||
32 | ]; | ||
33 | }; | 29 | }; |
34 | 30 | ||
35 | time.timeZone = null; | 31 | time.timeZone = null; |
@@ -56,6 +52,7 @@ in { | |||
56 | systemd-boot = { | 52 | systemd-boot = { |
57 | enable = true; | 53 | enable = true; |
58 | configurationLimit = 15; | 54 | configurationLimit = 15; |
55 | netbootxyz.enable = true; | ||
59 | }; | 56 | }; |
60 | efi.canTouchEfiVariables = true; | 57 | efi.canTouchEfiVariables = true; |
61 | timeout = null; | 58 | timeout = null; |
@@ -64,15 +61,20 @@ in { | |||
64 | plymouth.enable = true; | 61 | plymouth.enable = true; |
65 | 62 | ||
66 | kernelPackages = pkgs.linuxPackages_latest; | 63 | kernelPackages = pkgs.linuxPackages_latest; |
67 | extraModulePackages = with config.boot.kernelPackages; [ v4l2loopback ]; | ||
68 | kernelModules = ["v4l2loopback"]; | ||
69 | kernelPatches = [ | 64 | kernelPatches = [ |
70 | { name = "edac-config"; | 65 | { name = "edac-config"; |
71 | patch = null; | 66 | patch = null; |
72 | extraConfig = '' | 67 | extraStructuredConfig = with lib.kernel; { |
73 | EDAC y | 68 | EDAC = yes; |
74 | EDAC_IE31200 y | 69 | EDAC_IE31200 = yes; |
75 | ''; | 70 | }; |
71 | } | ||
72 | { name = "zswap-default"; | ||
73 | patch = null; | ||
74 | extraStructuredConfig = with lib.kernel; { | ||
75 | ZSWAP_DEFAULT_ON = yes; | ||
76 | ZSWAP_SHRINKER_DEFAULT_ON = yes; | ||
77 | }; | ||
76 | } | 78 | } |
77 | ]; | 79 | ]; |
78 | 80 | ||
@@ -124,40 +126,16 @@ in { | |||
124 | rulesetFile = ./ruleset.nft; | 126 | rulesetFile = ./ruleset.nft; |
125 | }; | 127 | }; |
126 | 128 | ||
127 | # firewall = { | ||
128 | # enable = true; | ||
129 | # allowedTCPPorts = [ 22 # ssh | ||
130 | # 8000 # quickserve | ||
131 | # ]; | ||
132 | # }; | ||
133 | |||
134 | # wlanInterfaces = { | ||
135 | # wlan0 = { | ||
136 | # device = "wlp82s0"; | ||
137 | # }; | ||
138 | # }; | ||
139 | |||
140 | # bonds = { | ||
141 | # "lan" = { | ||
142 | # interfaces = [ "wlan0" "enp0s31f6" "dock0" ]; | ||
143 | # driverOptions = { | ||
144 | # miimon = "1000"; | ||
145 | # mode = "active-backup"; | ||
146 | # primary_reselect = "always"; | ||
147 | # }; | ||
148 | # }; | ||
149 | # }; | ||
150 | |||
151 | useDHCP = false; | 129 | useDHCP = false; |
152 | useNetworkd = true; | 130 | useNetworkd = true; |
153 | |||
154 | # interfaces."tinc.yggdrasil" = { | ||
155 | # virtual = true; | ||
156 | # virtualType = config.services.tinc.networks.yggdrasil.interfaceType; | ||
157 | # macAddress = "5c:93:21:c3:61:39"; | ||
158 | # }; | ||
159 | }; | 131 | }; |
160 | 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 | }; | ||
161 | environment.etc."NetworkManager/dnsmasq.d/libvirt_dnsmasq.conf" = { | 139 | environment.etc."NetworkManager/dnsmasq.d/libvirt_dnsmasq.conf" = { |
162 | text = '' | 140 | text = '' |
163 | except-interface=virbr0 | 141 | except-interface=virbr0 |
@@ -400,19 +378,6 @@ in { | |||
400 | ]; | 378 | ]; |
401 | 379 | ||
402 | services = { | 380 | services = { |
403 | uucp = { | ||
404 | enable = true; | ||
405 | nodeName = "sif"; | ||
406 | remoteNodes = { | ||
407 | "ymir" = { | ||
408 | publicKeys = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG6KNtsCOl5fsZ4rV7udTulGMphJweLBoKapzerWNoLY root@ymir"]; | ||
409 | hostnames = ["ymir.yggdrasil.li" "ymir.niflheim.yggdrasil"]; | ||
410 | }; | ||
411 | }; | ||
412 | |||
413 | defaultCommands = lib.mkForce []; | ||
414 | }; | ||
415 | |||
416 | avahi.enable = true; | 381 | avahi.enable = true; |
417 | 382 | ||
418 | fwupd.enable = true; | 383 | fwupd.enable = true; |
@@ -431,8 +396,8 @@ in { | |||
431 | 396 | ||
432 | logind = { | 397 | logind = { |
433 | lidSwitch = "suspend"; | 398 | lidSwitch = "suspend"; |
434 | lidSwitchDocked = "lock"; | 399 | lidSwitchDocked = "ignore"; |
435 | lidSwitchExternalPower = "lock"; | 400 | lidSwitchExternalPower = "ignore"; |
436 | }; | 401 | }; |
437 | 402 | ||
438 | atd = { | 403 | atd = { |
@@ -441,7 +406,7 @@ in { | |||
441 | }; | 406 | }; |
442 | 407 | ||
443 | xserver = { | 408 | xserver = { |
444 | enable = true; | 409 | enable = false; |
445 | 410 | ||
446 | xkb = { | 411 | xkb = { |
447 | layout = "us"; | 412 | layout = "us"; |
@@ -467,47 +432,18 @@ in { | |||
467 | }; | 432 | }; |
468 | libinput.enable = true; | 433 | libinput.enable = true; |
469 | 434 | ||
470 | greetd = { | 435 | envfs.enable = false; |
471 | enable = true; | ||
472 | # settings.default_session.command = let | ||
473 | # cfg = config.programs.regreet; | ||
474 | # in pkgs.writeShellScript "greeter" '' | ||
475 | # modprobe -r nvidia_drm | ||
476 | 436 | ||
477 | # exec ${pkgs.dbus}/bin/dbus-run-session ${lib.getExe pkgs.cage} ${lib.escapeShellArgs cfg.cageArgs} -- ${lib.getExe cfg.package} | 437 | displayManager.defaultSession = "Niri"; |
478 | # ''; | ||
479 | }; | ||
480 | }; | ||
481 | |||
482 | programs.regreet = { | ||
483 | enable = true; | ||
484 | theme = { | ||
485 | package = pkgs.equilux-theme; | ||
486 | name = "Equilux-compact"; | ||
487 | }; | ||
488 | iconTheme = { | ||
489 | package = pkgs.paper-icon-theme; | ||
490 | name = "Paper-Mono-Dark"; | ||
491 | }; | ||
492 | font = { | ||
493 | package = pkgs.fira; | ||
494 | name = "Fira Sans"; | ||
495 | # size = 6; | ||
496 | }; | ||
497 | cageArgs = [ "-s" "-m" "last" ]; | ||
498 | settings = { | ||
499 | GTK.application_prefer_dark_theme = true; | ||
500 | }; | ||
501 | }; | 438 | }; |
502 | programs.niri.enable = true; | ||
503 | 439 | ||
504 | systemd.tmpfiles.settings = { | 440 | systemd.tmpfiles.settings = { |
505 | "10-localtime"."/etc/localtime".L.argument = "/.bcachefs/etc/localtime"; | 441 | "10-localtime"."/etc/localtime".L.argument = "/.bcachefs/etc/localtime"; |
506 | 442 | ||
507 | "10-regreet"."/var/cache/regreet/cache.toml".C.argument = toString ((pkgs.formats.toml {}).generate "cache.toml" { | 443 | # "10-regreet"."/var/cache/regreet/cache.toml".C.argument = toString ((pkgs.formats.toml {}).generate "cache.toml" { |
508 | last_user = "gkleen"; | 444 | # last_user = "gkleen"; |
509 | user_to_last_sess.gkleen = "niri"; | 445 | # user_to_last_sess.gkleen = "Niri"; |
510 | }); | 446 | # }); |
511 | }; | 447 | }; |
512 | 448 | ||
513 | users = { | 449 | users = { |
@@ -616,15 +552,15 @@ in { | |||
616 | }; | 552 | }; |
617 | 553 | ||
618 | nvidia = { | 554 | nvidia = { |
619 | open = true; | 555 | open = false; |
620 | modesetting.enable = true; | 556 | modesetting.enable = true; |
621 | powerManagement.enable = true; | 557 | powerManagement.enable = true; |
622 | prime = { | 558 | # prime = { |
623 | nvidiaBusId = "PCI:1:0:0"; | 559 | # nvidiaBusId = "PCI:1:0:0"; |
624 | intelBusId = "PCI:0:2:0"; | 560 | # intelBusId = "PCI:0:2:0"; |
625 | reverseSync.enable = true; | 561 | # reverseSync.enable = true; |
626 | offload.enableOffloadCmd = true; | 562 | # offload.enableOffloadCmd = true; |
627 | }; | 563 | # }; |
628 | }; | 564 | }; |
629 | 565 | ||
630 | graphics = { | 566 | graphics = { |
@@ -667,25 +603,6 @@ in { | |||
667 | 603 | ||
668 | 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; |
669 | 605 | ||
670 | systemd.services."ac-plugged" = { | ||
671 | description = "Inhibit handling of lid-switch and sleep"; | ||
672 | |||
673 | path = with pkgs; [ systemd coreutils ]; | ||
674 | |||
675 | script = '' | ||
676 | exec systemd-inhibit --what=handle-lid-switch --why="AC is connected" --mode=block sleep infinity | ||
677 | ''; | ||
678 | |||
679 | serviceConfig = { | ||
680 | Type = "simple"; | ||
681 | }; | ||
682 | }; | ||
683 | |||
684 | services.udev.extraRules = with pkgs; lib.mkAfter '' | ||
685 | SUBSYSTEM=="power_supply", ENV{POWER_SUPPLY_ONLINE}=="0", RUN+="${systemd}/bin/systemctl --no-block stop ac-plugged.service" | ||
686 | SUBSYSTEM=="power_supply", ENV{POWER_SUPPLY_ONLINE}=="1", RUN+="${systemd}/bin/systemctl --no-block start ac-plugged.service" | ||
687 | ''; | ||
688 | |||
689 | systemd.services."nix-daemon".serviceConfig = { | 606 | systemd.services."nix-daemon".serviceConfig = { |
690 | MemoryAccounting = true; | 607 | MemoryAccounting = true; |
691 | MemoryHigh = "50%"; | 608 | MemoryHigh = "50%"; |
@@ -698,6 +615,7 @@ in { | |||
698 | 615 | ||
699 | services.dbus.packages = with pkgs; | 616 | services.dbus.packages = with pkgs; |
700 | [ dbus dconf | 617 | [ dbus dconf |
618 | xdg-desktop-portal-gtk | ||
701 | ]; | 619 | ]; |
702 | 620 | ||
703 | services.udisks2.enable = true; | 621 | services.udisks2.enable = true; |
@@ -706,12 +624,8 @@ in { | |||
706 | light.enable = true; | 624 | light.enable = true; |
707 | wireshark.enable = true; | 625 | wireshark.enable = true; |
708 | dconf.enable = true; | 626 | dconf.enable = true; |
709 | }; | 627 | niri.enable = true; |
710 | 628 | fuse.userAllowOther = true; | |
711 | zramSwap = { | ||
712 | enable = true; | ||
713 | algorithm = "zstd"; | ||
714 | writebackDevice = "/dev/disk/by-label/swap"; | ||
715 | }; | 629 | }; |
716 | 630 | ||
717 | services.pcscd.enable = true; | 631 | services.pcscd.enable = true; |
@@ -731,6 +645,16 @@ in { | |||
731 | environment.sessionVariables."GTK_USE_PORTAL" = "1"; | 645 | environment.sessionVariables."GTK_USE_PORTAL" = "1"; |
732 | xdg.portal = { | 646 | xdg.portal = { |
733 | enable = true; | 647 | enable = true; |
648 | extraPortals = with pkgs; [ xdg-desktop-portal-gtk ]; | ||
649 | config.niri = { | ||
650 | default = ["gnome" "gtk"]; | ||
651 | "org.freedesktop.impl.portal.FileChooser" = ["gtk"]; | ||
652 | "org.freedesktop.impl.portal.OpenFile" = ["gtk"]; | ||
653 | "org.freedesktop.impl.portal.Access" = ["gtk"]; | ||
654 | "org.freedesktop.impl.portal.Notification" = ["gtk"]; | ||
655 | "org.freedesktop.impl.portal.Secret" = ["gnome-keyring"]; | ||
656 | "org.freedesktop.impl.portal.Inhibit" = ["none"]; | ||
657 | }; | ||
734 | }; | 658 | }; |
735 | 659 | ||
736 | environment.persistence."/.bcachefs" = { | 660 | environment.persistence."/.bcachefs" = { |
@@ -738,19 +662,17 @@ in { | |||
738 | directories = [ | 662 | directories = [ |
739 | "/nix" | 663 | "/nix" |
740 | "/root" | 664 | "/root" |
665 | "/home" | ||
741 | "/var/log" | 666 | "/var/log" |
742 | "/var/lib/sops-nix" | 667 | "/var/lib/sops-nix" |
743 | "/var/lib/nixos" | 668 | "/var/lib/nixos" |
744 | "/var/lib/systemd" | 669 | "/var/lib/systemd" |
745 | "/home" | ||
746 | "/var/lib/chrony" | 670 | "/var/lib/chrony" |
747 | "/var/lib/fprint" | 671 | "/var/lib/fprint" |
748 | "/var/lib/bluetooth" | 672 | "/var/lib/bluetooth" |
749 | "/var/lib/upower" | 673 | "/var/lib/upower" |
750 | "/var/lib/postfix" | 674 | "/var/lib/postfix" |
751 | "/etc/NetworkManager/system-connections" | 675 | "/etc/NetworkManager/system-connections" |
752 | { directory = "/var/uucp"; user = "uucp"; group = "uucp"; mode = "0700"; } | ||
753 | { directory = "/var/spool/uucp"; user = "uucp"; group = "uucp"; mode = "0750"; } | ||
754 | ]; | 676 | ]; |
755 | files = [ | 677 | files = [ |
756 | ]; | 678 | ]; |
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/greetd/default.nix b/hosts/sif/greetd/default.nix new file mode 100644 index 00000000..37ca13c5 --- /dev/null +++ b/hosts/sif/greetd/default.nix | |||
@@ -0,0 +1,49 @@ | |||
1 | { pkgs, ... }: | ||
2 | { | ||
3 | config = { | ||
4 | services.greetd = { | ||
5 | enable = true; | ||
6 | # settings.default_session.command = let | ||
7 | # cfg = config.programs.regreet; | ||
8 | # in pkgs.writeShellScript "greeter" '' | ||
9 | # modprobe -r nvidia_drm | ||
10 | |||
11 | # exec ${pkgs.dbus}/bin/dbus-run-session ${lib.getExe pkgs.cage} ${lib.escapeShellArgs cfg.cageArgs} -- ${lib.getExe cfg.package} | ||
12 | # ''; | ||
13 | }; | ||
14 | systemd.services.greetd.environment = { | ||
15 | XKB_DEFAULT_LAYOUT = "us,us"; | ||
16 | XKB_DEFAULT_VARIANT = "dvp,"; | ||
17 | XKB_DEFAULT_OPTIONS = "compose:caps,grp:win_space_toggle"; | ||
18 | }; | ||
19 | programs.regreet = { | ||
20 | enable = true; | ||
21 | theme = { | ||
22 | package = pkgs.equilux-theme; | ||
23 | name = "Equilux-compact"; | ||
24 | }; | ||
25 | iconTheme = { | ||
26 | package = pkgs.paper-icon-theme; | ||
27 | name = "Paper-Mono-Dark"; | ||
28 | }; | ||
29 | font = { | ||
30 | package = pkgs.fira; | ||
31 | name = "Fira Sans"; | ||
32 | # size = 6; | ||
33 | }; | ||
34 | cageArgs = [ "-s" "-m" "last" ]; | ||
35 | settings = { | ||
36 | GTK.application_prefer_dark_theme = true; | ||
37 | widget.clock.format = "%F %H:%M:%S%:z"; | ||
38 | background = { | ||
39 | path = pkgs.runCommand "wallpaper.png" { | ||
40 | buildInputs = with pkgs; [ imagemagick ]; | ||
41 | } '' | ||
42 | magick ${./wallpaper.png} -filter Gaussian -resize 6.25% -define filter:sigma=2.5 -resize 1600% "$out" | ||
43 | ''; | ||
44 | fit = "Cover"; | ||
45 | }; | ||
46 | }; | ||
47 | }; | ||
48 | }; | ||
49 | } | ||
diff --git a/hosts/sif/greetd/wallpaper.png b/hosts/sif/greetd/wallpaper.png new file mode 100644 index 00000000..20fc761a --- /dev/null +++ b/hosts/sif/greetd/wallpaper.png | |||
Binary files differ | |||
diff --git a/hosts/sif/hw.nix b/hosts/sif/hw.nix index d1fb2934..1bcf0261 100644 --- a/hosts/sif/hw.nix +++ b/hosts/sif/hw.nix | |||
@@ -19,6 +19,9 @@ | |||
19 | "/var/lib/sops-nix".neededForBoot = true; | 19 | "/var/lib/sops-nix".neededForBoot = true; |
20 | "/var/lib/systemd".neededForBoot = true; | 20 | "/var/lib/systemd".neededForBoot = true; |
21 | }; | 21 | }; |
22 | swapDevices = [ | ||
23 | { label = "swap"; } | ||
24 | ]; | ||
22 | # system.etc.overlay.enable = false; | 25 | # system.etc.overlay.enable = false; |
23 | 26 | ||
24 | boot.initrd.systemd.packages = [ | 27 | boot.initrd.systemd.packages = [ |
diff --git a/hosts/sif/libvirt/default.nix b/hosts/sif/libvirt/default.nix index d0be7dff..9712d0d9 100644 --- a/hosts/sif/libvirt/default.nix +++ b/hosts/sif/libvirt/default.nix | |||
@@ -8,6 +8,7 @@ with flakeInputs.nixVirt.lib; | |||
8 | qemu.swtpm.enable = true; | 8 | qemu.swtpm.enable = true; |
9 | allowedBridges = ["virbr0" "rz-0971" "rz-2403"]; | 9 | allowedBridges = ["virbr0" "rz-0971" "rz-2403"]; |
10 | }; | 10 | }; |
11 | virtualisation.spiceUSBRedirection.enable = true; | ||
11 | virtualisation.libvirt = { | 12 | virtualisation.libvirt = { |
12 | enable = true; | 13 | enable = true; |
13 | swtpm.enable = true; | 14 | swtpm.enable = true; |
diff --git a/hosts/sif/mail/default.nix b/hosts/sif/mail/default.nix deleted file mode 100644 index f36cd599..00000000 --- a/hosts/sif/mail/default.nix +++ /dev/null | |||
@@ -1,70 +0,0 @@ | |||
1 | { config, 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 b8a639d5..9d3101c0 100644 --- a/hosts/surtr/default.nix +++ b/hosts/surtr/default.nix | |||
@@ -7,6 +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 ./audiobookshelf.nix ./kimai.nix | ||
10 | ]; | 11 | ]; |
11 | 12 | ||
12 | config = { | 13 | config = { |
@@ -14,9 +15,6 @@ with lib; | |||
14 | system = "x86_64-linux"; | 15 | system = "x86_64-linux"; |
15 | }; | 16 | }; |
16 | 17 | ||
17 | networking.hostId = "a64cf4d7"; | ||
18 | environment.etc."machine-id".text = "a64cf4d793ab0a0ed3892ead609fc0bc"; | ||
19 | |||
20 | boot = { | 18 | boot = { |
21 | loader.grub = { | 19 | loader.grub = { |
22 | enable = true; | 20 | enable = true; |
@@ -30,6 +28,15 @@ with lib; | |||
30 | zfs.devNodes = "/dev"; # /dev/vda2 does not show up in /dev/disk/by-id | 28 | zfs.devNodes = "/dev"; # /dev/vda2 does not show up in /dev/disk/by-id |
31 | 29 | ||
32 | kernelModules = ["ptp_kvm"]; | 30 | kernelModules = ["ptp_kvm"]; |
31 | kernelPatches = [ | ||
32 | { name = "zswap-default"; | ||
33 | patch = null; | ||
34 | extraStructuredConfig = with lib.kernel; { | ||
35 | ZSWAP_DEFAULT_ON = yes; | ||
36 | ZSWAP_SHRINKER_DEFAULT_ON = yes; | ||
37 | }; | ||
38 | } | ||
39 | ]; | ||
33 | }; | 40 | }; |
34 | 41 | ||
35 | fileSystems = { | 42 | fileSystems = { |
@@ -38,6 +45,9 @@ with lib; | |||
38 | fsType = "vfat"; | 45 | fsType = "vfat"; |
39 | }; | 46 | }; |
40 | }; | 47 | }; |
48 | swapDevices = [ | ||
49 | { label = "swap"; } | ||
50 | ]; | ||
41 | 51 | ||
42 | networking = { | 52 | networking = { |
43 | hostName = "surtr"; | 53 | hostName = "surtr"; |
@@ -163,11 +173,6 @@ with lib; | |||
163 | stateful = true; | 173 | stateful = true; |
164 | }; | 174 | }; |
165 | 175 | ||
166 | zramSwap = { | ||
167 | enable = true; | ||
168 | algorithm = "zstd"; | ||
169 | }; | ||
170 | |||
171 | systemd.sysusers.enable = false; | 176 | systemd.sysusers.enable = false; |
172 | system.etc.overlay.mutable = true; | 177 | system.etc.overlay.mutable = true; |
173 | boot.enableContainers = true; | 178 | boot.enableContainers = true; |
diff --git a/hosts/surtr/dns/Gupfile b/hosts/surtr/dns/Gupfile index ac96f620..70674cce 100644 --- a/hosts/surtr/dns/Gupfile +++ b/hosts/surtr/dns/Gupfile | |||
@@ -1,2 +1,2 @@ | |||
1 | key.gup: | 1 | key.gup: |
2 | keys/*.yaml \ No newline at end of file | 2 | keys/* \ No newline at end of file |
diff --git a/hosts/surtr/dns/default.nix b/hosts/surtr/dns/default.nix index ee1d089d..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"]; | 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"]; }; |
@@ -169,15 +169,9 @@ in { | |||
169 | { domain = "kleen.li"; | 169 | { domain = "kleen.li"; |
170 | addACLs = { "kleen.li" = ["ymir_acme_acl"]; }; | 170 | addACLs = { "kleen.li" = ["ymir_acme_acl"]; }; |
171 | } | 171 | } |
172 | { domain = "xmpp.li"; | ||
173 | addACLs = { "xmpp.li" = ["ymir_acme_acl"]; }; | ||
174 | } | ||
175 | { domain = "synapse.li"; | 172 | { domain = "synapse.li"; |
176 | acmeDomains = ["element.synapse.li" "turn.synapse.li" "synapse.li"]; | 173 | acmeDomains = ["element.synapse.li" "turn.synapse.li" "synapse.li"]; |
177 | } | 174 | } |
178 | { domain = "dirty-haskell.org"; | ||
179 | addACLs = { "dirty-haskell.org" = ["ymir_acme_acl"]; }; | ||
180 | } | ||
181 | { domain = "praseodym.org"; | 175 | { domain = "praseodym.org"; |
182 | addACLs = { "praseodym.org" = ["ymir_acme_acl"]; }; | 176 | addACLs = { "praseodym.org" = ["ymir_acme_acl"]; }; |
183 | } | 177 | } |
diff --git a/hosts/surtr/dns/key.gup b/hosts/surtr/dns/key.gup index 32d4f7d6..5b5058b3 100644 --- a/hosts/surtr/dns/key.gup +++ b/hosts/surtr/dns/key.gup | |||
@@ -3,4 +3,4 @@ | |||
3 | keyName=${${2:t}%.yaml}_key | 3 | keyName=${${2:t}%.yaml}_key |
4 | 4 | ||
5 | keymgr -t ${keyName} > $1 | 5 | keymgr -t ${keyName} > $1 |
6 | sops -p '7ED22F4AA7BB55728B643DC5471B7D88E4EF66F8,30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51' --input-type=binary --output-type=binary -e -i $1 \ No newline at end of file | 6 | sops --input-type=binary --output-type=binary -e -i $1 |
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/hledger.yggdrasil.li_acme b/hosts/surtr/dns/keys/hledger.yggdrasil.li_acme new file mode 100644 index 00000000..b3f4cfb6 --- /dev/null +++ b/hosts/surtr/dns/keys/hledger.yggdrasil.li_acme | |||
@@ -0,0 +1,24 @@ | |||
1 | { | ||
2 | "data": "ENC[AES256_GCM,data:lCj8VYJL9z29FJ154XQtxKQLwwitCRGy4krJ6u8yw2FMzoHprEpFgm33+mFspxSKk/It2G8cfTGMZSeVkYJEHb66HNKHl0A2Fz3hwjpRjh1MZAw0wiZJlnS/LNqoGstQ2PJmTQTW3aJRMoT1GS7q/gSp/3rqySA5EOm0GgUiA3Vi7nGpkBenKDEbQbcIBXRdMOk66BCdiz5XGm/1VLQQLO9oVwY2KBnLaZSISohyGVhbIy7GT2ygoWHHxHn0c5CRVNvGNwesM1gO1NnTFrISLMWSrsDPaAtQ,iv:fa8LFjzqsf2ccfbEe5MOmerb7FzXb4xr24y1GWIMT1Q=,tag:7oQ54DKBb76Pbw1lmEHt+Q==,type:str]", | ||
3 | "sops": { | ||
4 | "kms": null, | ||
5 | "gcp_kms": null, | ||
6 | "azure_kv": null, | ||
7 | "hc_vault": null, | ||
8 | "age": [ | ||
9 | { | ||
10 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzcHJLRHh0VkpDNDU3cGZS\nMWFlVWpFemZ2RnpnSllDWU1EWGoyWUxyejNzClBGandzaFI5NXY1bG51Y3VxRk9r\nc29NbXBOaEZDblBuaVowemQydkxBdjgKLS0tICtVb2xkMmh4T0Q0cU4xQnBzZExI\namFRUnRYTWIyQ3RHNUVHWTFrUzhhK1UKqmATNmxlhkxM5PP1U6w7fSYVA8AgIRAt\nJ9WZrTffQfXMdw4RmjWcoVHFH39Fe4SteedxliCCcqjkjgSEB4Rgow==\n-----END AGE ENCRYPTED FILE-----\n" | ||
12 | }, | ||
13 | { | ||
14 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
15 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjWlhpQWI4c0x0ZXRGYVE2\nR1dHZzZud0ZxQWsrREhJWUowWE1zem5FVFI0CkJBUnIwY1FGS3N2VnpuSkZjRllZ\ncVgyeVg4cTVjRitzL0RKb0ZQb3BsOEkKLS0tIGs3SDBkamVBNDhQUlh5dmVVZXJs\nM3VKdlFKc21GcFY0UUtiaHFvYWI4V0kKKuWYEncxe9NT2ZS3X3+l/gT4BQOrdCg8\nj2jGL+Yzy/356GO3PFTn2HHLam6KWDKaYB5TlK/zSohfUt5giQH2Lw==\n-----END AGE ENCRYPTED FILE-----\n" | ||
16 | } | ||
17 | ], | ||
18 | "lastmodified": "2025-02-19T17:13:51Z", | ||
19 | "mac": "ENC[AES256_GCM,data:XsBdMCBjB+YuBMZQrjJ5uZtaYKSqsdWVvm+IEoJflCKPIhPk2rBZ3nY8KngXFbq2fWgsYyTM83kb2trEGIEHUPuERt+mgfCI3bSlylriwgsDWihCjyBecNE+BbdXE0+YcNl8pIwBU4M+3f2StQMH22YamToLJ9i9kfKcBrirDuU=,iv:VTIdBVY3kVBMYWhYUmrP2vZ9rpH90DzF68y1aDf2EAs=,tag:YkL+nw6LNXAceZtx9vgf6A==,type:str]", | ||
20 | "pgp": null, | ||
21 | "unencrypted_suffix": "_unencrypted", | ||
22 | "version": "3.9.4" | ||
23 | } | ||
24 | } \ No newline at end of file | ||
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/keys/paperless.yggdrasil.li_acme b/hosts/surtr/dns/keys/paperless.yggdrasil.li_acme new file mode 100644 index 00000000..bc4640db --- /dev/null +++ b/hosts/surtr/dns/keys/paperless.yggdrasil.li_acme | |||
@@ -0,0 +1,24 @@ | |||
1 | { | ||
2 | "data": "ENC[AES256_GCM,data:wOl8KLHD0H+btq0A3UreyVF9bOXZsiTwWJkVH8GubyIQDyiDC8vQm+dfv0rz8TwcBWYpC4aMIPPflG2HsdYO4rKGQ/nBmWmxhNXjpnyRo8iKM1BGb5bxNe4eVcUVhI60NuRJDRLmtDp+0rYGT/MVYp0/mHBINsQCXWBPDoaN2PI2GSnRag/x0wcL27xgH6NDd8glcdCN5nCAPDvazA3LialkXXv7/cceA5Q/Ee6HGzPP0w212/UvBm07Z5tXnHiy5cTbAGTUBfIqC8n501jtaQhpMh/yzA1R8KwUrw==,iv:bLzsthCaanNikNS2Es4J1++E5lijEbjyW5hU4zzNBcg=,tag:eWfZ3AtcSAGv8jWXzqlAwQ==,type:str]", | ||
3 | "sops": { | ||
4 | "kms": null, | ||
5 | "gcp_kms": null, | ||
6 | "azure_kv": null, | ||
7 | "hc_vault": null, | ||
8 | "age": [ | ||
9 | { | ||
10 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNYWZKOHd2UmxWYVlIamk4\nZ04zQnAwcDdsSDBDWUZnekNva3BzRERyWXljCmNvUU1Fczc2aUN6VGl6NEJ6MGIx\nWHVpeVluWnRnbjNadGxkSmYyNE1rZzQKLS0tIGpkYVZQRDJGS21ZZHdlRk1MMm02\nQ09aanNXSWltNi9QeUNtUVQ2UEZybmMK6/qcNYLMcyKTmtROX+ZsRqDxMXwkXiAV\ndsdsWJ5+zSJuK5SEIh0fqEZ/t4pxnMcr1WieETgLSd+w0sNQS7EKPQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
12 | }, | ||
13 | { | ||
14 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
15 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByRjdJOHdjamRHVjlZOTNV\nbTBuam5vNERIWTB6T0JkR2pLUnlQN1BGbUJNClhrZ2hPRWZtT3BERFNwdmNEMmVu\nT0dxcjNkNGIvMVJQWENoUmRhTGd6SXcKLS0tIDV6WDd1bks4K1VuVkgybjdMd0w0\nMHBsT3FmOWU0WnJsM2diQm1sTU1ON2MKtf5HZ0S1cLMx98vDKRKamS7aHIJZ0OnA\nzH4VoeVm+PKsOeqVfY+gMHLdaMEWLKYsz3B8bxIoL5pvnCdT1QAN2A==\n-----END AGE ENCRYPTED FILE-----\n" | ||
16 | } | ||
17 | ], | ||
18 | "lastmodified": "2025-02-13T19:23:42Z", | ||
19 | "mac": "ENC[AES256_GCM,data:o7zNTjkohzAouYpJUGqf8DUfYf4/g3GZgc+4cf+PjI0OF8uc1WDCPvliBFe6pf/8QMhV5DFWd2SfszWnpnQhtiIVG/2BEk5sw3P6r/SUbSErakFYHueVQKp+9rdxK6uKcHUYhO46E332AwIxTuvNeHtSBMxx0kAwQPuuD/u3L4A=,iv:aiM0sGyGMk5lfBOpB2bDFCY+UfWwyUNixieww6eOSLs=,tag:MI7xJ7RsyZgQfF1SBVVmcQ==,type:str]", | ||
20 | "pgp": null, | ||
21 | "unencrypted_suffix": "_unencrypted", | ||
22 | "version": "3.9.4" | ||
23 | } | ||
24 | } \ No newline at end of file | ||
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 d42b4719..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 | 2024102100 ; 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. |
@@ -59,5 +56,3 @@ _infinoted._tcp IN SRV 5 0 6523 ymir.yggdrasil.li. | |||
59 | _submission._tcp IN SRV 5 0 25 ymir.yggdrasil.li. | 56 | _submission._tcp IN SRV 5 0 25 ymir.yggdrasil.li. |
60 | _imap._tcp IN SRV 5 0 143 ymir.yggdrasil.li. | 57 | _imap._tcp IN SRV 5 0 143 ymir.yggdrasil.li. |
61 | _imaps._tcp IN SRV 5 0 993 ymir.yggdrasil.li. | 58 | _imaps._tcp IN SRV 5 0 993 ymir.yggdrasil.li. |
62 | |||
63 | _factorio._udp IN SRV 5 0 34197 game01.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.xmpp.soa b/hosts/surtr/dns/zones/li.xmpp.soa deleted file mode 100644 index a9e98fb4..00000000 --- a/hosts/surtr/dns/zones/li.xmpp.soa +++ /dev/null | |||
@@ -1,43 +0,0 @@ | |||
1 | $ORIGIN xmpp.li. | ||
2 | $TTL 3600 | ||
3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | ||
4 | 2023013000 ; serial | ||
5 | 10800 ; refresh | ||
6 | 3600 ; retry | ||
7 | 604800 ; expire | ||
8 | 3600 ; min TTL | ||
9 | ) | ||
10 | IN NS ns.yggdrasil.li. | ||
11 | IN NS ns.inwx.de. | ||
12 | IN NS ns2.inwx.de. | ||
13 | IN NS ns3.inwx.eu. | ||
14 | |||
15 | @ IN CAA 128 issue "letsencrypt.org; validationmethods=dns-01" | ||
16 | @ IN CAA 128 iodef "mailto:caa@yggdrasil.li" | ||
17 | |||
18 | @ IN A 188.68.51.254 | ||
19 | @ IN AAAA 2a03:4000:6:d004:: | ||
20 | @ IN MX 0 ymir.yggdrasil.li. | ||
21 | @ IN TXT "v=spf1 redirect=yggdrasil.li" | ||
22 | |||
23 | * IN A 188.68.51.254 | ||
24 | * IN AAAA 2a03:4000:6:d004:: | ||
25 | * IN MX 0 ymir.yggdrasil.li. | ||
26 | * IN TXT "v=spf1 redirect=yggdrasil.li" | ||
27 | |||
28 | _acme-challenge IN NS ns.yggdrasil.li. | ||
29 | |||
30 | ymir._domainkey IN TXT ( | ||
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 | |||
36 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | ||
37 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | ||
38 | |||
39 | _infinoted._tcp IN SRV 5 0 6523 ymir.yggdrasil.li. | ||
40 | |||
41 | _submission._tcp IN SRV 5 0 25 ymir.yggdrasil.li. | ||
42 | _imap._tcp IN SRV 5 0 143 ymir.yggdrasil.li. | ||
43 | _imaps._tcp IN SRV 5 0 993 ymir.yggdrasil.li. | ||
diff --git a/hosts/surtr/dns/zones/li.yggdrasil.soa b/hosts/surtr/dns/zones/li.yggdrasil.soa index 9af6232f..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 | 2025010300 ; serial | 4 | 2025060700 ; serial |
5 | 10800 ; refresh | 5 | 10800 ; refresh |
6 | 3600 ; retry | 6 | 3600 ; retry |
7 | 604800 ; expire | 7 | 604800 ; expire |
@@ -77,12 +77,46 @@ _acme-challenge.immich IN NS ns.yggdrasil.li. | |||
77 | 77 | ||
78 | immich IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | 78 | immich IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" |
79 | 79 | ||
80 | paperless IN A 202.61.241.61 | ||
81 | paperless IN AAAA 2a03:4000:52:ada:: | ||
82 | paperless IN MX 0 surtr.yggdrasil.li | ||
83 | paperless IN TXT "v=spf1 redirect=surtr.yggdrasil.li" | ||
84 | _acme-challenge.paperless IN NS ns.yggdrasil.li. | ||
85 | |||
86 | paperless IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | ||
87 | |||
88 | hledger IN A 202.61.241.61 | ||
89 | hledger IN AAAA 2a03:4000:52:ada:: | ||
90 | hledger IN MX 0 surtr.yggdrasil.li | ||
91 | hledger IN TXT "v=spf1 redirect=surtr.yggdrasil.li" | ||
92 | _acme-challenge.hledger IN NS ns.yggdrasil.li. | ||
93 | |||
94 | hledger IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | ||
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 | |||
80 | vidhar IN AAAA 2a03:4000:52:ada:4:1:: | 112 | vidhar IN AAAA 2a03:4000:52:ada:4:1:: |
81 | vidhar IN MX 0 ymir.yggdrasil.li | 113 | vidhar IN MX 0 ymir.yggdrasil.li |
82 | vidhar IN TXT "v=spf1 redirect=yggdrasil.li" | 114 | vidhar IN TXT "v=spf1 redirect=yggdrasil.li" |
83 | 115 | ||
84 | mailout IN A 188.68.51.254 | 116 | mailout IN A 188.68.51.254 |
85 | 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:: | ||
86 | mailout IN MX 0 ymir.yggdrasil.li | 120 | mailout IN MX 0 ymir.yggdrasil.li |
87 | mailout IN TXT "v=spf1 redirect=yggdrasil.li" | 121 | mailout IN TXT "v=spf1 redirect=yggdrasil.li" |
88 | 122 | ||
@@ -104,6 +138,3 @@ _infinoted._tcp IN SRV 5 0 6523 ymir.yggdrasil.li. | |||
104 | _submission._tcp IN SRV 5 0 25 ymir.yggdrasil.li. | 138 | _submission._tcp IN SRV 5 0 25 ymir.yggdrasil.li. |
105 | _imap._tcp IN SRV 5 0 143 ymir.yggdrasil.li. | 139 | _imap._tcp IN SRV 5 0 143 ymir.yggdrasil.li. |
106 | _imaps._tcp IN SRV 5 0 993 ymir.yggdrasil.li. | 140 | _imaps._tcp IN SRV 5 0 993 ymir.yggdrasil.li. |
107 | |||
108 | game01 IN A 94.16.107.151 | ||
109 | game01 IN AAAA 2a03:4000:50:13d:34ee:a2ff:fed0:328f | ||
diff --git a/hosts/surtr/dns/zones/org.dirty-haskell.soa b/hosts/surtr/dns/zones/org.dirty-haskell.soa deleted file mode 100644 index 27f0d7f9..00000000 --- a/hosts/surtr/dns/zones/org.dirty-haskell.soa +++ /dev/null | |||
@@ -1,34 +0,0 @@ | |||
1 | $ORIGIN dirty-haskell.org. | ||
2 | $TTL 3600 | ||
3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | ||
4 | 2023013000 ; serial | ||
5 | 10800 ; refresh | ||
6 | 3600 ; retry | ||
7 | 604800 ; expire | ||
8 | 3600 ; min TTL | ||
9 | ) | ||
10 | IN NS ns.yggdrasil.li. | ||
11 | IN NS ns.inwx.de. | ||
12 | IN NS ns2.inwx.de. | ||
13 | IN NS ns3.inwx.eu. | ||
14 | |||
15 | @ IN CAA 128 issue "letsencrypt.org; validationmethods=dns-01" | ||
16 | @ IN CAA 128 iodef "mailto:caa@yggdrasil.li" | ||
17 | |||
18 | @ IN A 188.68.51.254 | ||
19 | @ IN AAAA 2a03:4000:6:d004:: | ||
20 | @ IN MX 10 ymir.yggdrasil.li. | ||
21 | @ IN TXT "v=spf1 redirect=yggdrasil.li" | ||
22 | |||
23 | * IN A 188.68.51.254 | ||
24 | * IN AAAA 2a03:4000:6:d004:: | ||
25 | * IN MX 0 ymir.yggdrasil.li. | ||
26 | * IN TXT "v=spf1 redirect=yggdrasil.li" | ||
27 | |||
28 | _acme-challenge IN NS ns.yggdrasil.li. | ||
29 | |||
30 | ymir._domainkey IN TXT ( | ||
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 | ) | ||
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 4196a8bc..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,16 +206,19 @@ 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"; |
196 | address_verify_map = "internal:address_verify_map"; | 213 | address_verify_map = "internal:address_verify_map"; |
197 | address_verify_positive_expire_time = "1h"; | 214 | address_verify_positive_expire_time = "1h"; |
198 | address_verify_positive_refresh_time = "15m"; | 215 | address_verify_positive_refresh_time = "15m"; |
199 | address_verify_negative_expire_time = "15s"; | 216 | address_verify_negative_expire_time = "5m"; |
200 | address_verify_negative_refresh_time = "5s"; | 217 | address_verify_negative_refresh_time = "1m"; |
201 | address_verify_cache_cleanup_interval = "5s"; | 218 | address_verify_cache_cleanup_interval = "12h"; |
219 | address_verify_poll_count = "\${stress?15}\${stress:30}"; | ||
202 | address_verify_poll_delay = "1s"; | 220 | address_verify_poll_delay = "1s"; |
221 | address_verify_sender_ttl = "30045s"; | ||
203 | 222 | ||
204 | smtpd_relay_restrictions = [ | 223 | smtpd_relay_restrictions = [ |
205 | "check_ccert_access ${relay_ccert}" | 224 | "check_ccert_access ${relay_ccert}" |
@@ -213,7 +232,7 @@ in { | |||
213 | smtpd_client_event_limit_exceptions = ""; | 232 | smtpd_client_event_limit_exceptions = ""; |
214 | 233 | ||
215 | milter_default_action = "accept"; | 234 | milter_default_action = "accept"; |
216 | 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"]; |
217 | 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"]; |
218 | 237 | ||
219 | alias_maps = ""; | 238 | alias_maps = ""; |
@@ -235,11 +254,6 @@ in { | |||
235 | ::/0 silent-discard, dsn | 254 | ::/0 silent-discard, dsn |
236 | ''}"; | 255 | ''}"; |
237 | 256 | ||
238 | sender_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.forwardPort}"; | ||
239 | sender_canonical_classes = "envelope_sender"; | ||
240 | recipient_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.reversePort}"; | ||
241 | recipient_canonical_classes = ["envelope_recipient" "header_recipient"]; | ||
242 | |||
243 | virtual_mailbox_domains = ''pgsql:${pkgs.writeText "virtual_mailbox_domains.cf" '' | 257 | virtual_mailbox_domains = ''pgsql:${pkgs.writeText "virtual_mailbox_domains.cf" '' |
244 | hosts = postgresql:///email | 258 | hosts = postgresql:///email |
245 | dbname = email | 259 | dbname = email |
@@ -254,11 +268,24 @@ in { | |||
254 | virtual_transport = "dvlmtp:unix:/run/dovecot-lmtp"; | 268 | virtual_transport = "dvlmtp:unix:/run/dovecot-lmtp"; |
255 | smtputf8_enable = false; | 269 | smtputf8_enable = false; |
256 | 270 | ||
257 | 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= }"; | ||
258 | 274 | ||
259 | postscreen_access_list = ""; | 275 | postscreen_access_list = ""; |
260 | postscreen_denylist_action = "drop"; | 276 | postscreen_denylist_action = "drop"; |
261 | 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 | ''}''; | ||
262 | }; | 289 | }; |
263 | masterConfig = { | 290 | masterConfig = { |
264 | "465" = { | 291 | "465" = { |
@@ -283,7 +310,7 @@ in { | |||
283 | hosts = postgresql:///email | 310 | hosts = postgresql:///email |
284 | dbname = email | 311 | dbname = email |
285 | 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')) |
286 | ''},permit_tls_all_clientcerts,reject}'' | 313 | ''},check_policy_service unix:/run/postfix-internal-policy.sock,permit_tls_all_clientcerts,reject}'' |
287 | "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject" | 314 | "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject" |
288 | "-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}" |
289 | "-o" "unverified_sender_reject_code=550" | 316 | "-o" "unverified_sender_reject_code=550" |
@@ -313,7 +340,7 @@ in { | |||
313 | hosts = postgresql:///email | 340 | hosts = postgresql:///email |
314 | dbname = email | 341 | dbname = email |
315 | 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')) |
316 | ''},permit_sasl_authenticated,reject}'' | 343 | ''},check_policy_service unix:/run/postfix-internal-policy.sock,permit_sasl_authenticated,reject}'' |
317 | "-o" "smtpd_relay_restrictions=permit_sasl_authenticated,reject" | 344 | "-o" "smtpd_relay_restrictions=permit_sasl_authenticated,reject" |
318 | "-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}" |
319 | "-o" "unverified_sender_reject_code=550" | 346 | "-o" "unverified_sender_reject_code=550" |
@@ -364,17 +391,19 @@ in { | |||
364 | 391 | ||
365 | services.postsrsd = { | 392 | services.postsrsd = { |
366 | enable = true; | 393 | enable = true; |
367 | domain = "surtr.yggdrasil.li"; | 394 | domains = [ "surtr.yggdrasil.li" ] ++ concatMap (domain: [".${domain}" domain]) emailDomains; |
368 | separator = "+"; | 395 | separator = "+"; |
369 | excludeDomains = [ "surtr.yggdrasil.li" | 396 | extraConfig = '' |
370 | ] ++ concatMap (domain: [".${domain}" domain]) emailDomains; | 397 | socketmap = unix:/run/postsrsd/postsrsd-socketmap.sock |
398 | milter = unix:/run/postsrsd/postsrsd-milter.sock | ||
399 | ''; | ||
371 | }; | 400 | }; |
372 | 401 | ||
373 | services.opendkim = { | 402 | services.opendkim = { |
374 | enable = true; | 403 | enable = true; |
375 | user = "postfix"; group = "postfix"; | 404 | user = "postfix"; group = "postfix"; |
376 | socket = "local:/run/opendkim/opendkim.sock"; | 405 | socket = "local:/run/opendkim/opendkim.sock"; |
377 | 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)}''; |
378 | selector = "surtr"; | 407 | selector = "surtr"; |
379 | configFile = builtins.toFile "opendkim.conf" '' | 408 | configFile = builtins.toFile "opendkim.conf" '' |
380 | Syslog true | 409 | Syslog true |
@@ -484,6 +513,7 @@ in { | |||
484 | 513 | ||
485 | users.groups.${config.services.redis.servers.rspamd.user}.members = [ config.services.rspamd.user ]; | 514 | users.groups.${config.services.redis.servers.rspamd.user}.members = [ config.services.rspamd.user ]; |
486 | 515 | ||
516 | environment.systemPackages = with pkgs; [ dovecot_pigeonhole dovecot_fts_xapian ]; | ||
487 | services.dovecot2 = { | 517 | services.dovecot2 = { |
488 | enable = true; | 518 | enable = true; |
489 | enablePAM = false; | 519 | enablePAM = false; |
@@ -491,7 +521,6 @@ in { | |||
491 | sslServerKey = "/run/credentials/dovecot2.service/surtr.yggdrasil.li.key.pem"; | 521 | sslServerKey = "/run/credentials/dovecot2.service/surtr.yggdrasil.li.key.pem"; |
492 | sslCACert = toString ./ca/ca.crt; | 522 | sslCACert = toString ./ca/ca.crt; |
493 | mailLocation = "maildir:/var/lib/mail/%u/maildir:UTF-8:INDEX=/var/lib/dovecot/indices/%u"; | 523 | mailLocation = "maildir:/var/lib/mail/%u/maildir:UTF-8:INDEX=/var/lib/dovecot/indices/%u"; |
494 | modules = with pkgs; [ dovecot_pigeonhole dovecot_fts_xapian ]; | ||
495 | mailPlugins.globally.enable = [ "fts" "fts_xapian" ]; | 524 | mailPlugins.globally.enable = [ "fts" "fts_xapian" ]; |
496 | protocols = [ "lmtp" "sieve" ]; | 525 | protocols = [ "lmtp" "sieve" ]; |
497 | sieve = { | 526 | sieve = { |
@@ -673,7 +702,7 @@ in { | |||
673 | plugin { | 702 | plugin { |
674 | plugin = fts fts_xapian | 703 | plugin = fts fts_xapian |
675 | fts = xapian | 704 | fts = xapian |
676 | fts_xapian = partial=2 full=20 attachments=1 verbose=1 | 705 | fts_xapian = partial=3 full=20 attachments=1 verbose=1 |
677 | 706 | ||
678 | fts_autoindex = yes | 707 | fts_autoindex = yes |
679 | 708 | ||
@@ -693,7 +722,7 @@ in { | |||
693 | startAt = "*-*-* 22:00:00 Europe/Berlin"; | 722 | startAt = "*-*-* 22:00:00 Europe/Berlin"; |
694 | serviceConfig = { | 723 | serviceConfig = { |
695 | Type = "oneshot"; | 724 | Type = "oneshot"; |
696 | ExecStart = "${pkgs.dovecot}/bin/doveadm fts optimize -A"; | 725 | ExecStart = "${getExe' pkgs.dovecot "doveadm"} fts optimize -A"; |
697 | PrivateDevices = true; | 726 | PrivateDevices = true; |
698 | PrivateNetwork = true; | 727 | PrivateNetwork = true; |
699 | ProtectKernelTunables = true; | 728 | ProtectKernelTunables = true; |
@@ -778,7 +807,7 @@ in { | |||
778 | systemd.services.dovecot2 = { | 807 | systemd.services.dovecot2 = { |
779 | preStart = '' | 808 | preStart = '' |
780 | 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 |
781 | ${pkgs.dovecot_pigeonhole}/bin/sievec $f | 810 | ${getExe' pkgs.dovecot_pigeonhole "sievec"} $f |
782 | done | 811 | done |
783 | ''; | 812 | ''; |
784 | 813 | ||
@@ -845,15 +874,16 @@ in { | |||
845 | charset utf-8; | 874 | charset utf-8; |
846 | source_charset utf-8; | 875 | source_charset utf-8; |
847 | ''; | 876 | ''; |
848 | root = pkgs.runCommand "mta-sts.${domain}" {} '' | 877 | root = pkgs.writeTextFile { |
849 | mkdir -p $out/.well-known | 878 | name = "mta-sts.${domain}"; |
850 | cp ${pkgs.writeText "mta-sts.${domain}.txt" '' | 879 | destination = "/.well-known/mta-sts.txt"; |
880 | text = '' | ||
851 | version: STSv1 | 881 | version: STSv1 |
852 | mode: enforce | 882 | mode: enforce |
853 | max_age: 2419200 | 883 | max_age: 2419200 |
854 | mx: mailin.${domain} | 884 | mx: mailin.${domain} |
855 | ''} $out/.well-known/mta-sts.txt | 885 | ''; |
856 | ''; | 886 | }; |
857 | }; | 887 | }; |
858 | }) emailDomains); | 888 | }) emailDomains); |
859 | }; | 889 | }; |
@@ -870,7 +900,7 @@ in { | |||
870 | systemd.services.spm = { | 900 | systemd.services.spm = { |
871 | serviceConfig = { | 901 | serviceConfig = { |
872 | Type = "notify"; | 902 | Type = "notify"; |
873 | ExecStart = "${pkgs.spm}/bin/spm-server"; | 903 | ExecStart = getExe' pkgs.spm "spm-server"; |
874 | User = "spm"; | 904 | User = "spm"; |
875 | Group = "spm"; | 905 | Group = "spm"; |
876 | 906 | ||
@@ -928,7 +958,7 @@ in { | |||
928 | serviceConfig = { | 958 | serviceConfig = { |
929 | Type = "notify"; | 959 | Type = "notify"; |
930 | 960 | ||
931 | ExecStart = "${ccert-policy-server}/bin/ccert-policy-server"; | 961 | ExecStart = getExe' ccert-policy-server "ccert-policy-server"; |
932 | 962 | ||
933 | Environment = [ | 963 | Environment = [ |
934 | "PGDATABASE=email" | 964 | "PGDATABASE=email" |
@@ -961,6 +991,53 @@ in { | |||
961 | }; | 991 | }; |
962 | users.groups."postfix-ccert-sender-policy" = {}; | 992 | users.groups."postfix-ccert-sender-policy" = {}; |
963 | 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 | |||
964 | services.postfwd = { | 1041 | services.postfwd = { |
965 | enable = true; | 1042 | enable = true; |
966 | 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/hledger.nix b/hosts/surtr/hledger.nix new file mode 100644 index 00000000..e44933c3 --- /dev/null +++ b/hosts/surtr/hledger.nix | |||
@@ -0,0 +1,66 @@ | |||
1 | { config, ... }: | ||
2 | |||
3 | { | ||
4 | config = { | ||
5 | security.acme.rfc2136Domains = { | ||
6 | "hledger.yggdrasil.li" = { | ||
7 | restartUnits = ["nginx.service"]; | ||
8 | }; | ||
9 | }; | ||
10 | |||
11 | services.nginx = { | ||
12 | upstreams."hledger" = { | ||
13 | servers = { | ||
14 | "[2a03:4000:52:ada:4:1::]:5000" = {}; | ||
15 | }; | ||
16 | extraConfig = '' | ||
17 | keepalive 8; | ||
18 | ''; | ||
19 | }; | ||
20 | virtualHosts = { | ||
21 | "hledger.yggdrasil.li" = { | ||
22 | kTLS = true; | ||
23 | http3 = true; | ||
24 | forceSSL = true; | ||
25 | sslCertificate = "/run/credentials/nginx.service/hledger.yggdrasil.li.pem"; | ||
26 | sslCertificateKey = "/run/credentials/nginx.service/hledger.yggdrasil.li.key.pem"; | ||
27 | sslTrustedCertificate = "/run/credentials/nginx.service/hledger.yggdrasil.li.chain.pem"; | ||
28 | extraConfig = '' | ||
29 | charset utf-8; | ||
30 | ''; | ||
31 | |||
32 | locations = { | ||
33 | "/".extraConfig = '' | ||
34 | proxy_pass http://hledger; | ||
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 | "hledger.yggdrasil.li.key.pem:${config.security.acme.certs."hledger.yggdrasil.li".directory}/key.pem" | ||
60 | "hledger.yggdrasil.li.pem:${config.security.acme.certs."hledger.yggdrasil.li".directory}/fullchain.pem" | ||
61 | "hledger.yggdrasil.li.chain.pem:${config.security.acme.certs."hledger.yggdrasil.li".directory}/chain.pem" | ||
62 | ]; | ||
63 | }; | ||
64 | }; | ||
65 | }; | ||
66 | } | ||
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/paperless.nix b/hosts/surtr/paperless.nix new file mode 100644 index 00000000..7bc4397c --- /dev/null +++ b/hosts/surtr/paperless.nix | |||
@@ -0,0 +1,66 @@ | |||
1 | { config, ... }: | ||
2 | |||
3 | { | ||
4 | config = { | ||
5 | security.acme.rfc2136Domains = { | ||
6 | "paperless.yggdrasil.li" = { | ||
7 | restartUnits = ["nginx.service"]; | ||
8 | }; | ||
9 | }; | ||
10 | |||
11 | services.nginx = { | ||
12 | upstreams."paperless" = { | ||
13 | servers = { | ||
14 | "[2a03:4000:52:ada:4:1::]:28981" = {}; | ||
15 | }; | ||
16 | extraConfig = '' | ||
17 | keepalive 8; | ||
18 | ''; | ||
19 | }; | ||
20 | virtualHosts = { | ||
21 | "paperless.yggdrasil.li" = { | ||
22 | kTLS = true; | ||
23 | http3 = true; | ||
24 | forceSSL = true; | ||
25 | sslCertificate = "/run/credentials/nginx.service/paperless.yggdrasil.li.pem"; | ||
26 | sslCertificateKey = "/run/credentials/nginx.service/paperless.yggdrasil.li.key.pem"; | ||
27 | sslTrustedCertificate = "/run/credentials/nginx.service/paperless.yggdrasil.li.chain.pem"; | ||
28 | extraConfig = '' | ||
29 | charset utf-8; | ||
30 | ''; | ||
31 | |||
32 | locations = { | ||
33 | "/".extraConfig = '' | ||
34 | proxy_pass http://paperless; | ||
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 | "paperless.yggdrasil.li.key.pem:${config.security.acme.certs."paperless.yggdrasil.li".directory}/key.pem" | ||
60 | "paperless.yggdrasil.li.pem:${config.security.acme.certs."paperless.yggdrasil.li".directory}/fullchain.pem" | ||
61 | "paperless.yggdrasil.li.chain.pem:${config.security.acme.certs."paperless.yggdrasil.li".directory}/chain.pem" | ||
62 | ]; | ||
63 | }; | ||
64 | }; | ||
65 | }; | ||
66 | } | ||
diff --git a/hosts/surtr/postgresql/default.nix b/hosts/surtr/postgresql/default.nix index 583e4443..3786ea7c 100644 --- a/hosts/surtr/postgresql/default.nix +++ b/hosts/surtr/postgresql/default.nix | |||
@@ -89,6 +89,10 @@ in { | |||
89 | "d /var/spool/pgbackrest 0750 postgres postgres - -" | 89 | "d /var/spool/pgbackrest 0750 postgres postgres - -" |
90 | ]; | 90 | ]; |
91 | 91 | ||
92 | systemd.services.postgresql.serviceConfig = { | ||
93 | ReadWritePaths = [ "/var/spool/pgbackrest" "/var/lib/pgbackrest/archive/surtr" ]; | ||
94 | }; | ||
95 | |||
92 | systemd.services.migrate-postgresql = { | 96 | systemd.services.migrate-postgresql = { |
93 | after = [ "postgresql.service" ]; | 97 | after = [ "postgresql.service" ]; |
94 | bindsTo = [ "postgresql.service" ]; | 98 | bindsTo = [ "postgresql.service" ]; |
@@ -276,6 +280,64 @@ in { | |||
276 | 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; |
277 | 281 | ||
278 | 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; | ||
279 | ''} | 341 | ''} |
280 | 342 | ||
281 | psql etebase postgres -eXf ${pkgs.writeText "etebase.sql" '' | 343 | psql etebase postgres -eXf ${pkgs.writeText "etebase.sql" '' |
diff --git a/hosts/surtr/tls/tsig_key.gup b/hosts/surtr/tls/tsig_key.gup index 825479e5..46a3789e 100644 --- a/hosts/surtr/tls/tsig_key.gup +++ b/hosts/surtr/tls/tsig_key.gup | |||
@@ -3,4 +3,4 @@ | |||
3 | keyFile=../dns/keys/${2:t}_acme | 3 | keyFile=../dns/keys/${2:t}_acme |
4 | gup -u $keyFile | 4 | gup -u $keyFile |
5 | sops -d --input-type=binary --output-type=binary ${keyFile} | yq -r '.key[0].secret' > $1 | 5 | sops -d --input-type=binary --output-type=binary ${keyFile} | yq -r '.key[0].secret' > $1 |
6 | sops -p '7ED22F4AA7BB55728B643DC5471B7D88E4EF66F8,30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51' --input-type=binary -e -i $1 | 6 | sops --input-type=binary -e -i $1 |
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/hledger.yggdrasil.li b/hosts/surtr/tls/tsig_keys/hledger.yggdrasil.li new file mode 100644 index 00000000..ab6cdd68 --- /dev/null +++ b/hosts/surtr/tls/tsig_keys/hledger.yggdrasil.li | |||
@@ -0,0 +1,24 @@ | |||
1 | { | ||
2 | "data": "ENC[AES256_GCM,data:Yd70QIj9DE6a5IN+Mf2M5p95vkRMHRg9BXaM686W7BRtthOw9m54/5FK6JWr,iv:cIOIKinkqFFPgTZdewWVY0h6kM5hGfVzuA4iYNhwK5c=,tag:Ds/oI+TOERbIdcGbI4WoEg==,type:str]", | ||
3 | "sops": { | ||
4 | "kms": null, | ||
5 | "gcp_kms": null, | ||
6 | "azure_kv": null, | ||
7 | "hc_vault": null, | ||
8 | "age": [ | ||
9 | { | ||
10 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1NnJ4SmsvSlh3ZlliSm9C\nNG45clh3NEZWZ05jaHhVK0xqTVRFL2wxN1ZrCk5hL2p2ZjhtcDBjTEZscXFTNkY5\nemVZSUUwV2V5cFBTdWo4RWxsM2xROVEKLS0tIDBFSFlkUVJ0ajJEUENlelVFKzVk\ndnJhMURMU2o3WVBKZGNVRXBiNytqUFEKHivcSTYy5D770C0h7RsmLBmkIG9+MDoV\ngJHvfkGzXPKwmDTMKdHbIk+ctI+u0/1jMn/K2Q9OFnOIYxP3gHiFag==\n-----END AGE ENCRYPTED FILE-----\n" | ||
12 | }, | ||
13 | { | ||
14 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
15 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArYUNCQ1U2MXpQdmM4SHR0\nTnJWZlFGTUQ1NjVqZ3Z2MGt5NFpBVFp0b0YwCkdseitkbzI1RkZyL3V5Z1pNMG9R\nVnEzRUxrTDQrS3BiMXB1QUFPeUcxZUkKLS0tIGJFODkwNFY3c0tBLzFBNjFiYjJk\nZW82bTdia2F1NHpNSG1IYmhWb1ZCTHMK9ovFx3+x5PrV4y6+RH5XA5DK2wRPXlAt\ncxxpRZIlmnvhZXIeCYE9yhHFmz3uAn0Oib1RUDblca9FlnF9tyYD6g==\n-----END AGE ENCRYPTED FILE-----\n" | ||
16 | } | ||
17 | ], | ||
18 | "lastmodified": "2025-02-19T17:13:51Z", | ||
19 | "mac": "ENC[AES256_GCM,data:ReRuK9qdZV8AbMzA9Yur0AZW+1RF3aRnfBvsKJkQtXsFdkmJQ4QkRGtL27RmjFdvQ3kXBIyhib7hYA60AJ0amduYrSScY0dtz8AurjyE4f2BGQ9/QeKRBfKXHxLvj4/xWNvS4+PVdGKkKbqIs8isz9n77WQQ3lTHop2K/TjaTuQ=,iv:gUhDK9oeUHdpQ2Fp8mFDIgPFo2JjHE0jjooL7FmvmrE=,tag:2lkwSl3j3oqamdLbM9wbow==,type:str]", | ||
20 | "pgp": null, | ||
21 | "unencrypted_suffix": "_unencrypted", | ||
22 | "version": "3.9.4" | ||
23 | } | ||
24 | } \ No newline at end of file | ||
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/tls/tsig_keys/paperless.yggdrasil.li b/hosts/surtr/tls/tsig_keys/paperless.yggdrasil.li new file mode 100644 index 00000000..b1029931 --- /dev/null +++ b/hosts/surtr/tls/tsig_keys/paperless.yggdrasil.li | |||
@@ -0,0 +1,24 @@ | |||
1 | { | ||
2 | "data": "ENC[AES256_GCM,data:D9l0pklD2KDZ4/TXHtXg00MmCnjCVVBG0AK9j5OxxBCyYseCTckp2P/iPOng,iv:DjvuKWPr/jldfk0eZ5+jWHN0RurdruR4Md7AMAPzRQg=,tag:h0c4m3hpATzzb6a7DVmi9w==,type:str]", | ||
3 | "sops": { | ||
4 | "kms": null, | ||
5 | "gcp_kms": null, | ||
6 | "azure_kv": null, | ||
7 | "hc_vault": null, | ||
8 | "age": [ | ||
9 | { | ||
10 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3S05aS0ZEcVU2T3BIOG13\nSDJLTUp4OG9ZYVluK2gxbUJDSFRaQ0xnS0YwClFkKzByanJGOWFwbTlISndyU0Rx\nN1FJb3FVaUZOVDBsWEdHTVNGaGNtMVkKLS0tIDI1RmxaMlhVd1FPL1dNdlRGK0Nq\nMFpJZTNnWncrbkV5YS82ZnhGRld0UG8KIuf7bC7GVxaGeR7gwC7kGu/wtBppjq4H\nyDT05CYJf9/EE3K5aJpIOlxyqowRs2SINvIVkyd5ggYkkxCctmGXjQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
12 | }, | ||
13 | { | ||
14 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
15 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBUV3ZqTVJkNWdHeTlKK3Vl\nVTdrdzE2Z3YzVmZxelNFMDNMTUJneHNtNzFRCmQxTU84ekV4bFVLajU0ajB6ZzZK\neEpuQTcyS1o4MW9xTU5nMXVUR0gxTmcKLS0tIGVzZC8xYTc1VkU2RzA2NFQ1K2xz\nLzhPVjBUcytWNGRsdnFob3A4aWljelkKFMlmigcEVzelcEiv6WGya1dsIOJYr7YT\naBHgMttV7zzYHLqvIVJSCz+uw2FqDyqN46twmzFC0HSHeiKbvRrHVw==\n-----END AGE ENCRYPTED FILE-----\n" | ||
16 | } | ||
17 | ], | ||
18 | "lastmodified": "2025-02-13T19:23:42Z", | ||
19 | "mac": "ENC[AES256_GCM,data:0Fcgq0pOZtBBSiK8pUr/jadXMdtbZYFhUbSe+7DQpB8Fo2r8cEoT+Cpcy7tu+l9eXUiDk/tXTBJyMXaW4XWwS/Fe6Zcb95UYaYR1Y6OM9JVPYmwd6QSeC13MwzhYaCDlBiWWq69Zn8grEg7npWo/LS9LK7IEbN7EI8o7QYDI6cw=,iv:C+8ZmVTNWySQ+/6j+YirSwZzoMqXRlgstk47Efxmqps=,tag:Be/6Ve6M/Dcm/6QrbF+JTw==,type:str]", | ||
20 | "pgp": null, | ||
21 | "unencrypted_suffix": "_unencrypted", | ||
22 | "version": "3.9.4" | ||
23 | } | ||
24 | } \ No newline at end of file | ||
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/surtr/zfs.nix b/hosts/surtr/zfs.nix index 17c5cd32..3795956d 100644 --- a/hosts/surtr/zfs.nix +++ b/hosts/surtr/zfs.nix | |||
@@ -49,7 +49,7 @@ | |||
49 | 49 | ||
50 | boot.postBootCommands = '' | 50 | boot.postBootCommands = '' |
51 | echo "=== STARTING ZPOOL IMPORT ===" | 51 | echo "=== STARTING ZPOOL IMPORT ===" |
52 | ${pkgs.zfs}/bin/zpool import -a -N -d /dev | 52 | ${pkgs.zfs}/bin/zpool import -a -f -N -d /dev |
53 | ${pkgs.zfs}/bin/zpool status | 53 | ${pkgs.zfs}/bin/zpool status |
54 | ${pkgs.zfs}/bin/zfs mount -a | 54 | ${pkgs.zfs}/bin/zfs mount -a |
55 | echo "=== ZPOOL IMPORT COMPLETE ===" | 55 | echo "=== ZPOOL IMPORT COMPLETE ===" |
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 b0797d8a..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 | 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 |
diff --git a/hosts/vidhar/hledger/default.nix b/hosts/vidhar/hledger/default.nix new file mode 100644 index 00000000..ae080f66 --- /dev/null +++ b/hosts/vidhar/hledger/default.nix | |||
@@ -0,0 +1,83 @@ | |||
1 | { config, lib, pkgs, ... }: | ||
2 | { | ||
3 | config = { | ||
4 | services.hledger-web = { | ||
5 | enable = true; | ||
6 | allow = "view"; | ||
7 | stateDir = "/var/lib/hledger"; | ||
8 | journalFiles = lib.mkForce ["web.journal"]; | ||
9 | baseUrl = "https://hledger.yggdrasil.li"; | ||
10 | extraOptions = [ | ||
11 | "--socket=/run/hledger-web/http.sock" | ||
12 | ]; | ||
13 | }; | ||
14 | users = { | ||
15 | users.hledger.uid = 982; | ||
16 | groups.hledger.gid = 979; | ||
17 | }; | ||
18 | systemd.services.hledger-web = { | ||
19 | serviceConfig = { | ||
20 | UMask = "0002"; | ||
21 | ReadOnlyPaths = [ config.services.hledger-web.stateDir ]; | ||
22 | RuntimeDirectory = [ "hledger-web" ]; | ||
23 | PrivateDevices = true; | ||
24 | StateDirectory = "hledger"; | ||
25 | CapabilityBoundingSet = ""; | ||
26 | AmbientCapabilities = ""; | ||
27 | ProtectSystem = "strict"; | ||
28 | ProtectKernelTunables = true; | ||
29 | ProtectKernelModules = true; | ||
30 | ProtectControlGroups = true; | ||
31 | ProtectClock = true; | ||
32 | ProtectHostname = true; | ||
33 | ProtectHome = "tmpfs"; | ||
34 | ProtectKernelLogs = true; | ||
35 | ProtectProc = "invisible"; | ||
36 | ProcSubset = "pid"; | ||
37 | PrivateNetwork = false; | ||
38 | RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX"; | ||
39 | SystemCallArchitectures = "native"; | ||
40 | SystemCallFilter = [ | ||
41 | "@system-service @resources" | ||
42 | "~@obsolete @privileged" | ||
43 | ]; | ||
44 | RestrictSUIDSGID = true; | ||
45 | RemoveIPC = true; | ||
46 | NoNewPrivileges = true; | ||
47 | RestrictRealtime = true; | ||
48 | RestrictNamespaces = true; | ||
49 | LockPersonality = true; | ||
50 | PrivateUsers = true; | ||
51 | TemporaryFileSystem = [ "/var/lib/hledger/.cache:mode=0750,uid=${toString (config.users.users.hledger.uid)},gid=${toString (config.users.groups.hledger.gid)}" ]; | ||
52 | }; | ||
53 | }; | ||
54 | services.nginx = { | ||
55 | upstreams.hledger = { | ||
56 | servers = { "unix:/run/hledger-web/http.sock" = {}; }; | ||
57 | }; | ||
58 | virtualHosts."hledger.yggdrasil.li" = { | ||
59 | listen = [ | ||
60 | { addr = "[2a03:4000:52:ada:4:1::]"; port = 5000; } | ||
61 | ]; | ||
62 | extraConfig = '' | ||
63 | set_real_ip_from 2a03:4000:52:ada:4::; | ||
64 | auth_basic "hledger"; | ||
65 | auth_basic_user_file "/run/credentials/nginx.service/hledger_users"; | ||
66 | ''; | ||
67 | locations."/" = { | ||
68 | proxyPass = "http://hledger/"; | ||
69 | proxyWebsockets = true; | ||
70 | }; | ||
71 | }; | ||
72 | }; | ||
73 | systemd.services.nginx.serviceConfig = { | ||
74 | SupplementaryGroups = [ "hledger" ]; | ||
75 | LoadCredential = [ "hledger_users:${config.sops.secrets."hledger_users".path}" ]; | ||
76 | }; | ||
77 | sops.secrets."hledger_users" = { | ||
78 | format = "binary"; | ||
79 | sopsFile = ./htpasswd; | ||
80 | reloadUnits = [ "nginx.service" ]; | ||
81 | }; | ||
82 | }; | ||
83 | } | ||
diff --git a/hosts/vidhar/hledger/htpasswd b/hosts/vidhar/hledger/htpasswd new file mode 100644 index 00000000..016cb525 --- /dev/null +++ b/hosts/vidhar/hledger/htpasswd | |||
@@ -0,0 +1,24 @@ | |||
1 | { | ||
2 | "data": "ENC[AES256_GCM,data:9MNDIAc7ePYk3xQDorX2pU8ybJkJb33RKiJxc2DYauXFNQYxtGwCYhZwod7p7fPh3KqZxBNMRoZXr+/RnV+trsqjAcOOjnXTWLbX6nubq/xm+q0BxEjOPn7FvJF9XOblBeupldo+byGh2CMH9qQv5Fov,iv:3Tym+Mfr48OJet3qDFZPg0XjYr4sNQdNdiu0vUxmzbY=,tag:E0sxRY/jeMVlqH6uAYvD/Q==,type:str]", | ||
3 | "sops": { | ||
4 | "kms": null, | ||
5 | "gcp_kms": null, | ||
6 | "azure_kv": null, | ||
7 | "hc_vault": null, | ||
8 | "age": [ | ||
9 | { | ||
10 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3eFBsOEM2ZUNVT2V3LytC\nTUJvUDdKc0VzMyt2cDFKYU03djBjZVFpeVY4CjByMXhPVXRJVjhKQWZvQ2xuOTE3\ncXdJV1lZaHR3cVl0Z0hQaG00M2dGbjQKLS0tIEIzenVxb3cwM3pXTUl1YUZlSlk2\nbDc3VmE5NkEyZ2tRd01OUGZibmhtUlEKxdesIdvzm8s0SmXU5R+tSbmS5Dj24jrb\nEiMERYy1g8GyHR3d2/mU5iOIdsBegSZReUVzomaMT9L7/TmubgOP3g==\n-----END AGE ENCRYPTED FILE-----\n" | ||
12 | }, | ||
13 | { | ||
14 | "recipient": "age1qffdqvy9arld9zd5a5cylt0n98xhcns5shxhrhwjq5g4qa844ejselaa4l", | ||
15 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPa2RDZzR6cEFYTFA1QkND\nbndVeHVrMVJ0MWZvRmw5VXRhOHlRYllIRWxRCjU4dks4R25LS1RZMHFnbmpQRVZz\nNXhubkJvZFc2amRwMDVtQlE0NnBKNzQKLS0tIHRyeDUxTEFPMEMzWUVkZURzODdm\nSHdqbUpvNmFTS1QveFRpRHdnWHpHb28KnvdUkMkKGiBVHQD7Yv7n6WZjihCGJAR2\nMKl2WAn4g4jzgcXPwwIAIjUrMGSIdGpwCTUDcDnlKWAbRYO2B6P17A==\n-----END AGE ENCRYPTED FILE-----\n" | ||
16 | } | ||
17 | ], | ||
18 | "lastmodified": "2025-02-19T17:11:17Z", | ||
19 | "mac": "ENC[AES256_GCM,data:yBIEqHhr4igoMlRcgg2SigKfejqeuNmuleYolsLJo+QOaW4BHITJTvLxRV1JHPpcMVQkF//zx4ZfUUrb8tTN0znGu3Jnpd0JVagbfCVyEuT6d1SB/GzyUVvoQ2GlcA9us+5gjI4oEJTQCfVqnLDBWsw+jXdr3nEIWo6Mvbqo3lI=,iv:I6Swk4wyd+96+tJKRY/FHlS7ZShMDROcbl+l+ZLRxhM=,tag:P1uQvB4NLdkPEKRMI6lLxw==,type:str]", | ||
20 | "pgp": null, | ||
21 | "unencrypted_suffix": "_unencrypted", | ||
22 | "version": "3.9.4" | ||
23 | } | ||
24 | } \ No newline at end of file | ||
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 07a83351..11460393 100644 --- a/hosts/vidhar/network/dhcp/default.nix +++ b/hosts/vidhar/network/dhcp/default.nix | |||
@@ -1,8 +1,33 @@ | |||
1 | { flake, config, pkgs, lib, ... }: | 1 | { flake, config, pkgs, lib, sources, ... }: |
2 | 2 | ||
3 | with lib; | 3 | with lib; |
4 | 4 | ||
5 | { | 5 | let |
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 | }; | ||
30 | in { | ||
6 | config = { | 31 | config = { |
7 | services.kea = { | 32 | services.kea = { |
8 | dhcp4 = { | 33 | dhcp4 = { |
@@ -23,41 +48,67 @@ with lib; | |||
23 | }; | 48 | }; |
24 | 49 | ||
25 | client-classes = [ | 50 | client-classes = [ |
26 | { name = "eostre-ipxe"; | 51 | { name = "ipxe-eostre"; |
27 | 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'"; |
28 | next-server = "10.141.0.1"; | 53 | next-server = tftpIp; |
29 | boot-file-name = "http://nfsroot.vidhar.yggdrasil/eostre/netboot.ipxe"; | 54 | boot-file-name = "${nfsrootBaseUrl}/eostre.menu.ipxe"; |
55 | only-if-required = true; | ||
56 | } | ||
57 | { name = "ipxe-yggdrasil"; | ||
58 | test = "option[77].hex == 'iPXE-yggdrasil'"; | ||
59 | next-server = tftpIp; | ||
60 | boot-file-name = "${nfsrootBaseUrl}/installer-x86_64-linux.menu.ipxe"; | ||
61 | only-if-required = true; | ||
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"; | ||
30 | only-if-required = true; | 81 | only-if-required = true; |
31 | } | 82 | } |
32 | { name = "ipxe"; | 83 | { name = "ipxe-legacy"; |
33 | test = "option[77].hex == 'iPXE'"; | 84 | test = "option[77].hex == 'iPXE' and substring(option[60].hex,0,20) == 'PXEClient:Arch:00000'"; |
34 | next-server = "10.141.0.1"; | 85 | boot-file-name = "${nfsrootBaseUrl}/ipxe.lkrn"; |
35 | boot-file-name = "http://nfsroot.vidhar.yggdrasil/installer-x86_64-linux/netboot.ipxe"; | ||
36 | only-if-required = true; | 86 | only-if-required = true; |
37 | } | 87 | } |
88 | |||
38 | { name = "uefi-64"; | 89 | { name = "uefi-64"; |
39 | 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'"; |
40 | only-if-required = true; | ||
41 | option-data = [ | 91 | option-data = [ |
42 | { name = "tftp-server-name"; data = "10.141.0.1"; } | 92 | { name = "tftp-server-name"; data = tftpIp; } |
43 | ]; | 93 | ]; |
44 | boot-file-name = "ipxe.efi"; | 94 | boot-file-name = "ipxe.efi"; |
95 | only-if-required = true; | ||
45 | } | 96 | } |
46 | { name = "uefi-32"; | 97 | { name = "uefi-32"; |
47 | 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'"; |
48 | only-if-required = true; | ||
49 | option-data = [ | 99 | option-data = [ |
50 | { name = "tftp-server-name"; data = "10.141.0.1"; } | 100 | { name = "tftp-server-name"; data = tftpIp; } |
51 | ]; | 101 | ]; |
52 | boot-file-name = "i386-ipxe.efi"; | 102 | boot-file-name = "i386-ipxe.efi"; |
103 | only-if-required = true; | ||
53 | } | 104 | } |
54 | { name = "legacy"; | 105 | { name = "legacy"; |
55 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00000'"; | 106 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00000'"; |
56 | only-if-required = true; | ||
57 | option-data = [ | 107 | option-data = [ |
58 | { name = "tftp-server-name"; data = "10.141.0.1"; } | 108 | { name = "tftp-server-name"; data = tftpIp; } |
59 | ]; | 109 | ]; |
60 | boot-file-name = "undionly.kpxe"; | 110 | boot-file-name = "ipxe.lkrn"; |
111 | only-if-required = true; | ||
61 | } | 112 | } |
62 | ]; | 113 | ]; |
63 | 114 | ||
@@ -252,34 +303,78 @@ with lib; | |||
252 | name = "nfsroot.vidhar.yggdrasil"; | 303 | name = "nfsroot.vidhar.yggdrasil"; |
253 | paths = | 304 | paths = |
254 | (map (system: | 305 | (map (system: |
255 | let | 306 | pkgs.symlinkJoin { |
256 | installerBuild = (flake.nixosConfigurations.${"installer-${system}-nfsroot"}.extendModules { | 307 | name = "installer-${system}"; |
257 | modules = [ | 308 | paths = [ |
258 | ({ ... }: { | 309 | (builtins.addErrorContext "while evaluating installer-${system}-nfsroot" (let |
259 | config.nfsroot.storeDevice = "10.141.0.1:nix-store"; | 310 | installerBuild' = (flake.nixosConfigurations.${"installer-${system}-nfsroot"}.extendModules { |
260 | config.nfsroot.registrationUrl = "http://nfsroot.vidhar.yggdrasil/installer-${system}/registration"; | 311 | modules = [ |
261 | }) | 312 | ({ ... }: { |
262 | ]; | 313 | config.nfsroot.storeDevice = "${nfsIp}:nix-store"; |
263 | }).config.system.build; | 314 | config.nfsroot.registrationUrl = "${nfsrootBaseUrl}/installer-${system}/registration"; |
264 | in builtins.toPath (pkgs.runCommandLocal "install-${system}" {} '' | 315 | config.system.nixos.label = "installer-${system}"; |
265 | mkdir -p $out/installer-${system} | 316 | }) |
266 | install -m 0444 -t $out/installer-${system} \ | 317 | ]; |
267 | ${installerBuild.initialRamdisk}/initrd \ | 318 | }); |
268 | ${installerBuild.kernel}/bzImage \ | 319 | installerBuild = installerBuild'.config.system.build; |
269 | ${installerBuild.netbootIpxeScript}/netboot.ipxe \ | 320 | in builtins.toPath (pkgs.runCommandLocal "installer-${system}" {} '' |
270 | ${pkgs.closureInfo { rootPaths = installerBuild.storeContents; }}/registration | 321 | mkdir -p $out/installer-${system} |
271 | '') | 322 | install -m 0444 -t $out/installer-${system} \ |
272 | ) ["x86_64-linux"] | 323 | ${installerBuild.initialRamdisk}/initrd \ |
324 | ${installerBuild.kernel}/bzImage \ | ||
325 | ${installerBuild.netbootIpxeScript}/netboot.ipxe \ | ||
326 | ${pkgs.closureInfo { rootPaths = installerBuild.storeContents; }}/registration | ||
327 | install -m 0444 ${pkgs.writeText "installer-${system}.menu.ipxe" '' | ||
328 | #!ipxe | ||
329 | |||
330 | :start | ||
331 | menu iPXE boot menu for 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 | ||
334 | item netboot netboot.xyz | ||
335 | item shell iPXE shell | ||
336 | choose --timeout 0 --default installer selected || goto shell | ||
337 | goto ''${selected} | ||
338 | |||
339 | :shell | ||
340 | shell | ||
341 | goto start | ||
342 | |||
343 | :installer | ||
344 | chain installer-${system}/netboot.ipxe | ||
345 | goto start | ||
346 | |||
347 | :netboot | ||
348 | iseq ''${platform} efi && chain --autofree netboot.xyz.efi || chain --autofree netboot.xyz.lkrn | ||
349 | goto start | ||
350 | |||
351 | :memtest | ||
352 | iseq ''${platform} efi && chain --autofree memtest.efi || chain --autofree memtest.bin | ||
353 | goto start | ||
354 | ''} $out/installer-${system}.menu.ipxe | ||
355 | ''))) | ||
356 | ]; | ||
357 | }) ["x86_64-linux"] | ||
273 | ) ++ [ | 358 | ) ++ [ |
274 | (let | 359 | (pkgs.runCommandLocal "utils" {} '' |
275 | eostreBuild = (flake.nixosConfigurations.eostre.extendModules { | 360 | mkdir $out |
361 | install -m 0444 -t $out \ | ||
362 | ${ipxe}/{ipxe.efi,i386-ipxe.efi,ipxe.lkrn} \ | ||
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 { | ||
276 | modules = [ | 369 | modules = [ |
277 | ({ ... }: { | 370 | ({ ... }: { |
278 | config.nfsroot.storeDevice = "10.141.0.1:nix-store"; | 371 | config.nfsroot.storeDevice = "${nfsIp}:nix-store"; |
279 | config.nfsroot.registrationUrl = "http://nfsroot.vidhar.yggdrasil/eostre/registration"; | 372 | config.nfsroot.registrationUrl = "${nfsrootBaseUrl}/eostre/registration"; |
373 | config.system.nixos.label = "eostre"; | ||
280 | }) | 374 | }) |
281 | ]; | 375 | ]; |
282 | }).config.system.build; | 376 | }); |
377 | eostreBuild = eostreBuild'.config.system.build; | ||
283 | in builtins.toPath (pkgs.runCommandLocal "eostre" {} '' | 378 | in builtins.toPath (pkgs.runCommandLocal "eostre" {} '' |
284 | mkdir -p $out/eostre | 379 | mkdir -p $out/eostre |
285 | install -m 0444 -t $out/eostre \ | 380 | install -m 0444 -t $out/eostre \ |
@@ -287,7 +382,39 @@ with lib; | |||
287 | ${eostreBuild.kernel}/bzImage \ | 382 | ${eostreBuild.kernel}/bzImage \ |
288 | ${eostreBuild.netbootIpxeScript}/netboot.ipxe \ | 383 | ${eostreBuild.netbootIpxeScript}/netboot.ipxe \ |
289 | ${pkgs.closureInfo { rootPaths = eostreBuild.storeContents; }}/registration | 384 | ${pkgs.closureInfo { rootPaths = eostreBuild.storeContents; }}/registration |
290 | '')) | 385 | install -m 0444 ${pkgs.writeText "eostre.menu.ipxe" '' |
386 | #!ipxe | ||
387 | |||
388 | set menu-timeout 5000 | ||
389 | |||
390 | :start | ||
391 | menu iPXE boot menu for eostre | ||
392 | item eostre ${with eostreBuild'; "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} (Linux ${config.boot.kernelPackages.kernel.modDirVersion})"} | ||
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} | ||
399 | |||
400 | :shell | ||
401 | set menu-timeout 0 | ||
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 | ||
412 | |||
413 | :memtest | ||
414 | iseq ''${platform} efi && chain --autofree memtest.efi || chain --autofree memtest.bin | ||
415 | goto start | ||
416 | ''} $out/eostre.menu.ipxe | ||
417 | ''))) | ||
291 | ]; | 418 | ]; |
292 | }; | 419 | }; |
293 | }; | 420 | }; |
@@ -298,20 +425,12 @@ with lib; | |||
298 | after = [ "network.target" ]; | 425 | after = [ "network.target" ]; |
299 | wantedBy = [ "multi-user.target" ]; | 426 | wantedBy = [ "multi-user.target" ]; |
300 | serviceConfig.ExecStart = let | 427 | serviceConfig.ExecStart = let |
301 | ipxe = pkgs.ipxe.override { | ||
302 | additionalTargets = { | ||
303 | "bin-i386-efi/ipxe.efi" = "i386-ipxe.efi"; | ||
304 | }; | ||
305 | additionalOptions = [ | ||
306 | "NSLOOKUP_CMD" | ||
307 | ]; | ||
308 | }; | ||
309 | tftpRoot = pkgs.runCommandLocal "netboot" {} '' | 428 | tftpRoot = pkgs.runCommandLocal "netboot" {} '' |
310 | mkdir -p $out | 429 | mkdir -p $out |
311 | install -m 0444 -t $out \ | 430 | install -m 0444 -t $out \ |
312 | ${ipxe}/ipxe.efi ${ipxe}/i386-ipxe.efi ${ipxe}/undionly.kpxe | 431 | ${ipxe}/{ipxe.efi,i386-ipxe.efi,ipxe.lkrn} |
313 | ''; | 432 | ''; |
314 | 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}"; |
315 | }; | 434 | }; |
316 | }; | 435 | }; |
317 | } | 436 | } |
diff --git a/hosts/vidhar/network/ruleset.nft b/hosts/vidhar/network/ruleset.nft index 10fd4c51..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 | ||
@@ -92,6 +93,10 @@ table inet filter { | |||
92 | counter tftp-rx {} | 93 | counter tftp-rx {} |
93 | counter pgbackrest-rx {} | 94 | counter pgbackrest-rx {} |
94 | counter immich-rx {} | 95 | counter immich-rx {} |
96 | counter paperless-rx {} | ||
97 | counter hledger-rx {} | ||
98 | counter audiobookshelf-rx {} | ||
99 | counter kimai-rx {} | ||
95 | 100 | ||
96 | counter established-rx {} | 101 | counter established-rx {} |
97 | 102 | ||
@@ -121,6 +126,10 @@ table inet filter { | |||
121 | counter tftp-tx {} | 126 | counter tftp-tx {} |
122 | counter pgbackrest-tx {} | 127 | counter pgbackrest-tx {} |
123 | counter immich-tx {} | 128 | counter immich-tx {} |
129 | counter paperless-tx {} | ||
130 | counter hledger-tx {} | ||
131 | counter audiobookshelf-tx {} | ||
132 | counter kimai-tx {} | ||
124 | 133 | ||
125 | counter tx {} | 134 | counter tx {} |
126 | 135 | ||
@@ -144,8 +153,13 @@ table inet filter { | |||
144 | 153 | ||
145 | 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 |
146 | 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 | ||
147 | 157 | ||
148 | 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 | ||
149 | 163 | ||
150 | 164 | ||
151 | 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 |
@@ -197,6 +211,9 @@ table inet filter { | |||
197 | tcp dport 8432 counter name pgbackrest-rx accept | 211 | tcp dport 8432 counter name pgbackrest-rx accept |
198 | 212 | ||
199 | 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 |
214 | iifname bifrost tcp dport 28981 ip6 saddr $bifrost_surtr counter name paperless-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 | ||
200 | 217 | ||
201 | ct state { established, related } counter name established-rx accept | 218 | ct state { established, related } counter name established-rx accept |
202 | 219 | ||
@@ -246,6 +263,9 @@ table inet filter { | |||
246 | tcp sport 8432 counter name pgbackrest-tx accept | 263 | tcp sport 8432 counter name pgbackrest-tx accept |
247 | 264 | ||
248 | 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 |
266 | iifname bifrost tcp sport 28981 ip6 daddr $bifrost_surtr counter name paperless-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 | ||
249 | 269 | ||
250 | 270 | ||
251 | counter name tx | 271 | counter name tx |
@@ -254,7 +274,7 @@ table inet filter { | |||
254 | 274 | ||
255 | table inet nat { | 275 | table inet nat { |
256 | counter gpon-nat {} | 276 | counter gpon-nat {} |
257 | # counter container-nat {} | 277 | counter kimai-nat {} |
258 | 278 | ||
259 | chain postrouting { | 279 | chain postrouting { |
260 | type nat hook postrouting priority srcnat | 280 | type nat hook postrouting priority srcnat |
@@ -262,7 +282,7 @@ table inet nat { | |||
262 | 282 | ||
263 | 283 | ||
264 | meta nfproto ipv4 oifname gpon counter name gpon-nat masquerade | 284 | meta nfproto ipv4 oifname gpon counter name gpon-nat masquerade |
265 | # iifname ve-* oifname gpon counter name container-nat masquerade | 285 | iifname ve-kimai oifname gpon counter name kimai-nat masquerade |
266 | } | 286 | } |
267 | } | 287 | } |
268 | 288 | ||
diff --git a/hosts/vidhar/paperless/default.nix b/hosts/vidhar/paperless/default.nix new file mode 100644 index 00000000..dd02da38 --- /dev/null +++ b/hosts/vidhar/paperless/default.nix | |||
@@ -0,0 +1,25 @@ | |||
1 | { config, ... }: | ||
2 | |||
3 | { | ||
4 | config = { | ||
5 | services.paperless = { | ||
6 | enable = true; | ||
7 | address = "2a03:4000:52:ada:4:1::"; | ||
8 | passwordFile = config.sops.secrets."paperless-rootpw".path; | ||
9 | settings = { | ||
10 | PAPERLESS_OCR_LANGUAGE = "deu+eng"; | ||
11 | PAPERLESS_URL = "https://paperless.yggdrasil.li"; | ||
12 | PAPERLESS_FILENAME_FORMAT = "{{ created_year }}/{{ document_type }}/{{ correspondent }}/{{ created }}_{{ doc_pk }}_{{ title }}"; | ||
13 | PAPERLESS_FILENAME_FORMAT_REMOVE_NONE = "true"; | ||
14 | PAPERLESS_TASK_WORKERS = "3"; | ||
15 | PAPERLESS_THREADS_PER_WORKER = "4"; | ||
16 | }; | ||
17 | database.createLocally = true; | ||
18 | }; | ||
19 | |||
20 | sops.secrets."paperless-rootpw" = { | ||
21 | format = "binary"; | ||
22 | sopsFile = ./rootpw; | ||
23 | }; | ||
24 | }; | ||
25 | } | ||
diff --git a/hosts/vidhar/paperless/rootpw b/hosts/vidhar/paperless/rootpw new file mode 100644 index 00000000..11f48fcb --- /dev/null +++ b/hosts/vidhar/paperless/rootpw | |||
@@ -0,0 +1,24 @@ | |||
1 | { | ||
2 | "data": "ENC[AES256_GCM,data:Bsns3bLs7aA++eTf2Vh4g2iAXhmrMRTF,iv:zQ6hgXEvgHAloN6UMW54f2nYCvEhHPXQSBVSihHFiC0=,tag:uiGTEs07dpx12PcAjmbr9Q==,type:str]", | ||
3 | "sops": { | ||
4 | "kms": null, | ||
5 | "gcp_kms": null, | ||
6 | "azure_kv": null, | ||
7 | "hc_vault": null, | ||
8 | "age": [ | ||
9 | { | ||
10 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlVUJjdEdIZGd6UDJBRXlL\nODFyWDhHOU9oTEVCVlFiUXVXNm9XZmVuampVCkJ0YkFXTlZXVnRldmtlVkJaR3R2\nMFhpaHB5M3pLeDFkUkkzMUFydGNnOFEKLS0tIEJtNWc0V2JaaWYvQlp6TGxVdVZO\neVpzQzB5Um82TUZOeHBHeE50MGlqNWsKj1P54Fc+c5n35+Og9DwBWkvW947hgFsp\ni/G2QcaLHHJMTexTCZYsr1naSVa/cMBAbrZmtjz0HV4Q1kCJtvlrIg==\n-----END AGE ENCRYPTED FILE-----\n" | ||
12 | }, | ||
13 | { | ||
14 | "recipient": "age1qffdqvy9arld9zd5a5cylt0n98xhcns5shxhrhwjq5g4qa844ejselaa4l", | ||
15 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1UG1QSWtXcFZoQVRBOC9D\nT2VnTW9pcTRCMForcHdZVld0c1NmNFZpWUNBCkRkMERKUVliYXRqb25saWxyb2JN\nbC9YL2ZQbytRM0ZjNmlQOTlTZTQrV2sKLS0tIFZyUWtRcXNqZUZxMGN5d0tHUng2\nVXNSdFEwMmtIVEdVRVlWeVU1YmJVSkUKRJa42k551QtiC6S0tmMv7eVN7GRqpXWz\nvzNh+BM9TOJNaTMmVesr4vXNDLOSFS3PxYv95xuOBzVg3zOHuai72g==\n-----END AGE ENCRYPTED FILE-----\n" | ||
16 | } | ||
17 | ], | ||
18 | "lastmodified": "2025-02-13T19:20:33Z", | ||
19 | "mac": "ENC[AES256_GCM,data:mG6AC3L8MMeZ0Ajr7zV1mzPcHviQw2adtGjSbrbPRw1xqN7siu6svoybv8xkahP2Grq/xKAiyfXFOFo7Uyc3ub5fSovAEolNazqybZYsyam5vHpeC23dXcEkZUJSPJ9/CSB5uI9nX3NPC64QUjCxHZ7qfH5gcXT9D12H8LSqKlQ=,iv:4Skdj8l9jlTX9Unc2xE2hCKVawHBnHR8L4kZA6H8xNw=,tag:zJsJ3S//faAn7AGwLefNoA==,type:str]", | ||
20 | "pgp": null, | ||
21 | "unencrypted_suffix": "_unencrypted", | ||
22 | "version": "3.9.4" | ||
23 | } | ||
24 | } \ No newline at end of file | ||
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 cd1ee064..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 | ||
@@ -57,6 +57,10 @@ with lib; | |||
57 | 57 | ||
58 | system.disableInstallerTools = false; | 58 | system.disableInstallerTools = false; |
59 | 59 | ||
60 | xdg.autostart.enable = lib.mkForce false; | ||
61 | xdg.icons.enable = lib.mkForce false; | ||
62 | xdg.mime.enable = lib.mkForce false; | ||
63 | |||
60 | systemd.sysusers.enable = false; | 64 | systemd.sysusers.enable = false; |
61 | system.machine-id.generate.enable = false; | 65 | system.machine-id.generate.enable = false; |
62 | system.stateVersion = config.system.nixos.release; # No state in installer | 66 | system.stateVersion = config.system.nixos.release; # No state in installer |
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/backup-utils.nix b/modules/backup-utils.nix index 82a42ecd..698140da 100644 --- a/modules/backup-utils.nix +++ b/modules/backup-utils.nix | |||
@@ -9,5 +9,8 @@ with lib; | |||
9 | 9 | ||
10 | config = { | 10 | config = { |
11 | services.borgsnap.archive-prefix = mkDefault "yggdrasil.${hostName}."; | 11 | services.borgsnap.archive-prefix = mkDefault "yggdrasil.${hostName}."; |
12 | |||
13 | systemd.services."zfssnap-prune".restartIfChanged = false; | ||
14 | systemd.services."zfssnap".restartIfChanged = false; | ||
12 | }; | 15 | }; |
13 | } | 16 | } |
diff --git a/modules/borgcopy/default.nix b/modules/borgcopy/default.nix index 475edbd9..8e1afc27 100644 --- a/modules/borgcopy/default.nix +++ b/modules/borgcopy/default.nix | |||
@@ -22,6 +22,7 @@ let | |||
22 | }; | 22 | }; |
23 | 23 | ||
24 | copyService = name: opts: nameValuePair "copy-borg@${utils.escapeSystemdPath name}" { | 24 | copyService = name: opts: nameValuePair "copy-borg@${utils.escapeSystemdPath name}" { |
25 | restartIfChanged = false; | ||
25 | serviceConfig = { | 26 | serviceConfig = { |
26 | Type = "oneshot"; | 27 | Type = "oneshot"; |
27 | ExecStart = "${copyBorg}/bin/copy_borg --verbosity ${toString opts.verbosity} ${utils.escapeSystemdExecArgs [opts.from opts.to]}"; | 28 | ExecStart = "${copyBorg}/bin/copy_borg --verbosity ${toString opts.verbosity} ${utils.escapeSystemdExecArgs [opts.from opts.to]}"; |
diff --git a/modules/envfs.nix b/modules/envfs.nix deleted file mode 100644 index b5b453a5..00000000 --- a/modules/envfs.nix +++ /dev/null | |||
@@ -1,77 +0,0 @@ | |||
1 | { pkgs, config, lib, ... }: | ||
2 | |||
3 | let | ||
4 | cfg = config.services.envfs; | ||
5 | mounts = { | ||
6 | "/usr/bin" = { | ||
7 | device = "none"; | ||
8 | fsType = "envfs"; | ||
9 | options = [ | ||
10 | "bind-mount=/bin" | ||
11 | "fallback-path=${pkgs.symlinkJoin { | ||
12 | name = "fallback-path"; | ||
13 | inherit (cfg) paths; | ||
14 | }}" | ||
15 | "nofail" | ||
16 | ]; | ||
17 | }; | ||
18 | "/bin" = { | ||
19 | device = "/usr/bin"; | ||
20 | fsType = "none"; | ||
21 | options = [ "bind" "nofail" ]; | ||
22 | }; | ||
23 | }; | ||
24 | in { | ||
25 | disabledModules = [ "tasks/filesystems/envfs.nix" ]; | ||
26 | |||
27 | options = { | ||
28 | services.envfs = { | ||
29 | enable = lib.mkEnableOption "Envfs filesystem" // { | ||
30 | default = true; | ||
31 | description = '' | ||
32 | Fuse filesystem that returns symlinks to executables based on the PATH | ||
33 | of the requesting process. This is useful to execute shebangs on NixOS | ||
34 | that assume hard coded locations in locations like /bin or /usr/bin | ||
35 | etc. | ||
36 | ''; | ||
37 | }; | ||
38 | |||
39 | package = lib.mkOption { | ||
40 | type = lib.types.package; | ||
41 | default = pkgs.envfs; | ||
42 | defaultText = lib.literalExpression "pkgs.envfs"; | ||
43 | description = "Which package to use for the envfs."; | ||
44 | }; | ||
45 | |||
46 | paths = lib.mkOption { | ||
47 | type = lib.types.listOf lib.types.package; | ||
48 | default = [ | ||
49 | (pkgs.runCommand "fallback-path-environment" {} '' | ||
50 | mkdir -p $out | ||
51 | ln -s ${config.environment.usrbinenv} $out/env | ||
52 | ln -s ${config.environment.binsh} $out/sh | ||
53 | '') | ||
54 | ]; | ||
55 | defaultText = lib.literalExpression '' | ||
56 | [ (pkgs.runCommand "fallback-path-environment" {} ''' | ||
57 | mkdir -p $out | ||
58 | ln -s ''${config.environment.usrbinenv} $out/env | ||
59 | ln -s ''${config.environment.binsh} $out/sh | ||
60 | ''') | ||
61 | ] | ||
62 | ''; | ||
63 | description = "Extra packages to join into collection of fallback executables in case not other executable is found"; | ||
64 | }; | ||
65 | }; | ||
66 | }; | ||
67 | |||
68 | config = lib.mkIf (cfg.enable) { | ||
69 | environment.systemPackages = [ cfg.package ]; | ||
70 | # we also want these mounts in virtual machines. | ||
71 | fileSystems = if config.virtualisation ? qemu then lib.mkVMOverride mounts else mounts; | ||
72 | |||
73 | # We no longer need those when using envfs | ||
74 | system.activationScripts.usrbinenv = lib.mkForce ""; | ||
75 | system.activationScripts.binsh = lib.mkForce ""; | ||
76 | }; | ||
77 | } | ||
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/nix-access-tokens/default.nix b/modules/nix-access-tokens/default.nix new file mode 100644 index 00000000..a3b7abfa --- /dev/null +++ b/modules/nix-access-tokens/default.nix | |||
@@ -0,0 +1,24 @@ | |||
1 | { lib, config, hostName ,... }: | ||
2 | |||
3 | let | ||
4 | cfg = config.nix.includeAccessTokens; | ||
5 | in { | ||
6 | options = { | ||
7 | nix.includeAccessTokens.enable = lib.mkEnableOption "including access tokens in nix.conf" // { default = lib.elem hostName ["sif" "surtr" "vidhar"]; }; | ||
8 | }; | ||
9 | |||
10 | config = lib.mkIf cfg.enable { | ||
11 | nix = { | ||
12 | extraOptions = '' | ||
13 | !include ${config.sops.secrets.nixAccessTokens.path} | ||
14 | ''; | ||
15 | }; | ||
16 | |||
17 | sops.secrets.nixAccessTokens = { | ||
18 | format = "binary"; | ||
19 | sopsFile = ./nix.conf; | ||
20 | mode = "0440"; | ||
21 | group = "wheel"; | ||
22 | }; | ||
23 | }; | ||
24 | } | ||
diff --git a/modules/nix-access-tokens/nix.conf b/modules/nix-access-tokens/nix.conf new file mode 100644 index 00000000..f0b394ef --- /dev/null +++ b/modules/nix-access-tokens/nix.conf | |||
@@ -0,0 +1,32 @@ | |||
1 | { | ||
2 | "data": "ENC[AES256_GCM,data:/cdBpvCAFpgm0YWhy1WYlA09KlU6PzVfBYVLBD0boqGqvP+8wuyDzj5KWbcKsdGhoiklODiKR0ODXNU+fA35y862PFXvSb4xVyfbdKRndYdIA4W6vyobtoC9h7B1yR9pkq9L+1tqlU30Dgy2Gndg9rWHlIo+1lO/1A==,iv:B1Px2+cxCaopHZThkEG5saOib+PNvurPIS6aeAv2uPo=,tag:K3JqRaX3/iIqD3c//YdqSQ==,type:str]", | ||
3 | "sops": { | ||
4 | "kms": null, | ||
5 | "gcp_kms": null, | ||
6 | "azure_kv": null, | ||
7 | "hc_vault": null, | ||
8 | "age": [ | ||
9 | { | ||
10 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5NkZUUGI3M2pQYWVXeFV6\na2h2czRTeUJFekJCS012YlBkL1FDdTd3ekZ3ClJsTVh0R2JQM0Jua1JjL285RVA1\nRHhlbjlLdmNBUXVLelFGY2NGYWpLejQKLS0tIDBUWUhJNm8zWGoyQ0pBYnV1ZjBh\ndktNRkNPS1lpWXFITC81aEZJbXlONk0Km2c1xVKwSankaVs7O/utGJwRRX395upz\ndPbsOElTnbGmkb0esGtvGSPboTvK+gjn9w/GhaPyTnNDoos7GaIfyg==\n-----END AGE ENCRYPTED FILE-----\n" | ||
12 | }, | ||
13 | { | ||
14 | "recipient": "age1fj65apkhfkrwyv5tx6zcs9nkjg8267fy733qph30sc7zfn7vapjqkd5kne", | ||
15 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6bS9iY2lua3U4U3lJa1pK\nSlZNMmFZMEU5M1V2bWRjaXIwajZJVDJPMlM4Cmd3TTNFWjVuSGdtbC9iODltTS91\nOE5XOEVEQkh0SFpVVW5jc3IzbzNpTmMKLS0tIEtrSU54QUVPa2tBZDhLYlRFWitR\nc2x6MFlxL0tobDJTek42dEcyZXpoWDgKXzQfU+o6FkbJBwmm6oaHu4sDPi822uUR\n5VY6gY/h3g2kM4cuS03Q4NJmeRxuh7cx0UqGU3j5Mf8muE1LHpYEPw==\n-----END AGE ENCRYPTED FILE-----\n" | ||
16 | }, | ||
17 | { | ||
18 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
19 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaOVpNZ2lVT0VwbHVZNzFl\nenJsMGpnbkRvU0xOSU5obk5yT2p5ZVNzdXhNCnVlQzZtRjZNVmJLSUpKc3UwVXZs\nWi9EZ3kxZkJNeFJDSjl1L1IweTFNMXcKLS0tIDJUOTBwTldCUmlnU0tWVkZkNzJL\nejM4ajJVbVhvSm1YM2Vxa2JldllYN0UKAzxy2wkzRvCSiTy417AulpCu41z668HG\nto92eGF2ZRFfEG5LGlCKWeDcP3gM8QwKiVlm6wndbOkhMMfc4Sp3wA==\n-----END AGE ENCRYPTED FILE-----\n" | ||
20 | }, | ||
21 | { | ||
22 | "recipient": "age1qffdqvy9arld9zd5a5cylt0n98xhcns5shxhrhwjq5g4qa844ejselaa4l", | ||
23 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2ejRHcGttNUxYZnFzTU5J\nMTFvY3daQ1VMM2xxYTgvLzZwT1owazVNenhzCktaWFF6K2s5UjI2b20rSHFNSS9E\nMVlJSmZhQm15eUs3U0hGTGpSRndmSDgKLS0tIDVrcjl4eDhwak1pRithbnRWWEZy\nVE9EOEpKdEJoRTFrTXpQVDc1cmsrU1kK/goTdUmpZPeMRbY1QzLXAa6Qpg4YYYYo\n3v3GK1bzdey8szfgIr1dHTtQEzqE2WX1swzZizDXj/RiUWx01Ky3GA==\n-----END AGE ENCRYPTED FILE-----\n" | ||
24 | } | ||
25 | ], | ||
26 | "lastmodified": "2025-01-25T19:58:58Z", | ||
27 | "mac": "ENC[AES256_GCM,data:Oza4XgnTX3vly89nGluLbEytk1dUYAiOhIYewQyDLLLSSlUIpXmWhV+X0HUQ9AX5kUrEhNbVzRdvUG/9YwoWjTJfvd7tw41IYeTqgykMNXJUfGssoutXfeij9YR+t5aJaRhlTkIWcBhUjXSUNyJCl6Z3XmzWstTPZXEU9VmAvuE=,iv:LqVwIiit+WqI5NWSboexWsmPzg7e63nWJYsNFEK1Uog=,tag:ClR6oI62WXEfIYYAY6vL0A==,type:str]", | ||
28 | "pgp": null, | ||
29 | "unencrypted_suffix": "_unencrypted", | ||
30 | "version": "3.9.3" | ||
31 | } | ||
32 | } \ No newline at end of file | ||
diff --git a/modules/pgbackrest.nix b/modules/pgbackrest.nix index 886840b9..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"; |
@@ -216,6 +218,7 @@ in { | |||
216 | }; | 218 | }; |
217 | }; | 219 | }; |
218 | } // mapAttrs' (name: backupCfg: nameValuePair "pgbackrest-backup@${escapeSystemdPath name}" { | 220 | } // mapAttrs' (name: backupCfg: nameValuePair "pgbackrest-backup@${escapeSystemdPath name}" { |
221 | restartIfChanged = false; | ||
219 | description = "Perform pgBackRest Backup (${name}${optionalString (!(isNull backupCfg.repo)) " repo${backupCfg.repo}"})"; | 222 | description = "Perform pgBackRest Backup (${name}${optionalString (!(isNull backupCfg.repo)) " repo${backupCfg.repo}"})"; |
220 | serviceConfig = { | 223 | serviceConfig = { |
221 | Type = "oneshot"; | 224 | Type = "oneshot"; |
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/tzupdate.nix b/modules/tzupdate.nix new file mode 100644 index 00000000..6465d33f --- /dev/null +++ b/modules/tzupdate.nix | |||
@@ -0,0 +1,81 @@ | |||
1 | { | ||
2 | config, | ||
3 | lib, | ||
4 | pkgs, | ||
5 | ... | ||
6 | }: | ||
7 | let | ||
8 | cfg = config.services.tzupdate; | ||
9 | in | ||
10 | { | ||
11 | disabledModules = [ "services/misc/tzupdate.nix" ]; | ||
12 | |||
13 | options.services.tzupdate = { | ||
14 | enable = lib.mkOption { | ||
15 | type = lib.types.bool; | ||
16 | default = false; | ||
17 | description = '' | ||
18 | Enable the tzupdate timezone updating service. This provides | ||
19 | a one-shot service which can be activated with systemctl to | ||
20 | update the timezone. | ||
21 | ''; | ||
22 | }; | ||
23 | |||
24 | package = lib.mkPackageOption pkgs "tzupdate" { }; | ||
25 | |||
26 | timer.enable = lib.mkOption { | ||
27 | type = lib.types.bool; | ||
28 | default = true; | ||
29 | description = '' | ||
30 | Enable the tzupdate timer to update the timezone automatically. | ||
31 | ''; | ||
32 | }; | ||
33 | |||
34 | timer.interval = lib.mkOption { | ||
35 | type = lib.types.str; | ||
36 | default = "hourly"; | ||
37 | description = '' | ||
38 | The interval at which the tzupdate timer should run. See | ||
39 | {manpage}`systemd.time(7)` to understand the format. | ||
40 | ''; | ||
41 | }; | ||
42 | }; | ||
43 | |||
44 | config = lib.mkIf cfg.enable { | ||
45 | # We need to have imperative time zone management for this to work. | ||
46 | # This will give users an error if they have set an explicit time | ||
47 | # zone, which is better than silently overriding it. | ||
48 | time.timeZone = null; | ||
49 | |||
50 | # We provide a one-shot service that runs at startup once network | ||
51 | # interfaces are up, but we can’t ensure we actually have Internet access | ||
52 | # at that point. It can also be run manually with `systemctl start tzupdate`. | ||
53 | systemd.services.tzupdate = { | ||
54 | description = "tzupdate timezone update service"; | ||
55 | wantedBy = [ "multi-user.target" ]; | ||
56 | wants = [ "network-online.target" ]; | ||
57 | after = [ "network-online.target" ]; | ||
58 | script = '' | ||
59 | timezone="$(${lib.getExe cfg.package} --print-only)" | ||
60 | if [[ -n "$timezone" ]]; then | ||
61 | echo "Setting timezone to '$timezone'" | ||
62 | ${lib.getExe' config.systemd.package "timedatectl"} set-timezone "$timezone" | ||
63 | fi | ||
64 | ''; | ||
65 | |||
66 | serviceConfig = { | ||
67 | Type = "oneshot"; | ||
68 | }; | ||
69 | }; | ||
70 | |||
71 | systemd.timers.tzupdate = { | ||
72 | enable = cfg.timer.enable; | ||
73 | timerConfig = { | ||
74 | OnStartupSec = "30s"; | ||
75 | OnCalendar = cfg.timer.interval; | ||
76 | Persistent = true; | ||
77 | }; | ||
78 | wantedBy = [ "timers.target" ]; | ||
79 | }; | ||
80 | }; | ||
81 | } | ||
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 c0566373..72c0d99d 100644 --- a/nvfetcher.toml +++ b/nvfetcher.toml | |||
@@ -78,12 +78,12 @@ git.fetchSubmodules = true | |||
78 | src.git = "https://github.com/jgreco/mpv-youtube-quality" | 78 | src.git = "https://github.com/jgreco/mpv-youtube-quality" |
79 | fetch.git = "https://github.com/jgreco/mpv-youtube-quality" | 79 | fetch.git = "https://github.com/jgreco/mpv-youtube-quality" |
80 | 80 | ||
81 | [batman-adv] | 81 | # [batman-adv] |
82 | src.webpage = "https://www.open-mesh.org/projects/open-mesh/wiki/Download" | 82 | # src.webpage = "https://www.open-mesh.org/projects/open-mesh/wiki/Download" |
83 | src.regex = "The latest version of <a[^\\>]*>batman-adv</a> is <a[^\\>]*>batman-adv-([0-9\\.]+).tar.gz</a>" | 83 | # src.regex = "The latest version of <a[^\\>]*>batman-adv</a> is <a[^\\>]*>batman-adv-([0-9\\.]+).tar.gz</a>" |
84 | src.from_pattern = "^.*batman-adv-([0-9\\.]+).tar.gz.*$" | 84 | # src.from_pattern = "^.*batman-adv-([0-9\\.]+).tar.gz.*$" |
85 | src.to_pattern = "\\1" | 85 | # src.to_pattern = "\\1" |
86 | fetch.tarball = "https://downloads.open-mesh.org/batman/stable/sources/batman-adv/batman-adv-$ver.tar.gz" | 86 | # fetch.tarball = "https://downloads.open-mesh.org/batman/stable/sources/batman-adv/batman-adv-$ver.tar.gz" |
87 | 87 | ||
88 | [scutiger] | 88 | [scutiger] |
89 | src.github_tag = "bk2204/scutiger" | 89 | src.github_tag = "bk2204/scutiger" |
@@ -107,3 +107,19 @@ fetch.tarball = "https://github.com/JonathonReinhart/spice-record/archive/refs/t | |||
107 | [yt-dlp] | 107 | [yt-dlp] |
108 | src.pypi = "yt_dlp" | 108 | src.pypi = "yt_dlp" |
109 | fetch.pypi = "yt_dlp" | 109 | fetch.pypi = "yt_dlp" |
110 | |||
111 | [mako] | ||
112 | src.git = "https://github.com/emersion/mako" | ||
113 | fetch.git = "https://github.com/emersion/mako" | ||
114 | |||
115 | [swayosd] | ||
116 | src.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/batman-adv.nix b/overlays/batman-adv.nix deleted file mode 100644 index cce7dc4f..00000000 --- a/overlays/batman-adv.nix +++ /dev/null | |||
@@ -1,15 +0,0 @@ | |||
1 | { final, prev, sources, ... }: { | ||
2 | linuxPackages_latest = prev.linuxPackages_latest.extend (self: super: { | ||
3 | batman_adv = super.batman_adv.overrideAttrs (oldAttrs: { | ||
4 | version = "${sources.batman-adv.version}-${self.kernel.version}"; | ||
5 | inherit (sources.batman-adv) src; | ||
6 | }); | ||
7 | }); | ||
8 | |||
9 | linuxPackages_6_2 = prev.linuxPackages_6_2.extend (self: super: { | ||
10 | batman_adv = super.batman_adv.overrideAttrs (oldAttrs: { | ||
11 | version = "${sources.batman-adv.version}-${self.kernel.version}"; | ||
12 | inherit (sources.batman-adv) src; | ||
13 | }); | ||
14 | }); | ||
15 | } | ||
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 new file mode 100644 index 00000000..678c6f5f --- /dev/null +++ b/overlays/deploy-rs.nix | |||
@@ -0,0 +1,16 @@ | |||
1 | { final, prev, flakeInputs, ... }: prev.lib.composeExtensions | ||
2 | flakeInputs.deploy-rs.overlays.default | ||
3 | (final: prev: { | ||
4 | deploy-rs = prev.deploy-rs // { | ||
5 | deploy-rs = prev.symlinkJoin { | ||
6 | name = "${prev.deploy-rs.deploy-rs.name}-wrapped"; | ||
7 | paths = [ prev.deploy-rs.deploy-rs ]; | ||
8 | buildInputs = [ prev.makeWrapper ]; | ||
9 | postBuild = '' | ||
10 | wrapProgram $out/bin/deploy \ | ||
11 | --prefix PATH : ${prev.lib.makeBinPath (with final; [ nix-monitored ])} | ||
12 | ''; | ||
13 | }; | ||
14 | }; | ||
15 | }) | ||
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/keepassxc/database-open-dialog.patch b/overlays/keepassxc/database-open-dialog.patch new file mode 100644 index 00000000..dff84846 --- /dev/null +++ b/overlays/keepassxc/database-open-dialog.patch | |||
@@ -0,0 +1,129 @@ | |||
1 | diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp | ||
2 | index 60412b5a..c0497d91 100644 | ||
3 | --- a/src/browser/BrowserService.cpp | ||
4 | +++ b/src/browser/BrowserService.cpp | ||
5 | @@ -249,7 +249,7 @@ QJsonObject BrowserService::createNewGroup(const QString& groupName) | ||
6 | return result; | ||
7 | } | ||
8 | |||
9 | - auto dialogResult = MessageBox::warning(m_currentDatabaseWidget, | ||
10 | + auto dialogResult = MessageBox::warning(nullptr, | ||
11 | tr("KeePassXC - Create a new group"), | ||
12 | tr("A request for creating a new group \"%1\" has been received.\n" | ||
13 | "Do you want to create this group?\n") | ||
14 | @@ -422,7 +422,7 @@ QList<Entry*> BrowserService::confirmEntries(QList<Entry*>& entriesToConfirm, | ||
15 | |||
16 | m_dialogActive = true; | ||
17 | updateWindowState(); | ||
18 | - BrowserAccessControlDialog accessControlDialog(m_currentDatabaseWidget); | ||
19 | + BrowserAccessControlDialog accessControlDialog{}; | ||
20 | |||
21 | connect(m_currentDatabaseWidget, SIGNAL(databaseLockRequested()), &accessControlDialog, SLOT(reject())); | ||
22 | |||
23 | @@ -512,7 +512,7 @@ QString BrowserService::storeKey(const QString& key) | ||
24 | QString id; | ||
25 | |||
26 | do { | ||
27 | - QInputDialog keyDialog(m_currentDatabaseWidget); | ||
28 | + QInputDialog keyDialog{}; | ||
29 | connect(m_currentDatabaseWidget, SIGNAL(databaseLockRequested()), &keyDialog, SLOT(reject())); | ||
30 | keyDialog.setWindowTitle(tr("KeePassXC - New key association request")); | ||
31 | keyDialog.setLabelText(tr("You have received an association request for the following database:\n%1\n\n" | ||
32 | @@ -535,7 +535,7 @@ QString BrowserService::storeKey(const QString& key) | ||
33 | |||
34 | contains = db->metadata()->customData()->contains(CustomData::BrowserKeyPrefix + id); | ||
35 | if (contains) { | ||
36 | - dialogResult = MessageBox::warning(m_currentDatabaseWidget, | ||
37 | + dialogResult = MessageBox::warning(nullptr, | ||
38 | tr("KeePassXC - Overwrite existing key?"), | ||
39 | tr("A shared encryption key with the name \"%1\" " | ||
40 | "already exists.\nDo you want to overwrite it?") | ||
41 | @@ -595,7 +595,7 @@ QJsonObject BrowserService::showPasskeysRegisterPrompt(const QJsonObject& public | ||
42 | const auto existingEntries = getPasskeyEntriesWithUserHandle(rpId, userId, keyList); | ||
43 | |||
44 | raiseWindow(); | ||
45 | - BrowserPasskeysConfirmationDialog confirmDialog(m_currentDatabaseWidget); | ||
46 | + BrowserPasskeysConfirmationDialog confirmDialog{}; | ||
47 | confirmDialog.registerCredential(username, rpId, existingEntries, timeout); | ||
48 | |||
49 | auto dialogResult = confirmDialog.exec(); | ||
50 | @@ -612,7 +612,7 @@ QJsonObject BrowserService::showPasskeysRegisterPrompt(const QJsonObject& public | ||
51 | // If no entry is selected, show the import dialog for manual entry selection | ||
52 | auto selectedEntry = confirmDialog.getSelectedEntry(); | ||
53 | if (!selectedEntry) { | ||
54 | - PasskeyImporter passkeyImporter(m_currentDatabaseWidget); | ||
55 | + PasskeyImporter passkeyImporter{}; | ||
56 | const auto result = passkeyImporter.showImportDialog(db, | ||
57 | nullptr, | ||
58 | origin, | ||
59 | @@ -683,7 +683,7 @@ QJsonObject BrowserService::showPasskeysAuthenticationPrompt(const QJsonObject& | ||
60 | const auto timeout = publicKeyOptions["timeout"].toInt(); | ||
61 | |||
62 | raiseWindow(); | ||
63 | - BrowserPasskeysConfirmationDialog confirmDialog(m_currentDatabaseWidget); | ||
64 | + BrowserPasskeysConfirmationDialog confirmDialog{}; | ||
65 | confirmDialog.authenticateCredential(entries, rpId, timeout); | ||
66 | auto dialogResult = confirmDialog.exec(); | ||
67 | if (dialogResult == QDialog::Accepted) { | ||
68 | @@ -760,7 +760,7 @@ void BrowserService::addPasskeyToEntry(Entry* entry, | ||
69 | |||
70 | // Ask confirmation if entry already contains a Passkey | ||
71 | if (entry->hasPasskey()) { | ||
72 | - if (MessageBox::question(m_currentDatabaseWidget, | ||
73 | + if (MessageBox::question(nullptr, | ||
74 | tr("KeePassXC - Update passkey"), | ||
75 | tr("Entry already has a passkey.\nDo you want to overwrite the passkey in %1 - %2?") | ||
76 | .arg(entry->title(), passkeyUtils()->getUsernameFromEntry(entry)), | ||
77 | @@ -873,7 +873,7 @@ bool BrowserService::updateEntry(const EntryParameters& entryParameters, const Q | ||
78 | MessageBox::Button dialogResult = MessageBox::No; | ||
79 | if (!browserSettings()->alwaysAllowUpdate()) { | ||
80 | raiseWindow(); | ||
81 | - dialogResult = MessageBox::question(m_currentDatabaseWidget, | ||
82 | + dialogResult = MessageBox::question(nullptr, | ||
83 | tr("KeePassXC - Update Entry"), | ||
84 | tr("Do you want to update the information in %1 - %2?") | ||
85 | .arg(QUrl(entryParameters.siteUrl).host(), username), | ||
86 | @@ -909,7 +909,7 @@ bool BrowserService::deleteEntry(const QString& uuid) | ||
87 | return false; | ||
88 | } | ||
89 | |||
90 | - auto dialogResult = MessageBox::warning(m_currentDatabaseWidget, | ||
91 | + auto dialogResult = MessageBox::warning(nullptr, | ||
92 | tr("KeePassXC - Delete entry"), | ||
93 | tr("A request for deleting entry \"%1\" has been received.\n" | ||
94 | "Do you want to delete the entry?\n") | ||
95 | @@ -1536,7 +1536,7 @@ QSharedPointer<Database> BrowserService::selectedDatabase() | ||
96 | } | ||
97 | } | ||
98 | |||
99 | - BrowserEntrySaveDialog browserEntrySaveDialog(m_currentDatabaseWidget); | ||
100 | + BrowserEntrySaveDialog browserEntrySaveDialog{}; | ||
101 | int openDatabaseCount = browserEntrySaveDialog.setItems(databaseWidgets, m_currentDatabaseWidget); | ||
102 | if (openDatabaseCount > 1) { | ||
103 | int res = browserEntrySaveDialog.exec(); | ||
104 | diff --git a/src/fdosecrets/objects/Prompt.cpp b/src/fdosecrets/objects/Prompt.cpp | ||
105 | index e89cd499..347c98b8 100644 | ||
106 | --- a/src/fdosecrets/objects/Prompt.cpp | ||
107 | +++ b/src/fdosecrets/objects/Prompt.cpp | ||
108 | @@ -313,7 +313,7 @@ namespace FdoSecrets | ||
109 | if (!entries.isEmpty()) { | ||
110 | QString app = tr("%1 (PID: %2)").arg(client->name()).arg(client->pid()); | ||
111 | auto ac = new AccessControlDialog( | ||
112 | - findWindow(m_windowId), entries, app, client->processInfo(), AuthOption::Remember); | ||
113 | + nullptr, entries, app, client->processInfo(), AuthOption::Remember); | ||
114 | connect(ac, &AccessControlDialog::finished, this, &UnlockPrompt::itemUnlockFinished); | ||
115 | connect(ac, &AccessControlDialog::finished, ac, &AccessControlDialog::deleteLater); | ||
116 | ac->open(); | ||
117 | diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp | ||
118 | index 805d4eab..4836199e 100644 | ||
119 | --- a/src/gui/DatabaseTabWidget.cpp | ||
120 | +++ b/src/gui/DatabaseTabWidget.cpp | ||
121 | @@ -41,7 +41,7 @@ DatabaseTabWidget::DatabaseTabWidget(QWidget* parent) | ||
122 | : QTabWidget(parent) | ||
123 | , m_dbWidgetStateSync(new DatabaseWidgetStateSync(this)) | ||
124 | , m_dbWidgetPendingLock(nullptr) | ||
125 | - , m_databaseOpenDialog(new DatabaseOpenDialog(this)) | ||
126 | + , m_databaseOpenDialog(new DatabaseOpenDialog()) | ||
127 | , m_databaseOpenInProgress(false) | ||
128 | { | ||
129 | auto* tabBar = new QTabBar(this); | ||
diff --git a/overlays/keepassxc/default.nix b/overlays/keepassxc/default.nix new file mode 100644 index 00000000..46b3a459 --- /dev/null +++ b/overlays/keepassxc/default.nix | |||
@@ -0,0 +1,8 @@ | |||
1 | { final, prev, ... }: | ||
2 | { | ||
3 | keepassxc = prev.keepassxc.overrideAttrs (oldAttrs: { | ||
4 | patches = (oldAttrs.patches or []) ++ prev.lib.optional (prev.lib.versionAtLeast oldAttrs.version "2.7.9") [ | ||
5 | ./database-open-dialog.patch | ||
6 | ]; | ||
7 | }); | ||
8 | } | ||
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/mako.nix b/overlays/mako.nix new file mode 100644 index 00000000..1c1464fb --- /dev/null +++ b/overlays/mako.nix | |||
@@ -0,0 +1,5 @@ | |||
1 | { final, prev, sources, ... }: { | ||
2 | mako = prev.mako.overrideAttrs (oldAttrs: { | ||
3 | inherit (sources.mako) version src; | ||
4 | }); | ||
5 | } | ||
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/niri.nix b/overlays/niri.nix new file mode 100644 index 00000000..9188ed7d --- /dev/null +++ b/overlays/niri.nix | |||
@@ -0,0 +1,8 @@ | |||
1 | { final, prev, flakeInputs, ... }: prev.lib.composeExtensions | ||
2 | flakeInputs.niri-flake.overlays.niri | ||
3 | (final: prev: { | ||
4 | niri-unstable = prev.niri-unstable.overrideAttrs (oldAttrs: { | ||
5 | buildInputs = (oldAttrs.buildInputs or []) ++ [ final.libgbm ]; | ||
6 | }); | ||
7 | }) | ||
8 | final prev | ||
diff --git a/overlays/nix-direnv/default.nix b/overlays/nix-direnv/default.nix new file mode 100644 index 00000000..7c488e4e --- /dev/null +++ b/overlays/nix-direnv/default.nix | |||
@@ -0,0 +1,53 @@ | |||
1 | { final, prev, ... }: { | ||
2 | nix-direnv = prev.resholve.mkDerivation rec { | ||
3 | pname = "nix-direnv"; | ||
4 | version = "3.0.6"; | ||
5 | |||
6 | patches = [ | ||
7 | ./static-nix.patch | ||
8 | ]; | ||
9 | |||
10 | src = prev.fetchFromGitHub { | ||
11 | owner = "nix-community"; | ||
12 | repo = "nix-direnv"; | ||
13 | rev = version; | ||
14 | hash = "sha256-oNqhPqgQT92yxbKmcgX4F3e2yTUPyXYG7b2xQm3TvQw="; | ||
15 | }; | ||
16 | |||
17 | installPhase = '' | ||
18 | runHook preInstall | ||
19 | install -m400 -D direnvrc $out/share/nix-direnv/direnvrc | ||
20 | runHook postInstall | ||
21 | ''; | ||
22 | |||
23 | solutions = { | ||
24 | default = { | ||
25 | scripts = [ "share/nix-direnv/direnvrc" ]; | ||
26 | interpreter = "none"; | ||
27 | inputs = with final; [ coreutils nix-monitored ]; | ||
28 | fake = { | ||
29 | builtin = [ | ||
30 | "PATH_add" | ||
31 | "direnv_layout_dir" | ||
32 | "has" | ||
33 | "log_error" | ||
34 | "log_status" | ||
35 | "watch_file" | ||
36 | ]; | ||
37 | function = [ | ||
38 | # not really a function - this is in an else branch for macOS/homebrew that | ||
39 | # cannot be reached when built with nix | ||
40 | "shasum" | ||
41 | ]; | ||
42 | }; | ||
43 | keep = { | ||
44 | "$cmd" = true; | ||
45 | "$direnv" = true; | ||
46 | }; | ||
47 | execer = [ | ||
48 | "cannot:${prev.lib.getExe' final.nix-monitored "nix"}" | ||
49 | ]; | ||
50 | }; | ||
51 | }; | ||
52 | }; | ||
53 | } | ||
diff --git a/overlays/nix-direnv/static-nix.patch b/overlays/nix-direnv/static-nix.patch new file mode 100644 index 00000000..5de8193f --- /dev/null +++ b/overlays/nix-direnv/static-nix.patch | |||
@@ -0,0 +1,62 @@ | |||
1 | diff --git i/direnvrc w/direnvrc | ||
2 | index ddac0f5..fbcade6 100644 | ||
3 | --- i/direnvrc | ||
4 | +++ w/direnvrc | ||
5 | @@ -29,10 +29,8 @@ _nix_direnv_warning() { | ||
6 | |||
7 | _nix_direnv_error() { log_error "${_NIX_DIRENV_LOG_PREFIX}$*"; } | ||
8 | |||
9 | -_nix_direnv_nix="" | ||
10 | - | ||
11 | _nix() { | ||
12 | - ${_nix_direnv_nix} --extra-experimental-features "nix-command flakes" "$@" | ||
13 | + nix --extra-experimental-features "nix-command flakes" "$@" | ||
14 | } | ||
15 | |||
16 | _require_version() { | ||
17 | @@ -55,34 +53,6 @@ _require_cmd_version() { | ||
18 | _require_version "$cmd" "${BASH_REMATCH[1]}" "$required" | ||
19 | } | ||
20 | |||
21 | -_nix_direnv_resolve_nix() { | ||
22 | - local ambient_nix | ||
23 | - | ||
24 | - if ambient_nix=$(command -v nix); then | ||
25 | - if _require_cmd_version "${ambient_nix}" "${NIX_MIN_VERSION}"; then | ||
26 | - echo "${ambient_nix}" | ||
27 | - return 0 | ||
28 | - else | ||
29 | - _nix_direnv_warning "Nix version in PATH is too old, wanted ${NIX_MIN_VERSION}+, got $(${ambient_nix} --version), will attempt fallback" | ||
30 | - fi | ||
31 | - else | ||
32 | - _nix_direnv_warning "Could not find Nix in PATH, will attempt fallback" | ||
33 | - fi | ||
34 | - | ||
35 | - if [ -n "${NIX_DIRENV_FALLBACK_NIX}" ]; then | ||
36 | - if _require_cmd_version "${NIX_DIRENV_FALLBACK_NIX}" "${NIX_MIN_VERSION}"; then | ||
37 | - echo "${NIX_DIRENV_FALLBACK_NIX}" | ||
38 | - return 0 | ||
39 | - else | ||
40 | - _nix_direnv_error "Fallback Nix version is too old, wanted ${NIX_MIN_VERSION}+, got $(${NIX_DIRENV_FALLBACK_NIX} --version)" | ||
41 | - return 1 | ||
42 | - fi | ||
43 | - else | ||
44 | - _nix_direnv_error "Could not find fallback Nix binary, please add Nix to PATH or set NIX_DIRENV_FALLBACK_NIX" | ||
45 | - return 1 | ||
46 | - fi | ||
47 | -} | ||
48 | - | ||
49 | _nix_direnv_preflight() { | ||
50 | if [[ -z $direnv ]]; then | ||
51 | # shellcheck disable=2016 | ||
52 | @@ -102,10 +72,6 @@ _nix_direnv_preflight() { | ||
53 | fi | ||
54 | fi | ||
55 | |||
56 | - if ! _nix_direnv_nix=$(_nix_direnv_resolve_nix); then | ||
57 | - return 1 | ||
58 | - fi | ||
59 | - | ||
60 | local layout_dir | ||
61 | layout_dir=$(direnv_layout_dir) | ||
62 | |||
diff --git a/overlays/nix-monitored.nix b/overlays/nix-monitored.nix new file mode 100644 index 00000000..9f6caff1 --- /dev/null +++ b/overlays/nix-monitored.nix | |||
@@ -0,0 +1,8 @@ | |||
1 | { final, prev, flakeInputs, ... }: prev.lib.composeExtensions | ||
2 | flakeInputs.nix-monitored.overlays.default | ||
3 | (final: prev: { | ||
4 | nix-monitored = prev.nix-monitored.override { | ||
5 | withNotify = false; | ||
6 | }; | ||
7 | }) | ||
8 | final prev | ||
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/preserve-dscp/default.nix b/overlays/preserve-dscp/default.nix index 105eccb9..208d69db 100644 --- a/overlays/preserve-dscp/default.nix +++ b/overlays/preserve-dscp/default.nix | |||
@@ -15,7 +15,7 @@ | |||
15 | 15 | ||
16 | outputs = [ "out" "lib" ]; | 16 | outputs = [ "out" "lib" ]; |
17 | 17 | ||
18 | buildInputs = with final; [ elfutils libpcap zlib ]; | 18 | buildInputs = with final; [ elfutils libpcap libcap zlib ]; |
19 | nativeBuildInputs = with final; [ llvmPackages.clang llvmPackages.llvm pkg-config bpftools libmnl gnum4 glibc_multi makeWrapper ]; | 19 | nativeBuildInputs = with final; [ llvmPackages.clang llvmPackages.llvm pkg-config bpftools libmnl gnum4 glibc_multi makeWrapper ]; |
20 | 20 | ||
21 | installPhase = '' | 21 | installPhase = '' |
diff --git a/overlays/scutiger.nix b/overlays/scutiger.nix index 7a56adaf..fea15c5f 100644 --- a/overlays/scutiger.nix +++ b/overlays/scutiger.nix | |||
@@ -2,7 +2,7 @@ | |||
2 | scutiger = final.rustPlatform.buildRustPackage { | 2 | scutiger = final.rustPlatform.buildRustPackage { |
3 | inherit (sources.scutiger) pname version src; | 3 | inherit (sources.scutiger) pname version src; |
4 | 4 | ||
5 | cargoHash = "sha256-d+wJ3trrldCVATsudsbglElU6q4LaS6feRocRyHal2k="; | 5 | cargoHash = if prev.lib.versionOlder prev.lib.version "24.05" then "sha256-d+wJ3trrldCVATsudsbglElU6q4LaS6feRocRyHal2k=" else "sha256-FTAEmRuO95ii84uwaALVuImiymnSAQkB2UwZ5yX0WPs="; |
6 | 6 | ||
7 | nativeBuildInputs = with final; [ pkg-config pcre2.dev zlib.dev git ]; | 7 | nativeBuildInputs = with final; [ pkg-config pcre2.dev zlib.dev git ]; |
8 | }; | 8 | }; |
diff --git a/overlays/swayosd/default.nix b/overlays/swayosd/default.nix new file mode 100644 index 00000000..b4601a03 --- /dev/null +++ b/overlays/swayosd/default.nix | |||
@@ -0,0 +1,13 @@ | |||
1 | { final, prev, sources, ... }: { | ||
2 | swayosd = prev.swayosd.overrideAttrs (oldAttrs: rec { | ||
3 | inherit (sources.swayosd) version src; | ||
4 | cargoDeps = prev.rustPlatform.fetchCargoVendor { | ||
5 | inherit (oldAttrs) pname; | ||
6 | inherit version src; | ||
7 | hash = "sha256-yWybf4GKxHrk4WrW5SmjfPD0Gv79tpXOwNLlWeykYy0="; | ||
8 | }; | ||
9 | patches = (oldAttrs.patches or []) ++ [ | ||
10 | ./exponential.patch | ||
11 | ]; | ||
12 | }); | ||
13 | } | ||
diff --git a/overlays/swayosd/exponential.patch b/overlays/swayosd/exponential.patch new file mode 100644 index 00000000..eb90d739 --- /dev/null +++ b/overlays/swayosd/exponential.patch | |||
@@ -0,0 +1,57 @@ | |||
1 | diff --git a/src/brightness_backend/brightnessctl.rs b/src/brightness_backend/brightnessctl.rs | ||
2 | index ccb0e11..740fdb6 100644 | ||
3 | --- a/src/brightness_backend/brightnessctl.rs | ||
4 | +++ b/src/brightness_backend/brightnessctl.rs | ||
5 | @@ -107,10 +107,21 @@ impl VirtualDevice { | ||
6 | } | ||
7 | } | ||
8 | |||
9 | - fn set_percent(&mut self, mut val: u32) -> anyhow::Result<()> { | ||
10 | - val = val.clamp(0, 100); | ||
11 | - self.current = self.max.map(|max| val * max / 100); | ||
12 | - let _: String = self.run(("set", &*format!("{val}%")))?; | ||
13 | + fn val_to_percent(&mut self, val: u32) -> u32 { | ||
14 | + return ((val as f64 / self.get_max() as f64).powf(0.25) * 100_f64).round() as u32; | ||
15 | + } | ||
16 | + fn percent_to_val(&mut self, perc: u32) -> u32 { | ||
17 | + return ((perc as f64 / 100_f64).powf(4_f64) * self.get_max() as f64).round() as u32; | ||
18 | + } | ||
19 | + | ||
20 | + fn set_percent(&mut self, val: u32) -> anyhow::Result<()> { | ||
21 | + let new = self.percent_to_val(val); | ||
22 | + self.set_val(new) | ||
23 | + } | ||
24 | + fn set_val(&mut self, val: u32) -> anyhow::Result<()> { | ||
25 | + let curr = val.clamp(0, self.get_max()); | ||
26 | + self.current = Some(curr); | ||
27 | + let _: String = self.run(("set", &*format!("{curr}")))?; | ||
28 | Ok(()) | ||
29 | } | ||
30 | } | ||
31 | @@ -134,20 +145,18 @@ impl BrightnessBackend for BrightnessCtl { | ||
32 | |||
33 | fn lower(&mut self, by: u32) -> anyhow::Result<()> { | ||
34 | let curr = self.get_current(); | ||
35 | - let max = self.get_max(); | ||
36 | - | ||
37 | - let curr = curr * 100 / max; | ||
38 | + let mut new = self.device.val_to_percent(curr).saturating_sub(by); | ||
39 | + new = self.device.percent_to_val(new).min(curr.saturating_sub(1)); | ||
40 | |||
41 | - self.device.set_percent(curr.saturating_sub(by)) | ||
42 | + self.device.set_val(new) | ||
43 | } | ||
44 | |||
45 | fn raise(&mut self, by: u32) -> anyhow::Result<()> { | ||
46 | let curr = self.get_current(); | ||
47 | - let max = self.get_max(); | ||
48 | - | ||
49 | - let curr = curr * 100 / max; | ||
50 | + let mut new = self.device.val_to_percent(curr) + by; | ||
51 | + new = self.device.percent_to_val(new).max(curr + 1); | ||
52 | |||
53 | - self.device.set_percent(curr + by) | ||
54 | + self.device.set_val(new) | ||
55 | } | ||
56 | |||
57 | fn set(&mut self, val: u32) -> anyhow::Result<()> { | ||
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/waybar.nix b/overlays/waybar.nix index 20f37255..e7e3b807 100644 --- a/overlays/waybar.nix +++ b/overlays/waybar.nix | |||
@@ -1,3 +1,8 @@ | |||
1 | { final, prev, flakeInputs, ... }: | 1 | { final, prev, flakeInputs, ... }: prev.lib.composeExtensions |
2 | 2 | flakeInputs.waybar.overlays.default | |
3 | flakeInputs.waybar.overlays.default final prev | 3 | (final: prev: { |
4 | waybar = prev.waybar.overrideAttrs (oldAttrs: { | ||
5 | dontVersionCheck = true; | ||
6 | }); | ||
7 | }) | ||
8 | final prev | ||
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 5ecdf149..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.python310; | 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 54182b09..00000000 --- a/overlays/worktime/poetry.lock +++ /dev/null | |||
@@ -1,248 +0,0 @@ | |||
1 | # This file is automatically @generated by Poetry 1.6.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 | files = [ | ||
10 | {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, | ||
11 | {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, | ||
12 | ] | ||
13 | |||
14 | [[package]] | ||
15 | name = "certifi" | ||
16 | version = "2022.12.7" | ||
17 | description = "Python package for providing Mozilla's CA Bundle." | ||
18 | optional = false | ||
19 | python-versions = ">=3.6" | ||
20 | files = [ | ||
21 | {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, | ||
22 | {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, | ||
23 | ] | ||
24 | |||
25 | [[package]] | ||
26 | name = "charset-normalizer" | ||
27 | version = "3.1.0" | ||
28 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." | ||
29 | optional = false | ||
30 | python-versions = ">=3.7.0" | ||
31 | files = [ | ||
32 | {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, | ||
33 | {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, | ||
34 | {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, | ||
35 | {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, | ||
36 | {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, | ||
37 | {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, | ||
38 | {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, | ||
39 | {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, | ||
40 | {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, | ||
41 | {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, | ||
42 | {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, | ||
43 | {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, | ||
44 | {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, | ||
45 | {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, | ||
46 | {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, | ||
47 | {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, | ||
48 | {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, | ||
49 | {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, | ||
50 | {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, | ||
51 | {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, | ||
52 | {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, | ||
53 | {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, | ||
54 | {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, | ||
55 | {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, | ||
56 | {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, | ||
57 | {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, | ||
58 | {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, | ||
59 | {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, | ||
60 | {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, | ||
61 | {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, | ||
62 | {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, | ||
63 | {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, | ||
64 | {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, | ||
65 | {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, | ||
66 | {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, | ||
67 | {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, | ||
68 | {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, | ||
69 | {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, | ||
70 | {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, | ||
71 | {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, | ||
72 | {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, | ||
73 | {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, | ||
74 | {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, | ||
75 | {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, | ||
76 | {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, | ||
77 | {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, | ||
78 | {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, | ||
79 | {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, | ||
80 | {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, | ||
81 | {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, | ||
82 | {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, | ||
83 | {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, | ||
84 | {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, | ||
85 | {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, | ||
86 | {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, | ||
87 | {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, | ||
88 | {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, | ||
89 | {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, | ||
90 | {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, | ||
91 | {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, | ||
92 | {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, | ||
93 | {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, | ||
94 | {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, | ||
95 | {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, | ||
96 | {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, | ||
97 | {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, | ||
98 | {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, | ||
99 | {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, | ||
100 | {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, | ||
101 | {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, | ||
102 | {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, | ||
103 | {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, | ||
104 | {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, | ||
105 | {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, | ||
106 | {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, | ||
107 | ] | ||
108 | |||
109 | [[package]] | ||
110 | name = "idna" | ||
111 | version = "3.4" | ||
112 | description = "Internationalized Domain Names in Applications (IDNA)" | ||
113 | optional = false | ||
114 | python-versions = ">=3.5" | ||
115 | files = [ | ||
116 | {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, | ||
117 | {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, | ||
118 | ] | ||
119 | |||
120 | [[package]] | ||
121 | name = "jsonpickle" | ||
122 | version = "3.0.2" | ||
123 | description = "Python library for serializing any arbitrary object graph into JSON" | ||
124 | optional = false | ||
125 | python-versions = ">=3.7" | ||
126 | files = [ | ||
127 | {file = "jsonpickle-3.0.2-py3-none-any.whl", hash = "sha256:4a8442d97ca3f77978afa58068768dba7bff2dbabe79a9647bc3cdafd4ef019f"}, | ||
128 | {file = "jsonpickle-3.0.2.tar.gz", hash = "sha256:e37abba4bfb3ca4a4647d28bb9f4706436f7b46c8a8333b4a718abafa8e46b37"}, | ||
129 | ] | ||
130 | |||
131 | [package.extras] | ||
132 | docs = ["jaraco.packaging (>=3.2)", "rst.linker (>=1.9)", "sphinx"] | ||
133 | testing = ["ecdsa", "feedparser", "gmpy2", "numpy", "pandas", "pymongo", "pytest (>=3.5,!=3.7.3)", "pytest-black-multipy", "pytest-checkdocs (>=1.2.3)", "pytest-cov", "pytest-flake8 (>=1.1.1)", "scikit-learn", "sqlalchemy"] | ||
134 | testing-libs = ["simplejson", "ujson"] | ||
135 | |||
136 | [[package]] | ||
137 | name = "python-dateutil" | ||
138 | version = "2.8.2" | ||
139 | description = "Extensions to the standard Python datetime module" | ||
140 | optional = false | ||
141 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" | ||
142 | files = [ | ||
143 | {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, | ||
144 | {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, | ||
145 | ] | ||
146 | |||
147 | [package.dependencies] | ||
148 | six = ">=1.5" | ||
149 | |||
150 | [[package]] | ||
151 | name = "pyxdg" | ||
152 | version = "0.28" | ||
153 | description = "PyXDG contains implementations of freedesktop.org standards in python." | ||
154 | optional = false | ||
155 | python-versions = "*" | ||
156 | files = [ | ||
157 | {file = "pyxdg-0.28-py2.py3-none-any.whl", hash = "sha256:bdaf595999a0178ecea4052b7f4195569c1ff4d344567bccdc12dfdf02d545ab"}, | ||
158 | {file = "pyxdg-0.28.tar.gz", hash = "sha256:3267bb3074e934df202af2ee0868575484108581e6f3cb006af1da35395e88b4"}, | ||
159 | ] | ||
160 | |||
161 | [[package]] | ||
162 | name = "requests" | ||
163 | version = "2.28.2" | ||
164 | description = "Python HTTP for Humans." | ||
165 | optional = false | ||
166 | python-versions = ">=3.7, <4" | ||
167 | files = [ | ||
168 | {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, | ||
169 | {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, | ||
170 | ] | ||
171 | |||
172 | [package.dependencies] | ||
173 | certifi = ">=2017.4.17" | ||
174 | charset-normalizer = ">=2,<4" | ||
175 | idna = ">=2.5,<4" | ||
176 | urllib3 = ">=1.21.1,<1.27" | ||
177 | |||
178 | [package.extras] | ||
179 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] | ||
180 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] | ||
181 | |||
182 | [[package]] | ||
183 | name = "six" | ||
184 | version = "1.16.0" | ||
185 | description = "Python 2 and 3 compatibility utilities" | ||
186 | optional = false | ||
187 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" | ||
188 | files = [ | ||
189 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, | ||
190 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, | ||
191 | ] | ||
192 | |||
193 | [[package]] | ||
194 | name = "tabulate" | ||
195 | version = "0.9.0" | ||
196 | description = "Pretty-print tabular data" | ||
197 | optional = false | ||
198 | python-versions = ">=3.7" | ||
199 | files = [ | ||
200 | {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, | ||
201 | {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, | ||
202 | ] | ||
203 | |||
204 | [package.extras] | ||
205 | widechars = ["wcwidth"] | ||
206 | |||
207 | [[package]] | ||
208 | name = "toml" | ||
209 | version = "0.10.2" | ||
210 | description = "Python Library for Tom's Obvious, Minimal Language" | ||
211 | optional = false | ||
212 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" | ||
213 | files = [ | ||
214 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, | ||
215 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, | ||
216 | ] | ||
217 | |||
218 | [[package]] | ||
219 | name = "uritools" | ||
220 | version = "4.0.1" | ||
221 | description = "URI parsing, classification and composition" | ||
222 | optional = false | ||
223 | python-versions = "~=3.7" | ||
224 | files = [ | ||
225 | {file = "uritools-4.0.1-py3-none-any.whl", hash = "sha256:d122d394ed6e6e15ac0fddba6a5b19e9fa204e7797507815cbfb0e1455ac0475"}, | ||
226 | {file = "uritools-4.0.1.tar.gz", hash = "sha256:efc5c3a6de05404850685a8d3f34da8476b56aa3516fbf8eff5c8704c7a2826f"}, | ||
227 | ] | ||
228 | |||
229 | [[package]] | ||
230 | name = "urllib3" | ||
231 | version = "1.26.15" | ||
232 | description = "HTTP library with thread-safe connection pooling, file post, and more." | ||
233 | optional = false | ||
234 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" | ||
235 | files = [ | ||
236 | {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, | ||
237 | {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, | ||
238 | ] | ||
239 | |||
240 | [package.extras] | ||
241 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] | ||
242 | secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] | ||
243 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] | ||
244 | |||
245 | [metadata] | ||
246 | lock-version = "2.0" | ||
247 | python-versions = "^3.10" | ||
248 | content-hash = "d9137b4f8e37bba934abf732e4a2aeeb9924c4b6576830d8ae08bdb43b4e147f" | ||
diff --git a/overlays/worktime/pyproject.toml b/overlays/worktime/pyproject.toml index 08002d4d..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.10" | ||
9 | pyxdg = "^0.28" | ||
10 | python-dateutil = "^2.8.2" | ||
11 | uritools = "^4.0.1" | ||
12 | requests = "^2.28.2" | ||
13 | tabulate = "^0.9.0" | ||
14 | backoff = "^2.2.1" | ||
15 | toml = "^0.10.2" | ||
16 | jsonpickle = "^3.0.2" | ||
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 362c8da4..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 | ||
@@ -23,80 +25,80 @@ import argparse | |||
23 | from copy import deepcopy | 25 | from copy import deepcopy |
24 | 26 | ||
25 | import sys | 27 | import sys |
26 | from sys import stderr | 28 | 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 |
43 | import json | ||
44 | |||
45 | import asyncio | ||
46 | |||
47 | from frozendict import frozendict | ||
48 | from contextlib import closing | ||
49 | import os | ||
50 | from time import clock_gettime_ns, CLOCK_MONOTONIC | ||
51 | from atomicwriter import AtomicWriter | ||
52 | import desktop_notify.aio as notify | ||
53 | |||
54 | class BearerAuth(requests.auth.AuthBase): | ||
55 | def __init__(self, token): | ||
56 | self.token = token | ||
57 | def __call__(self, r): | ||
58 | r.headers["authorization"] = "Bearer " + self.token | ||
59 | return r | ||
60 | |||
61 | class KimaiSession(requests.Session): | ||
62 | def __init__(self, base_url: str, api_token: str): | ||
63 | super().__init__() | ||
64 | self.base_url = base_url | ||
65 | self.auth = BearerAuth(api_token) | ||
66 | retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[500, 502, 503, 504]) | ||
67 | super().mount(base_url, HTTPAdapter(max_retries=retries)) | ||
68 | |||
69 | def request(self, method, url, *args, **kwargs): | ||
70 | joined_url = urljoin(self.base_url, url) | ||
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']}) | ||
72 | |||
73 | class KimaiAPI(object): | ||
74 | def __init__(self, base_url: str, api_token: str, clients: Iterable[str]): | ||
75 | self._session = KimaiSession(base_url, api_token) | ||
76 | self._kimai_clients = self._session.get('/api/customers').json() | ||
77 | self._client_ids = self.resolve_clients(clients) | ||
78 | kimai_user = self._session.get('/api/users/me').json() | ||
79 | self._tz = gettz(kimai_user['timezone']) | ||
80 | |||
81 | def resolve_clients(self, clients: Iterable[str]) -> frozenset[int]: | ||
82 | return frozenset({ client['id'] for client in self._kimai_clients if client['name'] in clients }) | ||
83 | |||
84 | def render_datetime(self, datetime: datetime) -> str: | ||
85 | return datetime.astimezone(self._tz).strftime('%Y-%m-%dT%H:%M:%S') | ||
86 | |||
87 | def get_timesheets(self, params: dict[str, Any] = {}) -> Generator[Any]: | ||
88 | for page in count(start=1): | ||
89 | resp = self._session.get('/api/timesheets', params=params | {'size': 100, 'page': page}) | ||
90 | if resp.status_code == 404: | ||
91 | break | ||
92 | yield from resp.json() | ||
41 | 93 | ||
42 | class TogglAPISection(Enum): | 94 | def entry_durations(self, start_date: datetime, *, end_date: datetime, clients: Iterable[str] | None = None) -> Generator[timedelta]: |
43 | TOGGL = '/api/v9' | 95 | client_ids = None |
44 | REPORTS = '/reports/api/v2' | 96 | if clients is not None and not clients: |
45 | |||
46 | class TogglAPIError(Exception): | ||
47 | def __init__(self, response, *, http_error=None): | ||
48 | self.http_error = http_error | ||
49 | self.response = response | ||
50 | |||
51 | def __str__(self): | ||
52 | if not self.http_error is None: | ||
53 | return str(self.http_error) | ||
54 | else: | ||
55 | return self.response.text | ||
56 | |||
57 | class TogglAPI(object): | ||
58 | def __init__(self, api_token, workspace_id, client_ids): | ||
59 | self._api_token = api_token | ||
60 | self._workspace_id = workspace_id | ||
61 | self._client_ids = set(map(int, client_ids.split(','))) if client_ids else None | ||
62 | |||
63 | def _make_url(self, api=TogglAPISection.TOGGL, section=['me', 'time_entries', 'current'], params={}): | ||
64 | if api is TogglAPISection.REPORTS: | ||
65 | params.update({'user_agent': 'worktime', 'workspace_id': self._workspace_id}) | ||
66 | |||
67 | api_path = api.value | ||
68 | section_path = '/'.join(section) | ||
69 | uri = uricompose(scheme='https', host='api.track.toggl.com', path=f"{api_path}/{section_path}", query=params) | ||
70 | |||
71 | return uri | ||
72 | |||
73 | def _query(self, url, method): | ||
74 | response = self._raw_query(url, method) | ||
75 | response.raise_for_status() | ||
76 | return response | ||
77 | |||
78 | @backoff.on_predicate( | ||
79 | backoff.expo, | ||
80 | factor=0.1, max_value=2, | ||
81 | predicate=lambda r: r.status_code == 429, | ||
82 | max_time=10, | ||
83 | ) | ||
84 | def _raw_query(self, url, method): | ||
85 | headers = {'content-type': 'application/json', 'accept': 'application/json'} | ||
86 | response = None | ||
87 | |||
88 | if method == 'GET': | ||
89 | response = requests.get(url, headers=headers, auth=HTTPBasicAuth(self._api_token, 'api_token')) | ||
90 | elif method == 'POST': | ||
91 | response = requests.post(url, headers=headers, auth=HTTPBasicAuth(self._api_token, 'api_token')) | ||
92 | else: | ||
93 | raise ValueError(f"Undefined HTTP method “{method}”") | ||
94 | |||
95 | return response | ||
96 | |||
97 | def entry_durations(self, start_date, *, end_date, rounding=False, client_ids): | ||
98 | if client_ids is not None and not client_ids: | ||
99 | return | 97 | return |
98 | elif clients is None: | ||
99 | client_ids = self._client_ids | ||
100 | else: | ||
101 | client_ids = self.resolve_clients(clients) | ||
100 | 102 | ||
101 | cache_dir = Path(BaseDirectory.save_cache_path('worktime')) / 'entry_durations' | 103 | cache_dir = Path(BaseDirectory.save_cache_path('worktime')) / 'entry_durations' |
102 | step = timedelta(days = 120) | 104 | step = timedelta(days = 120) |
@@ -115,11 +117,8 @@ class TogglAPI(object): | |||
115 | cache_key = blake2s(jsonpickle.encode({ | 117 | cache_key = blake2s(jsonpickle.encode({ |
116 | 'start': req_start, | 118 | 'start': req_start, |
117 | 'end': req_end, | 119 | 'end': req_end, |
118 | 'rounding': rounding, | 120 | 'client_ids': client_ids, |
119 | 'clients': client_ids, | 121 | }).encode('utf-8'), key = self._session.auth.token.encode('utf-8')).hexdigest() |
120 | 'workspace': self._workspace_id, | ||
121 | 'workspace_clients': self._client_ids | ||
122 | }).encode('utf-8'), key = self._api_token.encode('utf-8')).hexdigest() | ||
123 | 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' |
124 | try: | 123 | try: |
125 | with cache_path.open('r', encoding='utf-8') as ch: | 124 | with cache_path.open('r', encoding='utf-8') as ch: |
@@ -129,85 +128,83 @@ class TogglAPI(object): | |||
129 | pass | 128 | pass |
130 | 129 | ||
131 | entries = list() | 130 | entries = list() |
132 | params = { 'since': (req_start - timedelta(days=1)).date().isoformat(), | 131 | params = { |
133 | 'until': (req_end + timedelta(days=1)).date().isoformat(), | 132 | 'begin': self.render_datetime(req_start), |
134 | 'rounding': 'yes' if rounding else 'no', | 133 | 'end': self.render_datetime(req_end), |
135 | 'billable': 'yes' | 134 | 'customers[]': list(client_ids), |
136 | } | 135 | 'billable': 1, |
137 | if client_ids is not None: | 136 | } |
138 | params |= { 'client_ids': ','.join(map(str, client_ids)) } | 137 | |
139 | for page in count(start = 1): | 138 | for entry in self.get_timesheets(params): |
140 | url = self._make_url(api = TogglAPISection.REPORTS, section = ['details'], params = params | { 'page': page }) | 139 | if entry['end'] is None: |
141 | r = self._query(url = url, method='GET') | 140 | continue |
142 | if not r or not r.json(): | 141 | |
143 | raise TogglAPIError(r) | 142 | start = isoparse(entry['begin']) |
144 | report = r.json() | 143 | end = isoparse(entry['end']) |
145 | for entry in report['data']: | 144 | |
146 | start = isoparse(entry['start']) | 145 | if start > req_end or end < req_start: |
147 | end = isoparse(entry['end']) | 146 | continue |
148 | |||
149 | if start > req_end or end < req_start: | ||
150 | continue | ||
151 | 147 | ||
152 | x = min(end, req_end) - max(start, req_start) | 148 | x = min(end, req_end) - max(start, req_start) |
153 | if cache_key: | 149 | if cache_key: |
154 | entries.append(x) | 150 | entries.append(x) |
155 | yield x | 151 | yield x |
156 | if not report['data']: | ||
157 | break | ||
158 | 152 | ||
159 | if cache_path: | 153 | if cache_path: |
160 | cache_path.parent.mkdir(parents=True, exist_ok=True) | 154 | cache_path.parent.mkdir(parents=True, exist_ok=True) |
161 | with cache_path.open('w', encoding='utf-8') as ch: | 155 | with cache_path.open('w', encoding='utf-8') as ch: |
162 | ch.write(jsonpickle.encode(entries)) | 156 | ch.write(jsonpickle.encode(entries)) |
163 | # res = timedelta(milliseconds=report['total_billable']) if report['total_billable'] else timedelta(milliseconds=0) | ||
164 | # return res | ||
165 | 157 | ||
166 | 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: |
167 | billable_acc = timedelta(milliseconds = 0) | 159 | return sum(self.entry_durations(start_date, end_date=end_date), start=timedelta(milliseconds=0)) |
168 | if 0 in self._client_ids: | ||
169 | url = self._make_url(api = TogglAPISection.TOGGL, section = ['workspaces', self._workspace_id, 'clients']) | ||
170 | r = self._query(url = url, method = 'GET') | ||
171 | if not r or not r.json(): | ||
172 | raise TogglAPIError(r) | ||
173 | 160 | ||
174 | 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: |
175 | 162 | kimai_entries = self._session.get('/api/timesheets/active').json() | |
176 | 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: |
177 | 164 | return None | |
178 | return billable_acc | 165 | entry = kimai_entries[0] |
179 | 166 | ||
180 | def get_running_clock(self, now=datetime.now(timezone.utc)): | 167 | if entry['project']['customer']['id'] not in self._client_ids: |
181 | url = self._make_url(api = TogglAPISection.TOGGL, section = ['me', 'time_entries', 'current']) | 168 | return None |
182 | r = self._query(url = url, method='GET') | ||
183 | 169 | ||
184 | if not r or (not r.json() and r.json() is not None): | 170 | return entry |
185 | raise TogglAPIError(r) | ||
186 | 171 | ||
187 | 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: | ||
188 | return None | 175 | return None |
176 | start = isoparse(entry['begin']) | ||
177 | return now - start if start <= now else None | ||
189 | 178 | ||
190 | if self._client_ids is not None: | 179 | def get_recent_entries(self) -> Generator[Any]: |
191 | if 'pid' in r.json() and r.json()['pid']: | 180 | step = timedelta(days = 7) |
192 | url = self._make_url(api = TogglAPISection.TOGGL, section = ['projects', str(r.json()['pid'])]) | 181 | now = datetime.now().astimezone(timezone.utc) |
193 | pr = self._query(url = url, method = 'GET') | 182 | ids = set() |
194 | if not pr or not pr.json(): | 183 | for req_end in (now - step * i for i in count()): |
195 | raise TogglAPIError(pr) | 184 | params = { |
196 | 185 | 'begin': self.render_datetime(req_end - step), | |
197 | if not pr.json(): | 186 | 'end': self.render_datetime(req_end), |
198 | return None | 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 | ||
199 | 194 | ||
200 | if 'cid' in pr.json() and pr.json()['cid']: | 195 | def start_clock(self, project_id: int, activity_id: int, description: str | None = None, tags: Iterable[str] | None = None, billable: bool = True): |
201 | if pr.json()['cid'] not in self._client_ids: | 196 | self._session.post('/api/timesheets', json={ |
202 | return None | 197 | 'begin': self.render_datetime(datetime.now()), |
203 | elif 0 not in self._client_ids: | 198 | 'project': project_id, |
204 | return None | 199 | 'activity': activity_id, |
205 | elif 0 not in self._client_ids: | 200 | 'description': description if description else '', |
206 | return None | 201 | 'tags': (','.join(tags)) if tags else '', |
202 | 'billable': billable, | ||
203 | }).raise_for_status() | ||
207 | 204 | ||
208 | start = isoparse(r.json()['start']) | 205 | def stop_clock(self, running_id: int): |
206 | self._session.patch(f'/api/timesheets/{running_id}/stop').raise_for_status() | ||
209 | 207 | ||
210 | return now - start if start <= now else None | ||
211 | 208 | ||
212 | class Worktime(object): | 209 | class Worktime(object): |
213 | time_worked = timedelta() | 210 | time_worked = timedelta() |
@@ -223,6 +220,7 @@ class Worktime(object): | |||
223 | leave_budget = dict() | 220 | leave_budget = dict() |
224 | time_per_day = None | 221 | time_per_day = None |
225 | workdays = None | 222 | workdays = None |
223 | pull_forward = dict() | ||
226 | 224 | ||
227 | @staticmethod | 225 | @staticmethod |
228 | @cache | 226 | @cache |
@@ -279,10 +277,10 @@ class Worktime(object): | |||
279 | 277 | ||
280 | config = Worktime.config() | 278 | config = Worktime.config() |
281 | config_dir = BaseDirectory.load_first_config('worktime') | 279 | config_dir = BaseDirectory.load_first_config('worktime') |
282 | api = TogglAPI( | 280 | api = KimaiAPI( |
283 | api_token=config.get("TOGGL", {}).get("ApiToken", None), | 281 | base_url=config.get("KIMAI", {}).get("BaseUrl", None), |
284 | workspace_id=config.get("TOGGL", {}).get("Workspace", None), | 282 | api_token=config.get("KIMAI", {}).get("ApiToken", None), |
285 | client_ids=config.get("TOGGL", {}).get("ClientIds", None) | 283 | clients=config.get("KIMAI", {}).get("Clients", None) |
286 | ) | 284 | ) |
287 | date_format = config.get("WORKTIME", {}).get("DateFormat", '%Y-%m-%d') | 285 | date_format = config.get("WORKTIME", {}).get("DateFormat", '%Y-%m-%d') |
288 | 286 | ||
@@ -390,7 +388,7 @@ class Worktime(object): | |||
390 | if e.errno != 2: | 388 | if e.errno != 2: |
391 | raise e | 389 | raise e |
392 | 390 | ||
393 | pull_forward = dict() | 391 | self.time_per_day = lambda day: timedelta(hours = hours_per_week(day)) / len(self.workdays) - (holidays[day] if day in holidays else timedelta()) |
394 | 392 | ||
395 | start_day = self.start_date.date() | 393 | start_day = self.start_date.date() |
396 | end_day = self.end_date.date() | 394 | end_day = self.end_date.date() |
@@ -418,21 +416,19 @@ class Worktime(object): | |||
418 | if not d == datetime.strptime(c, date_format).replace(tzinfo=tzlocal()).date(): break | 416 | if not d == datetime.strptime(c, date_format).replace(tzinfo=tzlocal()).date(): break |
419 | else: | 417 | else: |
420 | if d >= self.end_date.date(): | 418 | if d >= self.end_date.date(): |
421 | pull_forward[d] = min(timedelta(hours = float(hours)), self.time_per_day(d) - (holidays[d] if d in holidays else timedelta())) | 419 | self.pull_forward[d] = min(timedelta(hours = float(hours)), self.time_per_day(d) - (holidays[d] if d in holidays else timedelta())) |
422 | except IOError as e: | 420 | except IOError as e: |
423 | if e.errno != 2: | 421 | if e.errno != 2: |
424 | raise e | 422 | raise e |
425 | 423 | ||
426 | self.days_to_work = dict() | 424 | self.days_to_work = dict() |
427 | 425 | ||
428 | if pull_forward: | 426 | if self.pull_forward: |
429 | end_day = max(end_day, max(list(pull_forward))) | 427 | end_day = max(end_day, max(list(self.pull_forward))) |
430 | 428 | ||
431 | for day in [start_day + timedelta(days = x) for x in range(0, (end_day - start_day).days + 1)]: | 429 | for day in [start_day + timedelta(days = x) for x in range(0, (end_day - start_day).days + 1)]: |
432 | if day.isoweekday() in self.workdays: | 430 | if day.isoweekday() in self.workdays: |
433 | time_to_work = self.time_per_day(day) | 431 | time_to_work = self.time_per_day(day) |
434 | if day in holidays.keys(): | ||
435 | time_to_work -= holidays[day] | ||
436 | if time_to_work > timedelta(): | 432 | if time_to_work > timedelta(): |
437 | self.days_to_work[day] = time_to_work | 433 | self.days_to_work[day] = time_to_work |
438 | 434 | ||
@@ -470,17 +466,17 @@ class Worktime(object): | |||
470 | self.extra_days_to_work[self.now.date()] = timedelta() | 466 | self.extra_days_to_work[self.now.date()] = timedelta() |
471 | 467 | ||
472 | self.time_to_work = sum([self.days_to_work[day] for day in self.days_to_work.keys() if day <= self.end_date.date()], timedelta()) | 468 | self.time_to_work = sum([self.days_to_work[day] for day in self.days_to_work.keys() if day <= self.end_date.date()], timedelta()) |
473 | for day in [d for d in list(pull_forward) if d > self.end_date.date()]: | 469 | for day in [d for d in list(self.pull_forward) if d > self.end_date.date()]: |
474 | days_forward = set([d for d in self.days_to_work.keys() if d >= self.end_date.date() and d < day and (not d in pull_forward or d == self.end_date.date())]) | 470 | days_forward = set([d for d in self.days_to_work.keys() if d >= self.end_date.date() and d < day and (not d in self.pull_forward or d == self.end_date.date())]) |
475 | extra_days_forward = set([d for d in self.extra_days_to_work.keys() if d >= self.end_date.date() and d < day and (not d in pull_forward or d == self.end_date.date())]) | 471 | extra_days_forward = set([d for d in self.extra_days_to_work.keys() if d >= self.end_date.date() and d < day and (not d in self.pull_forward or d == self.end_date.date())]) |
476 | days_forward = days_forward.union(extra_days_forward) | 472 | days_forward = days_forward.union(extra_days_forward) |
477 | 473 | ||
478 | extra_day_time_left = timedelta() | 474 | extra_day_time_left = timedelta() |
479 | for extra_day in extra_days_forward: | 475 | for extra_day in extra_days_forward: |
480 | day_time = max(timedelta(), self.time_per_day(extra_day) - self.extra_days_to_work[extra_day]) | 476 | day_time = max(timedelta(), self.time_per_day(extra_day) - self.extra_days_to_work[extra_day]) |
481 | extra_day_time_left += day_time | 477 | extra_day_time_left += day_time |
482 | extra_day_time = min(extra_day_time_left, pull_forward[day]) | 478 | extra_day_time = min(extra_day_time_left, self.pull_forward[day]) |
483 | time_forward = pull_forward[day] - extra_day_time | 479 | time_forward = self.pull_forward[day] - extra_day_time |
484 | if extra_day_time_left > timedelta(): | 480 | if extra_day_time_left > timedelta(): |
485 | for extra_day in extra_days_forward: | 481 | for extra_day in extra_days_forward: |
486 | day_time = max(timedelta(), self.time_per_day(extra_day) - self.extra_days_to_work[extra_day]) | 482 | day_time = max(timedelta(), self.time_per_day(extra_day) - self.extra_days_to_work[extra_day]) |
@@ -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: |
@@ -518,7 +514,14 @@ def format_days(worktime, days, date_format=None): | |||
518 | return ', '.join(map(lambda group: ','.join(map(format_group, group)), groups)) | 514 | return ', '.join(map(lambda group: ','.join(map(format_group, group)), groups)) |
519 | 515 | ||
520 | 516 | ||
521 | def worktime(**args): | 517 | def tooltip_timedelta(td): |
518 | if td < timedelta(seconds = 0): | ||
519 | return "-" + tooltip_timedelta(-td) | ||
520 | mm, ss = divmod(td.total_seconds(), 60) | ||
521 | hh, mm = divmod(mm, 60) | ||
522 | return "%d:%02d:%02d" % (hh, mm, ss) | ||
523 | |||
524 | def worktime(pull_forward_cutoff, waybar, **args): | ||
522 | worktime = Worktime(**args) | 525 | worktime = Worktime(**args) |
523 | 526 | ||
524 | def format_worktime(worktime): | 527 | def format_worktime(worktime): |
@@ -557,24 +560,41 @@ def worktime(**args): | |||
557 | return f"{indicator}{difference_string}" | 560 | return f"{indicator}{difference_string}" |
558 | else: | 561 | else: |
559 | difference_string = difference_string(total_minutes_difference * timedelta(minutes = 1)) | 562 | difference_string = difference_string(total_minutes_difference * timedelta(minutes = 1)) |
560 | if worktime.now_is_workday: | 563 | return difference_string |
561 | return difference_string | 564 | |
562 | else: | 565 | out_class = "running" if worktime.running_entry else "stopped" |
563 | return f"({difference_string})" | 566 | difference = worktime.time_to_work - worktime.time_worked |
564 | 567 | if worktime.running_entry and -min(timedelta(milliseconds=0), difference) > sum(worktime.pull_forward.values(), start=timedelta(milliseconds=0)) or not worktime.running_entry and max(timedelta(milliseconds=0), difference) > worktime.time_per_day(worktime.now.date()) and worktime.now_is_workday: | |
565 | if worktime.time_pulled_forward >= timedelta(minutes = 15): | 568 | out_class = "over" |
569 | pull_forward_sum = sum(worktime.pull_forward.values(), start=timedelta(milliseconds=0)) | ||
570 | if pull_forward_sum >= min(pull_forward_cutoff, timedelta(seconds = 1)): | ||
566 | worktime_no_pulled_forward = deepcopy(worktime) | 571 | worktime_no_pulled_forward = deepcopy(worktime) |
567 | worktime_no_pulled_forward.time_to_work -= worktime_no_pulled_forward.time_pulled_forward | 572 | worktime_no_pulled_forward.time_to_work -= worktime_no_pulled_forward.time_pulled_forward |
568 | worktime_no_pulled_forward.time_pulled_forward = timedelta() | 573 | worktime_no_pulled_forward.time_pulled_forward = timedelta() |
574 | worktime_no_pulled_forward.pull_forward = dict() | ||
575 | worktime.time_to_work += pull_forward_sum | ||
569 | 576 | ||
570 | difference_string = format_worktime(worktime) | ||
571 | difference_string_no_pulled_forward = format_worktime(worktime_no_pulled_forward) | 577 | difference_string_no_pulled_forward = format_worktime(worktime_no_pulled_forward) |
572 | 578 | ||
573 | print(f"{difference_string_no_pulled_forward}…{difference_string}") | 579 | tooltip = tooltip_timedelta(worktime_no_pulled_forward.time_to_work - worktime_no_pulled_forward.time_worked) + "…" + tooltip_timedelta(difference + pull_forward_sum) |
580 | if pull_forward_sum >= pull_forward_cutoff: | ||
581 | out_text = f"{difference_string_no_pulled_forward}…{format_worktime(worktime)}" | ||
582 | else: | ||
583 | out_text = format_worktime(worktime) | ||
574 | else: | 584 | else: |
575 | print(format_worktime(worktime)) | 585 | tooltip = tooltip_timedelta(difference) |
586 | out_text = format_worktime(worktime) | ||
587 | |||
588 | if waybar: | ||
589 | json.dump({"text": out_text, "class": out_class, "tooltip": tooltip}, stdout) | ||
590 | else: | ||
591 | print(out_text) | ||
592 | |||
593 | def pull_forward(**args): | ||
594 | worktime = Worktime(**args) | ||
595 | print(tooltip_timedelta(sum(worktime.pull_forward.values(), start=timedelta(milliseconds=0)))) | ||
576 | 596 | ||
577 | def time_worked(now, **args): | 597 | def time_worked(now, waybar, **args): |
578 | then = now.replace(hour = 0, minute = 0, second = 0, microsecond = 0) | 598 | then = now.replace(hour = 0, minute = 0, second = 0, microsecond = 0) |
579 | if now.time() == time(): | 599 | if now.time() == time(): |
580 | now = now + timedelta(days = 1) | 600 | now = now + timedelta(days = 1) |
@@ -584,33 +604,62 @@ def time_worked(now, **args): | |||
584 | 604 | ||
585 | worked = now.time_worked - then.time_worked | 605 | worked = now.time_worked - then.time_worked |
586 | 606 | ||
607 | out_text = None | ||
608 | out_class = "running" if now.running_entry else "stopped" | ||
609 | tooltip = tooltip_timedelta(worked) | ||
610 | target_time = max(then.time_per_day(then.now.date()), now.time_per_day(now.now.date())) if then.time_per_day(then.now.date()) and now.time_per_day(now.now.date()) else (then.time_per_day(then.now.date()) if then.time_per_day(then.now.date()) else now.time_per_day(now.now.date())); | ||
611 | difference = target_time - worked | ||
612 | difference_pull_forward = difference + now.time_pulled_forward | ||
613 | if now.running_entry and difference_pull_forward < timedelta(seconds=0): | ||
614 | out_class = "over" | ||
587 | if args['do_round']: | 615 | if args['do_round']: |
588 | total_minutes_difference = 5 * ceil(worked / timedelta(minutes = 5)) | 616 | total_minutes_difference = 5 * ceil(worked / timedelta(minutes = 5)) |
589 | (hours_difference, minutes_difference) = divmod(abs(total_minutes_difference), 60) | 617 | (hours_difference, minutes_difference) = divmod(abs(total_minutes_difference), 60) |
590 | sign = '' if total_minutes_difference >= 0 else '-' | 618 | sign = '' if total_minutes_difference >= 0 else '-' |
591 | |||
592 | difference_string = f"{sign}" | ||
593 | if hours_difference != 0: | ||
594 | difference_string += f"{hours_difference}h" | ||
595 | if hours_difference == 0 or minutes_difference != 0: | ||
596 | difference_string += f"{minutes_difference}m" | ||
597 | |||
598 | clockout_time = None | ||
599 | clockout_difference = None | ||
600 | if then.now_is_workday or now.now_is_workday: | ||
601 | target_time = max(then.time_per_day(then.now.date()), now.time_per_day(now.now.date())) if then.time_per_day(then.now.date()) and now.time_per_day(now.now.date()) else (then.time_per_day(then.now.date()) if then.time_per_day(then.now.date()) else now.time_per_day(now.now.date())); | ||
602 | difference = target_time - worked | ||
603 | clockout_difference = 5 * ceil(difference / timedelta(minutes = 5)) | ||
604 | clockout_time = now.now + difference | ||
605 | clockout_time += (5 - clockout_time.minute % 5) * timedelta(minutes = 1) | ||
606 | clockout_time = clockout_time.replace(second = 0, microsecond = 0) | ||
607 | 619 | ||
608 | if now.running_entry and clockout_time and clockout_difference >= 0: | 620 | difference_string = f"{sign}" |
609 | print(f"{difference_string}/{clockout_time:%H:%M}") | 621 | if hours_difference != 0: |
610 | else: | 622 | difference_string += f"{hours_difference}h" |
611 | print(difference_string) | 623 | if hours_difference == 0 or minutes_difference != 0: |
624 | difference_string += f"{minutes_difference}m" | ||
625 | |||
626 | def round_clockout_time(difference): | ||
627 | clockout_time = None | ||
628 | clockout_difference = None | ||
629 | exact_clockout_time = None | ||
630 | if then.now_is_workday or now.now_is_workday: | ||
631 | clockout_difference = 5 * ceil(difference / timedelta(minutes = 5)) | ||
632 | clockout_time = now.now + difference | ||
633 | exact_clockout_time = clockout_time | ||
634 | clockout_time += (5 - clockout_time.minute % 5) * timedelta(minutes = 1) | ||
635 | clockout_time = clockout_time.replace(second = 0, microsecond = 0) | ||
636 | |||
637 | return clockout_time, exact_clockout_time, clockout_difference | ||
638 | |||
639 | clockout_time, exact_clockout_time, clockout_difference = round_clockout_time(difference) | ||
640 | clockout_time_pull_forward, exact_clockout_time_pull_forward, clockout_difference_pull_forward = round_clockout_time(difference_pull_forward) | ||
641 | clockout_pull_forward_sum, exact_clockout_pull_forward_sum, _ = round_clockout_time(now.time_to_work - now.time_worked + sum(now.pull_forward.values(), start=timedelta(milliseconds=0))) | ||
642 | |||
643 | if now.running_entry and clockout_time and (clockout_difference >= 0 or clockout_difference_pull_forward >= 0): | ||
644 | out_text = f"{difference_string}/{clockout_time:%H:%M}" | ||
645 | tooltip = f"{tooltip_timedelta(worked)}/{exact_clockout_time:%H:%M:%S}" | ||
646 | |||
647 | if clockout_pull_forward_sum >= clockout_time_pull_forward and clockout_time_pull_forward != clockout_time: | ||
648 | out_text += f"…{clockout_time_pull_forward:%H:%M}" | ||
649 | if exact_clockout_pull_forward_sum >= exact_clockout_time_pull_forward and exact_clockout_time_pull_forward != exact_clockout_time: | ||
650 | tooltip += f"…{exact_clockout_time_pull_forward:%H:%M:%S}" | ||
651 | else: | ||
652 | out_text = difference_string | ||
653 | else: | ||
654 | out_text = str(worked) | ||
655 | |||
656 | if not now.now_is_workday: | ||
657 | out_text = f'({out_text})' | ||
658 | |||
659 | if waybar: | ||
660 | json.dump({"text": out_text, "class": out_class, "tooltip": tooltip}, stdout) | ||
612 | else: | 661 | else: |
613 | print(worked) | 662 | print(out_text) |
614 | 663 | ||
615 | def diff(now, **args): | 664 | def diff(now, **args): |
616 | now = now.replace(hour = 0, minute = 0, second = 0, microsecond = 0) | 665 | now = now.replace(hour = 0, minute = 0, second = 0, microsecond = 0) |
@@ -798,18 +847,54 @@ def classification(classification_name, table, table_format, **args): | |||
798 | def main(): | 847 | def main(): |
799 | def isotime(s): | 848 | def isotime(s): |
800 | return datetime.fromisoformat(s).replace(tzinfo=tzlocal()) | 849 | return datetime.fromisoformat(s).replace(tzinfo=tzlocal()) |
850 | def duration_minutes(s): | ||
851 | return timedelta(minutes = float(s)) | ||
852 | |||
853 | def set_default_subparser(self, name, args=None, positional_args=0): | ||
854 | """default subparser selection. Call after setup, just before parse_args() | ||
855 | name: is the name of the subparser to call by default | ||
856 | args: if set is the argument list handed to parse_args() | ||
857 | |||
858 | , tested with 2.7, 3.2, 3.3, 3.4 | ||
859 | it works with 2.6 assuming argparse is installed | ||
860 | """ | ||
861 | subparser_found = False | ||
862 | for arg in sys.argv[1:]: | ||
863 | if arg in ['-h', '--help']: # global help if no subparser | ||
864 | break | ||
865 | else: | ||
866 | for x in self._subparsers._actions: | ||
867 | if not isinstance(x, argparse._SubParsersAction): | ||
868 | continue | ||
869 | for sp_name in x._name_parser_map.keys(): | ||
870 | if sp_name in sys.argv[1:]: | ||
871 | subparser_found = True | ||
872 | if not subparser_found: | ||
873 | # insert default in last position before global positional | ||
874 | # arguments, this implies no global options are specified after | ||
875 | # first positional argument | ||
876 | if args is None: | ||
877 | sys.argv.insert(len(sys.argv) - positional_args, name) | ||
878 | else: | ||
879 | args.insert(len(args) - positional_args, name) | ||
880 | |||
881 | argparse.ArgumentParser.set_default_subparser = set_default_subparser | ||
801 | 882 | ||
802 | config = Worktime.config() | 883 | config = Worktime.config() |
803 | 884 | ||
804 | parser = argparse.ArgumentParser(prog = "worktime", description = 'Track worktime using toggl API') | 885 | parser = argparse.ArgumentParser(prog = "worktime", description = 'Track worktime using Kimai API') |
805 | 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())) |
806 | 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) |
807 | parser.add_argument('--no-running', dest = 'include_running', action = 'store_false') | 888 | parser.add_argument('--no-running', dest = 'include_running', action = 'store_false') |
808 | parser.add_argument('--no-force-day-to-work', dest = 'force_day_to_work', action = 'store_false') | 889 | parser.add_argument('--no-force-day-to-work', dest = 'force_day_to_work', action = 'store_false') |
809 | subparsers = parser.add_subparsers(help = 'Subcommands') | 890 | subparsers = parser.add_subparsers(help = 'Subcommands') |
810 | parser.set_defaults(cmd = worktime) | 891 | worktime_parser = subparsers.add_parser('time_worked', aliases = ['time', 'worked']) |
811 | time_worked_parser = subparsers.add_parser('time_worked', aliases = ['time', 'worked', 'today']) | 892 | worktime_parser.add_argument('--pull-forward-cutoff', dest = 'pull_forward_cutoff', metavar = 'MINUTES', type = duration_minutes, default = timedelta(minutes = 15)) |
893 | worktime_parser.add_argument('--waybar', action='store_true') | ||
894 | worktime_parser.set_defaults(cmd = worktime) | ||
895 | time_worked_parser = subparsers.add_parser('today') | ||
812 | time_worked_parser.add_argument('--no-round', dest = 'do_round', action = 'store_false') | 896 | time_worked_parser.add_argument('--no-round', dest = 'do_round', action = 'store_false') |
897 | time_worked_parser.add_argument('--waybar', action='store_true') | ||
813 | time_worked_parser.set_defaults(cmd = time_worked) | 898 | time_worked_parser.set_defaults(cmd = time_worked) |
814 | diff_parser = subparsers.add_parser('diff') | 899 | diff_parser = subparsers.add_parser('diff') |
815 | diff_parser.set_defaults(cmd = diff) | 900 | diff_parser.set_defaults(cmd = diff) |
@@ -827,9 +912,146 @@ def main(): | |||
827 | classification_parser.add_argument('--table', action = 'store_true') | 912 | classification_parser.add_argument('--table', action = 'store_true') |
828 | classification_parser.add_argument('--table-format', dest='table_format', type=str, default='fancy_grid') | 913 | classification_parser.add_argument('--table-format', dest='table_format', type=str, default='fancy_grid') |
829 | classification_parser.set_defaults(cmd = partial(classification, classification_name=classification_name)) | 914 | classification_parser.set_defaults(cmd = partial(classification, classification_name=classification_name)) |
915 | pull_forward_parser = subparsers.add_parser('pull-forward') | ||
916 | pull_forward_parser.set_defaults(cmd = pull_forward) | ||
917 | parser.set_default_subparser('time_worked') | ||
830 | args = parser.parse_args() | 918 | args = parser.parse_args() |
831 | 919 | ||
832 | args.cmd(**vars(args)) | 920 | args.cmd(**vars(args)) |
833 | 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 | |||
834 | if __name__ == "__main__": | 1056 | if __name__ == "__main__": |
835 | 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; |
@@ -1,19 +1,29 @@ | |||
1 | inputs@{ system, self, deploy-rs, nvfetcher, nixpkgs, ca-util, ... }: | 1 | inputs@{ system, self, nvfetcher, nixpkgs, ca-util, ... }: |
2 | let | 2 | 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 |
9 | wireguard-tools | 15 | wireguard-tools |
10 | gup | 16 | gup |
11 | nftables | 17 | nftables |
12 | deploy-rs.packages.${system}.deploy-rs | 18 | deploy-rs.deploy-rs |
13 | knot-dns | 19 | knot-dns |
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 d60507b0..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,11 +157,9 @@ 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; }; | 162 | extraSpecialArgs = { inherit flake flakeInputs path; hostConfig = config; }; |
185 | }; | 163 | }; |
186 | 164 | ||
187 | sops = mkIf hasSops { | 165 | sops = mkIf hasSops { |
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/niri-unstable.nix b/system-profiles/niri-unstable.nix new file mode 100644 index 00000000..3a8b393d --- /dev/null +++ b/system-profiles/niri-unstable.nix | |||
@@ -0,0 +1,11 @@ | |||
1 | { config, pkgs, lib, ... }: | ||
2 | { | ||
3 | config = { | ||
4 | programs.niri.package = lib.mkDefault pkgs.niri-unstable; | ||
5 | home-manager.sharedModules = [ | ||
6 | { | ||
7 | programs.niri.package = lib.mkDefault config.programs.niri.package; | ||
8 | } | ||
9 | ]; | ||
10 | }; | ||
11 | } | ||
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/system-profiles/zfs.nix b/system-profiles/zfs.nix index 149decee..a93dddd2 100644 --- a/system-profiles/zfs.nix +++ b/system-profiles/zfs.nix | |||
@@ -1,8 +1,8 @@ | |||
1 | { pkgs, lib, ... } : { | 1 | { pkgs, lib, ... } : { |
2 | config = { | 2 | config = { |
3 | boot = { | 3 | boot = { |
4 | kernelPackages = pkgs.linuxPackages_6_11; | 4 | kernelPackages = pkgs.linuxPackages_6_12; |
5 | zfs.package = pkgs.zfs_unstable; | 5 | zfs.package = pkgs.zfs_2_3; |
6 | 6 | ||
7 | supportedFilesystems.zfs = true; | 7 | supportedFilesystems.zfs = true; |
8 | }; | 8 | }; |
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/mpv/default.nix b/user-profiles/mpv/default.nix index 83eba2a9..94f241c8 100644 --- a/user-profiles/mpv/default.nix +++ b/user-profiles/mpv/default.nix | |||
@@ -10,7 +10,7 @@ | |||
10 | (pkgs.stdenv.mkDerivation (sources.mpv-reload // rec { | 10 | (pkgs.stdenv.mkDerivation (sources.mpv-reload // rec { |
11 | installPhase = '' | 11 | installPhase = '' |
12 | install -d $out/share/mpv/scripts | 12 | install -d $out/share/mpv/scripts |
13 | install -m 0644 reload.lua $out/share/mpv/scripts/${passthru.scriptName} | 13 | install -m 0644 main.lua $out/share/mpv/scripts/${passthru.scriptName} |
14 | ''; | 14 | ''; |
15 | 15 | ||
16 | passthru.scriptName = "reload.lua"; | 16 | passthru.scriptName = "reload.lua"; |
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/yt-dlp.nix b/user-profiles/yt-dlp.nix index 9e77f734..ef0be87e 100644 --- a/user-profiles/yt-dlp.nix +++ b/user-profiles/yt-dlp.nix | |||
@@ -28,7 +28,7 @@ | |||
28 | # "youtube:formats=dashy" | 28 | # "youtube:formats=dashy" |
29 | # ]; | 29 | # ]; |
30 | remux-video = "mp4>mkv"; | 30 | remux-video = "mp4>mkv"; |
31 | output = "\"%(title)s [%(uploader)s %(webpage_url)s].%(ext)s\""; | 31 | output = lib.mkDefault "\"%(modified_date>%Y%m%d,release_date>%Y%m%d,upload_date>%Y%m%d)s %(title)s [%(uploader)s %(webpage_url)s].%(ext)s\""; |
32 | }; | 32 | }; |
33 | }; | 33 | }; |
34 | }; | 34 | }; |
diff --git a/user-profiles/zsh/default.nix b/user-profiles/zsh/default.nix index daeb7e82..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 | 13 | append = true; | |
14 | plugins = [ | 14 | expireDuplicatesFirst = true; |
15 | { name = "powerlevel10k"; | 15 | extended = true; |
16 | file = "share/zsh-powerlevel10k/powerlevel10k.zsh-theme"; | 16 | findNoDups = true; |
17 | src = pkgs.zsh-powerlevel10k; | 17 | }; |
18 | } | 18 | syntaxHighlighting.enable = true; |
19 | ]; | 19 | zsh-abbr = { |
20 | initExtraFirst = '' | 20 | enable = true; |
21 | if [[ $TERM == "dumb" ]]; then | 21 | abbreviations = { |
22 | unsetopt zle | 22 | re = "systemctl restart"; |
23 | PS1='$ ' | 23 | ure = "systemctl --user restart"; |
24 | return | 24 | st = "systemctl status"; |
25 | fi | 25 | ust = "systemctl --user status"; |
26 | ''; | 26 | }; |
27 | initExtraBeforeCompInit = '' | 27 | globalAbbreviations = { |
28 | source "${cfg.home.homeDirectory}/${p10kZsh}" | 28 | "L" = "| less"; |
29 | ''; | 29 | "S" = "&> /dev/null"; |
30 | initExtra = lib.mkAfter '' | 30 | "G" = "| grep"; |
31 | source ${./zshrc} | 31 | "B" = "&> /dev/null &"; |
32 | source "${pkgs.zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" | 32 | "BB" = "&> /dev/null &!"; |
33 | ''; | 33 | "J" = lib.mkIf config.programs.jq.enable "| jq '.'"; |
34 | }; | ||
35 | }; | ||
36 | |||
37 | plugins = [ | ||
38 | { name = "powerlevel10k"; | ||
39 | file = "share/zsh-powerlevel10k/powerlevel10k.zsh-theme"; | ||
40 | src = pkgs.zsh-powerlevel10k; | ||
41 | } | ||
42 | ]; | ||
43 | initContent = lib.mkMerge [ | ||
44 | (lib.mkBefore '' | ||
45 | if [[ $TERM == "dumb" ]]; then | ||
46 | unsetopt zle | ||
47 | PS1='$ ' | ||
48 | return | ||
49 | fi | ||
50 | '') | ||
51 | (lib.mkOrder 550 '' | ||
52 | source "$HOME/${config.xdg.configFile."zsh/.p10k.zsh".target}" | ||
53 | '') | ||
54 | (lib.mkAfter '' | ||
55 | source ${./zshrc} | ||
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 a83a8069..af3aca64 100644 --- a/user-profiles/zsh/zshrc +++ b/user-profiles/zsh/zshrc | |||
@@ -24,7 +24,12 @@ setopt ignore_eof | |||
24 | bindkey -e | 24 | bindkey -e |
25 | bindkey ';5C' emacs-forward-word | 25 | bindkey ';5C' emacs-forward-word |
26 | bindkey ';5D' emacs-backward-word | 26 | bindkey ';5D' emacs-backward-word |
27 | bindkey '^[[1;5C' emacs-forward-word | ||
28 | bindkey '^[[1;5D' emacs-backward-word | ||
29 | bindkey '^H' backward-kill-word | ||
27 | 30 | ||
28 | autoload -Uz url-quote-magic bracketed-paste-magic | 31 | autoload -Uz url-quote-magic bracketed-paste-magic |
29 | zle -N self-insert url-quote-magic | 32 | zle -N self-insert url-quote-magic |
30 | zle -N bracketed-paste bracketed-paste-magic \ No newline at end of file | 33 | zle -N bracketed-paste bracketed-paste-magic |
34 | |||
35 | setopt extended_glob | ||
diff --git a/users/gkleen/default.nix b/users/gkleen/default.nix index 4ddf4be3..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} = { |
@@ -29,9 +29,39 @@ | |||
29 | userName = "Gregor Kleen"; | 29 | userName = "Gregor Kleen"; |
30 | delta.enable = true; | 30 | delta.enable = true; |
31 | extraConfig = { | 31 | extraConfig = { |
32 | pull.rebase = false; | 32 | core.excludesfile = toString ./gitignore; |
33 | pull.rebase = true; | ||
33 | submodule.recurse = true; | 34 | submodule.recurse = true; |
34 | init.defaultBranch = "main"; | 35 | init.defaultBranch = "main"; |
36 | column.ui = "auto"; | ||
37 | branch.sort = "-committerdate"; | ||
38 | tag.sort = "version:refname"; | ||
39 | diff = { | ||
40 | algorithm = "histogram"; | ||
41 | colorMoved = "plain"; | ||
42 | mnemonicPrefix = true; | ||
43 | renames = true; | ||
44 | }; | ||
45 | push = { | ||
46 | default = "simple"; | ||
47 | autoSetupRemote = true; | ||
48 | followTags = true; | ||
49 | }; | ||
50 | fetch = { | ||
51 | prune = true; | ||
52 | pruneTags = true; | ||
53 | all = true; | ||
54 | }; | ||
55 | rerere = { | ||
56 | enabled = true; | ||
57 | autoupdate = true; | ||
58 | }; | ||
59 | rebase = { | ||
60 | autoSquash = true; | ||
61 | autoStash = true; | ||
62 | updateRefs = true; | ||
63 | }; | ||
64 | merge.conflictstyle = "zdiff3"; | ||
35 | }; | 65 | }; |
36 | }; | 66 | }; |
37 | 67 | ||
diff --git a/users/gkleen/gitignore b/users/gkleen/gitignore new file mode 100644 index 00000000..f7082b20 --- /dev/null +++ b/users/gkleen/gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | **/#*# | ||
2 | **/.#* | ||
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 { |