diff --git a/.git-crypt/.gitattributes b/.git-crypt/.gitattributes new file mode 100644 index 0000000..665b10e --- /dev/null +++ b/.git-crypt/.gitattributes @@ -0,0 +1,4 @@ +# Do not edit this file. To specify the files to encrypt, create your own +# .gitattributes file in the directory where your files are. +* !filter !diff +*.gpg binary diff --git a/.git-crypt/keys/default/0/3044E71E3DEFF49B586CF5809BF4FCCB90854DA9.gpg b/.git-crypt/keys/default/0/3044E71E3DEFF49B586CF5809BF4FCCB90854DA9.gpg new file mode 100644 index 0000000..469ad36 Binary files /dev/null and b/.git-crypt/keys/default/0/3044E71E3DEFF49B586CF5809BF4FCCB90854DA9.gpg differ diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..eea1737 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +**/secrets/** filter=git-crypt diff=git-crypt +**/secrets.yaml diff=sops +*.wav filter=lfs diff=lfs merge=lfs -text + +hosts/iron/services/tvproxy.nix filter=git-crypt diff=git-crypt diff --git a/.sops.yaml b/.sops.yaml index 3dc2cf6..44b3cd9 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -1,10 +1,12 @@ keys: - - &admin_jalr 66FB54F6081375106EEBF651A222365EB448F934 + - &admin_jalr 3044E71E3DEFF49B586CF5809BF4FCCB90854DA9 - &admin_jalr_tb FE170812543DF81393EA56BA5042B8317A10617E - &host_aluminium age1ne08hny30vrkejqhh7dcx4ql6dmkx6jw9dqkf3cz7mzvt53njy0qh59w44 - &host_hafnium age1ahnfjspcpwxxk7getcxkj3fypwt37rr6p3xsmp8n2tqqqz8jtg7q2am0et - &host_iron age1hx7fdu4mcha7kkxe7yevtvs6xgzgaafgenm3drhvr609wlj94sgqm497je - - &host_weinturm_pretix_prod age1w42q9qg7l6gea36erhw0u7jvlpenvtrjm38q4ux0aasa929hes6s2ecj6m + - &host_magnesium age19qkgfaq08kmyxghet48dq4gxwjuy9zpvuyxys9jkmcqa5634537qlxjcd8 + - &host_weinturm_pretix_prod age1djjxl3lcvzs85nj0met6w8ujsz8pvr6ngmmdwlxfh0k9d5lkrpdqlzzehf + - &host_copper age1rrut5ntrkqmvttvmpa5jcmjhr2pfpyaqgu9dmtx6v07lgjxx5ppsl7e5v3 creation_rules: - path_regex: hosts/aluminium/secrets\.yaml$ key_groups: @@ -24,12 +26,24 @@ creation_rules: - *admin_jalr age: - *host_iron + - path_regex: hosts/magnesium/secrets\.yaml$ + key_groups: + - pgp: + - *admin_jalr + age: + - *host_magnesium - path_regex: hosts/weinturm-pretix-prod/secrets\.yaml$ key_groups: - pgp: - *admin_jalr age: - *host_weinturm_pretix_prod + - path_regex: hosts/copper/secrets\.yaml$ + key_groups: + - pgp: + - *admin_jalr + age: + - *host_copper - path_regex: secrets\.yaml$ key_groups: - pgp: diff --git a/README.md b/README.md index 15ccbb8..332bf77 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,27 @@ -## home-manager -https://github.com/nix-community/home-manager - -For a systematic overview of Home Manager and its available options, please see -- the [Home Manager manual](https://nix-community.github.io/home-manager/index.html) and -- the [Home Manager configuration options](https://nix-community.github.io/home-manager/options.html). - +# jalr's NixOS Configuration ## Install a new host This installs nixos on host `somehost`: + +### NixOS Anywhere + +```bash +nix run github:nix-community/nixos-anywhere -- --flake .# root@ +``` + +### The traditional way + ```bash nix-shell -p nixUnstable --run 'nixos-install --flake https://gitlab.jalr.de/jalr/nixos-configuration#somehost --no-channel-copy' ``` +### Build a configuration + +``` +nix build .#nixosConfigurations.iron.config.system.build.toplevel +``` + ### setting up sops Get the host key and convert it. ```bash @@ -46,4 +55,12 @@ nix-repl> :lf .# ``` gpg --card-edit gpg/card> fetch +gpg --edit-key $key +gpg> trust +Your decision? 5 ``` + +## Debugging boot issues + +1. Add `rd.systemd.debug_shell` kernel parameter +2. Press CTRL+ALT+F9 to switch to root shell diff --git a/custom-utils/default.nix b/custom-utils/default.nix new file mode 100644 index 0000000..9bc4a53 --- /dev/null +++ b/custom-utils/default.nix @@ -0,0 +1,5 @@ +{ lib, ... }: + +{ + validatePortAttrset = import ./ports.nix { inherit lib; }; +} diff --git a/custom-utils/ports.nix b/custom-utils/ports.nix new file mode 100644 index 0000000..a8d1a54 --- /dev/null +++ b/custom-utils/ports.nix @@ -0,0 +1,33 @@ +{ lib, ... }: + +let + filterPort = pm: port: ( + lib.attrsets.catAttrs port ( + lib.attrsets.attrValues ( + lib.attrsets.filterAttrs (_: v: v ? "${port}") pm + ) + ) + ); + onlyUniqueItemsInList = x: lib.lists.length x == lib.lists.length (lib.lists.unique x); + mkRange = { from, to }: (lib.lists.range from to); +in +portmap: +if builtins.all + ( + proto: + if onlyUniqueItemsInList + ( + lib.flatten ( + map + (x: + if lib.isInt x then x + else if lib.isList x then x + else if lib.isAttrs x then mkRange x + else builtins.abort "found invalid entry in portmap" + ) + (filterPort portmap proto) + ) + ) then true else builtins.abort "Found duplicate ${proto} ports." + ) [ "tcp" "udp" ] +then portmap +else builtins.abort "Found duplicate ports." diff --git a/flake.lock b/flake.lock index 0a5e625..7c92712 100644 --- a/flake.lock +++ b/flake.lock @@ -1,13 +1,91 @@ { "nodes": { + "asterisk-sounds-de": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nix-filter": [ + "nix-filter" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1748284610, + "narHash": "sha256-B3/OOZC0puXbODupPEbdMA6sJP39MzbMCl4j1HvgNfU=", + "ref": "refs/heads/main", + "rev": "6b1c484318727af78a64aee3f46903493dae8259", + "revCount": 1, + "type": "git", + "url": "https://git.jalr.de/jalr/asterisk-sounds-de" + }, + "original": { + "type": "git", + "url": "https://git.jalr.de/jalr/asterisk-sounds-de" + } + }, + "bldcSrc": { + "flake": false, + "locked": { + "lastModified": 1733324381, + "narHash": "sha256-ui9N8QSog1G5zyK7yRrD0Xl+Y2CZhvvhBkaJuQZ2qZw=", + "owner": "vedderb", + "repo": "bldc", + "rev": "a0d40e2c5a42c810888d8c379307e6b0a118a125", + "type": "github" + }, + "original": { + "owner": "vedderb", + "ref": "release_6_05", + "repo": "bldc", + "type": "github" + } + }, + "crane": { + "locked": { + "lastModified": 1731098351, + "narHash": "sha256-HQkYvKvaLQqNa10KEFGgWHfMAbWBfFp+4cAgkut+NNE=", + "owner": "ipetkov", + "repo": "crane", + "rev": "ef80ead953c1b28316cc3f8613904edc2eb90c28", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "disko": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1762276996, + "narHash": "sha256-TtcPgPmp2f0FAnc+DMEw4ardEgv1SGNR3/WFGH0N19M=", + "owner": "nix-community", + "repo": "disko", + "rev": "af087d076d3860760b3323f6b583f4d828c1ac17", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "disko", + "type": "github" + } + }, "flake-compat": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -16,16 +94,74 @@ "type": "github" } }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1747046372, + "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "lanzaboote", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1730504689, + "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "506278e768c2a08bec68eb62932193e341f55c90", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_2": { + "inputs": { + "nixpkgs-lib": [ + "nur", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1733312601, + "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" }, "locked": { - "lastModified": 1681202837, - "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "cfacdce06f30d2b68473a46042957675eebb3401", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -34,7 +170,49 @@ "type": "github" } }, + "gg-chatmix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1748177977, + "narHash": "sha256-xC/dOrDrZoQhUfVotj/z14iTwGlE80OqSl9S5zkevdA=", + "owner": "nilathedragon", + "repo": "gg-chatmix", + "rev": "1dadaa51794042c20ddc52d52479e8a156bd235b", + "type": "github" + }, + "original": { + "owner": "nilathedragon", + "repo": "gg-chatmix", + "type": "github" + } + }, "gitignore": { + "inputs": { + "nixpkgs": [ + "lanzaboote", + "pre-commit-hooks-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "gitignore_2": { "inputs": { "nixpkgs": [ "nix-pre-commit-hooks", @@ -42,11 +220,11 @@ ] }, "locked": { - "lastModified": 1660459072, - "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", "owner": "hercules-ci", "repo": "gitignore.nix", - "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", "type": "github" }, "original": { @@ -55,6 +233,29 @@ "type": "github" } }, + "gomod2nix": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1759991118, + "narHash": "sha256-pDyrtUQyeP1lVTMIYqJtftzDtsXEZaJjYy9ZQ/SGhL8=", + "owner": "nix-community", + "repo": "gomod2nix", + "rev": "7f8d7438f5870eb167abaf2c39eea3d2302019d1", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "gomod2nix", + "type": "github" + } + }, "home-manager": { "inputs": { "nixpkgs": [ @@ -62,20 +263,35 @@ ] }, "locked": { - "lastModified": 1667907331, - "narHash": "sha256-bHkAwkYlBjkupPUFcQjimNS8gxWSWjOTevEuwdnp5m0=", + "lastModified": 1758463745, + "narHash": "sha256-uhzsV0Q0I9j2y/rfweWeGif5AWe0MGrgZ/3TjpDYdGA=", "owner": "nix-community", "repo": "home-manager", - "rev": "6639e3a837fc5deb6f99554072789724997bc8e5", + "rev": "3b955f5f0a942f9f60cdc9cacb7844335d0f21c3", "type": "github" }, "original": { "owner": "nix-community", - "ref": "release-22.05", + "ref": "release-25.05", "repo": "home-manager", "type": "github" } }, + "impermanence": { + "locked": { + "lastModified": 1737831083, + "narHash": "sha256-LJggUHbpyeDvNagTUrdhe/pRVp4pnS6wVKALS782gRI=", + "owner": "nix-community", + "repo": "impermanence", + "rev": "4b3e914cdf97a5b536a889e939fb2fd2b043a170", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "impermanence", + "type": "github" + } + }, "krops": { "inputs": { "flake-utils": [ @@ -99,86 +315,146 @@ "type": "github" } }, - "nix-pre-commit-hooks": { + "lanzaboote": { "inputs": { + "crane": "crane", "flake-compat": "flake-compat", - "flake-utils": [ - "flake-utils" + "flake-parts": "flake-parts", + "nixpkgs": [ + "nixpkgs" ], - "gitignore": "gitignore", - "nixpkgs": "nixpkgs", - "nixpkgs-stable": "nixpkgs-stable" + "pre-commit-hooks-nix": "pre-commit-hooks-nix", + "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1684842236, - "narHash": "sha256-rYWsIXHvNhVQ15RQlBUv67W3YnM+Pd+DuXGMvCBq2IE=", + "lastModified": 1737639419, + "narHash": "sha256-AEEDktApTEZ5PZXNDkry2YV2k6t0dTgLPEmAZbnigXU=", + "owner": "nix-community", + "repo": "lanzaboote", + "rev": "a65905a09e2c43ff63be8c0e86a93712361f871e", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "v0.4.2", + "repo": "lanzaboote", + "type": "github" + } + }, + "nix-filter": { + "locked": { + "lastModified": 1757882181, + "narHash": "sha256-+cCxYIh2UNalTz364p+QYmWHs0P+6wDhiWR4jDIKQIU=", + "owner": "numtide", + "repo": "nix-filter", + "rev": "59c44d1909c72441144b93cf0f054be7fe764de5", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "nix-filter", + "type": "github" + } + }, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "poetry2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1729742964, + "narHash": "sha256-B4mzTcQ0FZHdpeWcpDYPERtyjJd/NIuaQ9+BV1h+MpA=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "e04df33f62cdcf93d73e9a04142464753a16db67", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, + "nix-pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat_2", + "gitignore": "gitignore_2", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1763319842, + "narHash": "sha256-YG19IyrTdnVn0l3DvcUYm85u3PaqBt6tI6VvolcuHnA=", "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "61e567d6497bc9556f391faebe5e410e6623217f", + "repo": "git-hooks.nix", + "rev": "7275fa67fbbb75891c16d9dee7d88e58aea2d761", "type": "github" }, "original": { "owner": "cachix", "ref": "master", - "repo": "pre-commit-hooks.nix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "nixos-hardware": { + "locked": { + "lastModified": 1762847253, + "narHash": "sha256-BWWnUUT01lPwCWUvS0p6Px5UOBFeXJ8jR+ZdLX8IbrU=", + "owner": "nixos", + "repo": "nixos-hardware", + "rev": "899dc449bc6428b9ee6b3b8f771ca2b0ef945ab9", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "master", + "repo": "nixos-hardware", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1681303793, - "narHash": "sha256-JEdQHsYuCfRL2PICHlOiH/2ue3DwoxUX7DJ6zZxZXFk=", - "owner": "NixOS", + "lastModified": 1763334038, + "narHash": "sha256-LBVOyaH6NFzQ3X/c6vfMZ9k4SV2ofhpxeL9YnhHNJQQ=", + "owner": "nixos", "repo": "nixpkgs", - "rev": "fe2ecaf706a5907b5e54d979fbde4924d84b65fc", + "rev": "4c8cdd5b1a630e8f72c9dd9bf582b1afb3127d2c", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "nixos-unstable", + "owner": "nixos", + "ref": "nixos-25.05", "repo": "nixpkgs", "type": "github" } }, "nixpkgs-stable": { "locked": { - "lastModified": 1678872516, - "narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=", + "lastModified": 1730741070, + "narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "9b8e5abb18324c7fe9f07cb100c3cd4a29cda8b8", + "rev": "d063c1dd113c91ab27959ba540c0d9753409edf3", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-22.11", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-stable_2": { - "locked": { - "lastModified": 1684632198, - "narHash": "sha256-SdxMPd0WmU9MnDBuuy7ouR++GftrThmSGL7PCQj/uVI=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "d0dade110dc7072d67ce27826cfe9ab2ab0cf247", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "release-22.11", + "ref": "nixos-24.05", "repo": "nixpkgs", "type": "github" } }, "nixpkgsMaster": { "locked": { - "lastModified": 1684857135, - "narHash": "sha256-MrX+6QO3xf+Gkm+BgU45jBB/l9XRvH/hGsLfx8fEetU=", + "lastModified": 1763473525, + "narHash": "sha256-NzmsN8hRIn/9rJvZH3vPirBrOJJfeSfvPr4+feeK7LY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "499cad7a722caf0bebb2a382a67fa39c992acebd", + "rev": "15901670689a6f338ebd2a9436b947ec189463a3", "type": "github" }, "original": { @@ -188,29 +464,65 @@ "type": "github" } }, - "nixpkgs_2": { + "nixpkgsOld": { "locked": { - "lastModified": 1684661732, - "narHash": "sha256-2/Xo/UmUUoMXc0T5tzoUsYjMLLMjEfzRWDAQB0WwtW0=", + "lastModified": 1748037224, + "narHash": "sha256-92vihpZr6dwEMV6g98M5kHZIttrWahb9iRPBm1atcPk=", "owner": "nixos", "repo": "nixpkgs", - "rev": "b0671cbf1e5c443f7fbfd4941ee0f8a151435114", + "rev": "f09dede81861f3a83f7f06641ead34f02f37597f", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixos-22.11", + "ref": "nixos-24.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1763283776, + "narHash": "sha256-Y7TDFPK4GlqrKrivOcsHG8xSGqQx3A6c+i7novT85Uk=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "50a96edd8d0db6cc8db57dab6bb6d6ee1f3dc49a", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1735554305, + "narHash": "sha256-zExSA1i/b+1NMRhGGLtNfFGXgLtgo+dcuzHzaWA6w3Q=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "0e82ab234249d8eee3e8c91437802b32c74bb3fd", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", "repo": "nixpkgs", "type": "github" } }, "nur": { + "inputs": { + "flake-parts": "flake-parts_2", + "nixpkgs": "nixpkgs_2" + }, "locked": { - "lastModified": 1684856747, - "narHash": "sha256-sauDfmQDn1NFW2IdQ5aOcwcU5YTJ+OTN7VpqskVXrb0=", + "lastModified": 1763471545, + "narHash": "sha256-B1ua1UtkPuMwT8o4nOR7yNP5yz10usMcNnxwHpGtLck=", "owner": "nix-community", "repo": "NUR", - "rev": "2c7307a5423802a6da62ec3bc80ce44e1788dd5b", + "rev": "4c584dcedf9aa3394e9730e62693515a0e47674b", "type": "github" }, "original": { @@ -219,31 +531,114 @@ "type": "github" } }, + "poetry2nix": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nix-github-actions": "nix-github-actions", + "nixpkgs": [ + "nixpkgs" + ], + "systems": "systems_2", + "treefmt-nix": "treefmt-nix" + }, + "locked": { + "lastModified": 1743690424, + "narHash": "sha256-cX98bUuKuihOaRp8dNV1Mq7u6/CQZWTPth2IJPATBXc=", + "owner": "nix-community", + "repo": "poetry2nix", + "rev": "ce2369db77f45688172384bbeb962bc6c2ea6f94", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "poetry2nix", + "type": "github" + } + }, + "pre-commit-hooks-nix": { + "inputs": { + "flake-compat": [ + "lanzaboote", + "flake-compat" + ], + "gitignore": "gitignore", + "nixpkgs": [ + "lanzaboote", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1731363552, + "narHash": "sha256-vFta1uHnD29VUY4HJOO/D6p6rxyObnf+InnSMT4jlMU=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "cd1af27aa85026ac759d5d3fccf650abe7e1bbf0", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, "root": { "inputs": { + "asterisk-sounds-de": "asterisk-sounds-de", + "disko": "disko", "flake-utils": "flake-utils", + "gg-chatmix": "gg-chatmix", + "gomod2nix": "gomod2nix", "home-manager": "home-manager", + "impermanence": "impermanence", "krops": "krops", + "lanzaboote": "lanzaboote", + "nix-filter": "nix-filter", "nix-pre-commit-hooks": "nix-pre-commit-hooks", - "nixpkgs": "nixpkgs_2", + "nixos-hardware": "nixos-hardware", + "nixpkgs": "nixpkgs", "nixpkgsMaster": "nixpkgsMaster", "nur": "nur", - "sops-nix": "sops-nix" + "poetry2nix": "poetry2nix", + "sops-nix": "sops-nix", + "vesc-tool": "vesc-tool" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "lanzaboote", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1731897198, + "narHash": "sha256-Ou7vLETSKwmE/HRQz4cImXXJBr/k9gp4J4z/PF8LzTE=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "0be641045af6d8666c11c2c40e45ffc9667839b5", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" } }, "sops-nix": { "inputs": { "nixpkgs": [ "nixpkgs" - ], - "nixpkgs-stable": "nixpkgs-stable_2" + ] }, "locked": { - "lastModified": 1684637723, - "narHash": "sha256-0vAxL7MVMhGbTkAyvzLvleELHjVsaS43p+PR1h9gzNQ=", + "lastModified": 1763417348, + "narHash": "sha256-n5xDOeNN+smocQp3EMIc11IzBlR9wvvTIJZeL0g33Fs=", "owner": "Mic92", "repo": "sops-nix", - "rev": "4ccdfb573f323a108a44c13bb7730e42baf962a9", + "rev": "3f66a7fb9626a9a9c077612ef10a0ce396286c7d", "type": "github" }, "original": { @@ -266,6 +661,87 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "poetry2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1730120726, + "narHash": "sha256-LqHYIxMrl/1p3/kvm2ir925tZ8DkI0KA10djk8wecSk=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "9ef337e492a5555d8e17a51c911ff1f02635be15", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + }, + "treefmt-nix_2": { + "inputs": { + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1744961264, + "narHash": "sha256-aRmUh0AMwcbdjJHnytg1e5h5ECcaWtIFQa6d9gI85AI=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "8d404a69efe76146368885110f29a2ca3700bee6", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + }, + "vesc-tool": { + "inputs": { + "bldcSrc": "bldcSrc", + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgsOld": "nixpkgsOld", + "treefmt-nix": "treefmt-nix_2" + }, + "locked": { + "lastModified": 1762968599, + "narHash": "sha256-j+AZQYOuZ0X33p76LsZu4/NZl1Ccu6kkwPKC5HpIn1Y=", + "owner": "vedderb", + "repo": "vesc_tool", + "rev": "6a75051ce9742d97f14addd5d175ac516effb3c6", + "type": "github" + }, + "original": { + "owner": "vedderb", + "ref": "master", + "repo": "vesc_tool", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index f767bb9..d56e73d 100644 --- a/flake.nix +++ b/flake.nix @@ -1,19 +1,58 @@ { inputs = { - flake-utils.url = "github:numtide/flake-utils"; - nixpkgs.url = "github:nixos/nixpkgs/nixos-22.11"; - nixpkgsMaster.url = "github:NixOS/nixpkgs/master"; + disko.inputs.nixpkgs.follows = "nixpkgs"; + disko.url = "github:nix-community/disko"; - nur.url = "github:nix-community/NUR"; + flake-utils.url = "github:numtide/flake-utils"; + + nix-filter.url = "github:numtide/nix-filter"; + + gg-chatmix = { + url = "github:nilathedragon/gg-chatmix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + gomod2nix = { + url = "github:nix-community/gomod2nix"; + inputs.flake-utils.follows = "flake-utils"; + inputs.nixpkgs.follows = "nixpkgs"; + }; home-manager = { - url = "github:nix-community/home-manager/release-22.05"; + url = "github:nix-community/home-manager/release-25.05"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + impermanence.url = "github:nix-community/impermanence"; + + krops = { + url = "github:Mic92/krops"; + inputs.flake-utils.follows = "flake-utils"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + lanzaboote = { + url = "github:nix-community/lanzaboote/v0.4.2"; inputs.nixpkgs.follows = "nixpkgs"; }; nix-pre-commit-hooks = { - url = "github:cachix/pre-commit-hooks.nix/master"; + url = "github:cachix/git-hooks.nix/master"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + nixos-hardware.url = "github:nixos/nixos-hardware/master"; + + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; + + nixpkgsMaster.url = "github:NixOS/nixpkgs/master"; + + nur.url = "github:nix-community/NUR"; + + poetry2nix = { + url = "github:nix-community/poetry2nix"; inputs.flake-utils.follows = "flake-utils"; + inputs.nixpkgs.follows = "nixpkgs"; }; sops-nix = { @@ -21,22 +60,29 @@ inputs.nixpkgs.follows = "nixpkgs"; }; - krops = { - url = "github:Mic92/krops"; + asterisk-sounds-de = { + url = "git+https://git.jalr.de/jalr/asterisk-sounds-de"; inputs = { flake-utils.follows = "flake-utils"; + nix-filter.follows = "nix-filter"; nixpkgs.follows = "nixpkgs"; }; }; + + vesc-tool = { + url = "github:vedderb/vesc_tool/master"; + inputs.flake-utils.follows = "flake-utils"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; outputs = { self - , nixpkgs , flake-utils - , krops , home-manager - , nur + , krops , nix-pre-commit-hooks + , nixpkgs + , nur , ... }@inputs: flake-utils.lib.eachSystem [ "x86_64-linux" @@ -52,20 +98,26 @@ src = self; hooks = { black.enable = true; + deadnix.enable = true; nixpkgs-fmt.enable = true; shellcheck.enable = true; + statix = { + enable = true; + settings.ignore = [ ".direnv" ]; + }; }; + excludes = [ ".envrc" ]; }; }; - devShell = pkgs.mkShell { - buildInputs = (with pkgs; [ + devShells.default = pkgs.mkShell { + buildInputs = with pkgs; [ black just nixpkgs-fmt shellcheck sops ssh-to-age - ]); + ]; shellHook = '' ${self.checks.${system}.pre-commit-check.shellHook} @@ -73,7 +125,7 @@ }; apps = lib.mapAttrs - (name: program: { type = "app"; program = toString program; }) + (_: program: { type = "app"; program = toString program; }) (flake-utils.lib.flattenTree { deploy = lib.recurseIntoAttrs (lib.mapAttrs (hostname: machine: @@ -97,56 +149,102 @@ command = targetPath: '' nixos-rebuild switch --flake ${targetPath}/config -L --keep-going ''; + force = true; } ) self.nixosConfigurations); - - showKeyFingerprint = pkgs.writeShellScript "show-key-fingerprint" '' - ${pkgs.gnupg}/bin/gpg --with-fingerprint --with-colons --show-key "keys/''${1}.asc" | awk -F: '$1 == "fpr" { print $10; exit }' - ''; + argon2id = + let + python = pkgs.python3.withPackages (pp: with pp; [ + argon2-cffi + ]); + in + pkgs.writeTextFile { + name = "argon2id"; + text = '' + #!${python}/bin/python + import getpass + from argon2 import PasswordHasher + pw = getpass.getpass() + ph = PasswordHasher( + time_cost=5, + memory_cost=2*1024*1024, # in kibibytes + parallelism=4, + ) + print(ph.hash(pw)) + ''; + executable = true; + }; }); - }) // { - overlay = import ./pkgs; + overlays.default = import ./pkgs inputs; nixosConfigurations = nixpkgs.lib.mapAttrs (hostname: { system , extraModules ? [ ] , targetHost ? hostname , nixpkgs ? inputs.nixpkgs - }: nixpkgs.lib.nixosSystem rec { + }: nixpkgs.lib.nixosSystem { inherit system; specialArgs = { inherit self system; }; - modules = [ - (./hosts + "/${hostname}/configuration.nix") + modules = + let + hostDir = ./hosts + "/${hostname}"; + in + [ + (hostDir + "/configuration.nix") - ./modules + ./modules - { - _module.args.inputs = inputs; - } - - # deployment settings - ({ lib, ... }: { - options.deployment = { - targetHost = lib.mkOption { - type = lib.types.str; - readOnly = true; - internal = true; + { + _module.args = { + inherit inputs; + custom-utils = import ./custom-utils { inherit (nixpkgs) lib; }; }; - }; - config.deployment = { - inherit targetHost; - }; - }) - ] ++ [{ - nixpkgs.overlays = [ nur.overlay ]; - }] ++ [ - home-manager.nixosModules.home-manager - ] ++ (with inputs; [ - sops-nix.nixosModules.sops - ]) ++ extraModules; + } + + # deployment settings + ({ lib, ... }: { + options.deployment = { + targetHost = lib.mkOption { + type = lib.types.str; + readOnly = true; + internal = true; + }; + }; + config.deployment = { + inherit targetHost; + }; + }) + + # sops settings + ({ lib, config, pkgs, ... }: + { + sops.defaultSopsFile = hostDir + "/secrets.yaml"; + sops.secrets = + let + secretFile = config.sops.defaultSopsFile; + getSecrets = file: builtins.fromJSON (builtins.readFile (pkgs.runCommandNoCC "secretKeys" { } ''${pkgs.yq-go}/bin/yq -o json '[del .sops | .. | select(tag != "!!seq" and tag != "!!map") | path | join("/")]' ${file} > $out'')); + secretNames = getSecrets secretFile; + secrets = + if builtins.pathExists secretFile then + lib.listToAttrs (builtins.map (name: lib.nameValuePair name { }) secretNames) + else + { }; + in + secrets; + }) + ] ++ [ + { nixpkgs.overlays = [ nur.overlays.default inputs.vesc-tool.overlays.default ]; } + home-manager.nixosModules.home-manager + inputs.asterisk-sounds-de.nixosModules.default + inputs.disko.nixosModules.disko + inputs.impermanence.nixosModules.impermanence + inputs.lanzaboote.nixosModules.lanzaboote + inputs.sops-nix.nixosModules.sops + inputs.gg-chatmix.nixosModule + ] ++ extraModules; }) (import ./hosts inputs); }; diff --git a/home-manager/README.md b/home-manager/README.md deleted file mode 100644 index e245e64..0000000 --- a/home-manager/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Documentation -[Home Manager Manual](https://rycee.gitlab.io/home-manager/) diff --git a/home-manager/modules/alacritty.nix b/home-manager/modules/alacritty.nix deleted file mode 100644 index 17385c2..0000000 --- a/home-manager/modules/alacritty.nix +++ /dev/null @@ -1,174 +0,0 @@ -{ lib, pkgs, nixosConfig, ... }: -let - solarized = import ./solarized.nix; - - #nixosConfig.myConfig.terminalEmulator.command = pkgs.writeShellScriptBin "alacritty-sway-cwd" '' - # this_alacritty_pid="$(swaymsg -t get_tree | ${pkgs.jq} -e 'recurse(.nodes[]?) | select((.focused==true) and (.app_id=="Alacritty")).pid')" - - # if [ "$this_alacritty_pid" ]; then - # child_pid="$(pgrep -P "$this_alacritty_pid")" - # cwd="$(readlink /proc/$child_pid/cwd)" - # fi - # if [ -e "$cwd" ]; then - # exec ${pkgs.alacritty} --working-directory "$cwd" - # fi - - # exec alacritty - #''; - - colorschemes = { - # https://github.com/alacritty/alacritty/wiki/Color-schemes#solarized - solarized-dark = { - # Default colors - primary = { - background = solarized.base03.hex; - foreground = solarized.base0.hex; - }; - - # Cursor colors - cursor = { - text = solarized.base03.hex; - cursor = solarized.base0.hex; - }; - - # Normal colors - normal = { - black = solarized.base02.hex; - red = solarized.red.hex; - green = solarized.green.hex; - yellow = solarized.yellow.hex; - blue = solarized.blue.hex; - magenta = solarized.magenta.hex; - cyan = solarized.cyan.hex; - white = solarized.base2.hex; - }; - - # Bright colors - bright = { - black = solarized.base03.hex; - red = solarized.orange.hex; - green = solarized.base01.hex; - yellow = solarized.base00.hex; - blue = solarized.base0.hex; - magenta = solarized.violet.hex; - cyan = solarized.base1.hex; - white = solarized.base3.hex; - }; - }; - - solarized-light = { - # Default colors - primary = { - background = solarized.base3.hex; - foreground = solarized.base00.hex; - }; - - # Cursor colors - cursor = { - text = solarized.base3.hex; - cursor = solarized.base00.hex; - }; - - # Normal colors - normal = { - black = solarized.base02.hex; - red = solarized.red.hex; - green = solarized.green.hex; - yellow = solarized.yellow.hex; - blue = solarized.blue.hex; - magenta = solarized.magenta.hex; - cyan = solarized.cyan.hex; - white = solarized.base2.hex; - }; - - # Bright colors - bright = { - black = solarized.base03.hex; - red = solarized.orange.hex; - green = solarized.base01.hex; - yellow = solarized.base00.hex; - blue = solarized.base0.hex; - magenta = solarized.violet.hex; - cyan = solarized.base1.hex; - white = solarized.base3.hex; - }; - }; - }; - commonSettings = { - font = { - normal = { - family = "Inconsolata for Powerline"; - style = "Regular"; - }; - size = 12; - }; - - mouse.hide_when_typing = true; - - key_bindings = [ - { - key = "F1"; - mods = "Control"; - action = "DecreaseFontSize"; - } - { - key = "F2"; - mods = "Control"; - action = "IncreaseFontSize"; - } - ]; - - bell = { - duration = 100; - color = "#000000"; - }; - - window.dynamic_title = true; - - scrolling.history = 100000; - - window.opacity = 0.95; - }; - settings = { - dark = commonSettings // { - colors = colorschemes.solarized-dark; - }; - light = commonSettings // { - colors = colorschemes.solarized-light; - }; - }; -in -{ - - programs.alacritty = { - enable = nixosConfig.myConfig.gui.enable; - }; - - # The option `home-manager.users.jalr.xdg.configFile.dark.alacritty/alacritty-dark.yml' does not exist - - /* - xdg.configFile = builtins.mapAttrs (colorScheme: cfg: { - "alacritty/alacritty-${colorScheme}.yml" = lib.replaceStrings [ "\\\\" ] [ "\\" ] (builtins.toJSON cfg); - }) settings; - */ - - xdg.configFile = lib.attrsets.mapAttrs' - (colorScheme: cfg: lib.attrsets.nameValuePair "alacritty/alacritty-${colorScheme}.yml" { - text = lib.replaceStrings [ "\\\\" ] [ "\\" ] (builtins.toJSON cfg); - }) - settings; - - programs.fish.functions = { - ssh = { - description = "ssh wrapper function"; - wraps = "ssh"; - body = '' - if [ "$TERM" = alacritty ] - TERM=xterm-256color command ssh $argv - else - command ssh $argv - end - ''; - }; - }; -} diff --git a/home-manager/modules/cli.nix b/home-manager/modules/cli.nix deleted file mode 100644 index 587d068..0000000 --- a/home-manager/modules/cli.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ nixosConfig, lib, pkgs, ... }: -{ - home.packages = with pkgs; [ - cached-nix-shell - file - htop - jq - lsof - ncdu - ripgrep - ] ++ (if ! nixosConfig.myConfig.workstation.enable then [ ] else [ - direnv - dnsutils - screen - speedtest-cli - usbutils - wget - whois - yt-dlp - ]); -} diff --git a/home-manager/modules/communication/default.nix b/home-manager/modules/communication/default.nix deleted file mode 100644 index 6be079f..0000000 --- a/home-manager/modules/communication/default.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ nixosConfig, ... }: - -{ - imports = [ - ./ferdium.nix - ./mumble.nix - ./qtox.nix - ./telegram-desktop.nix - ]; -} diff --git a/home-manager/modules/communication/ferdium.nix b/home-manager/modules/communication/ferdium.nix deleted file mode 100644 index 0109d07..0000000 --- a/home-manager/modules/communication/ferdium.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ nixosConfig, lib, pkgs, ... }: - -lib.mkIf nixosConfig.myConfig.tradebyte.enable { - home.packages = with pkgs; [ - master.ferdium - ]; -} diff --git a/home-manager/modules/default.nix b/home-manager/modules/default.nix deleted file mode 100644 index 37637d0..0000000 --- a/home-manager/modules/default.nix +++ /dev/null @@ -1,39 +0,0 @@ -{ nixosConfig, ... }: - -{ - imports = [ - ./${nixosConfig.myConfig.terminalEmulator}.nix - ./aws.nix - ./claws-mail.nix - ./cli.nix - ./communication - ./direnv.nix - ./dynamic-colors.nix - ./firefox - ./fish.nix - ./fpv.nix - ./git.nix - ./gnuradio.nix - ./graphics - ./gui.nix - ./jameica.nix - ./kicad.nix - ./mpv.nix - ./mute-indicator.nix - ./neo.nix - ./neovim.nix - ./obs-studio - ./openscad.nix - ./pass.nix - ./pcmanfm.nix - ./python.nix - ./sound - ./sway - ./terraform.nix - ./tmux.nix - ./tor-browser.nix - ./vdirsyncer.nix - ]; - - programs.nix-index.enable = true; -} diff --git a/home-manager/modules/dynamic-colors.nix b/home-manager/modules/dynamic-colors.nix deleted file mode 100644 index d9d05b5..0000000 --- a/home-manager/modules/dynamic-colors.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ nixosConfig, lib, pkgs, ... }: - - -let dynamic-colors = pkgs.writeShellScriptBin "dynamic-colors" /* bash */ '' - case "''$1" in - light|dark) - if [ -e "''$HOME/.config/alacritty/alacritty-''$1.yml" ]; then - ln -sf "''$HOME/.config/alacritty/alacritty-''$1.yml" "$HOME/.config/alacritty/alacritty.yml" - fi - ;; - *) - echo "unknown command ''$1" >&2 - exit 1 - esac -''; -in -{ - home.packages = [ - dynamic-colors - ]; -} diff --git a/home-manager/modules/firefox/default.nix b/home-manager/modules/firefox/default.nix deleted file mode 100644 index b07c1ba..0000000 --- a/home-manager/modules/firefox/default.nix +++ /dev/null @@ -1,102 +0,0 @@ -{ nixosConfig, pkgs, ... }: -{ - programs.firefox = { - enable = nixosConfig.myConfig.gui.enable; - package = pkgs.firefox-esr; - extensions = with pkgs.nur.repos.rycee.firefox-addons; [ - tree-style-tab - ublock-origin - umatrix - violentmonkey - ]; - profiles = { - default = { - settings = { - #"browser.startup.homepage" = "https://nixos.org"; - #"browser.search.region" = "GB"; - #"browser.search.isUS" = false; - #"distribution.searchplugins.defaultLocale" = "en-GB"; - #"general.useragent.locale" = "en-GB"; - #"browser.bookmarks.showMobileBookmarks" = true; - "app.normandy.enabled" = false; - "app.shield.optoutstudies.enabled" = false; - "app.update.auto" = false; - "browser.ctrlTab.sortByRecentlyUsed" = true; - "browser.fixup.alternate.enabled" = false; - "browser.formfill.enable" = false; - "browser.link.open_newwindow.restriction" = 0; - "browser.newtabpage.enabled" = false; - "browser.ping-centre.telemetry" = false; - "browser.safebrowsing.downloads.enabled" = false; - "browser.safebrowsing.downloads.remote.block_dangerous" = false; - "browser.safebrowsing.downloads.remote.block_dangerous_host" = false; - "browser.safebrowsing.downloads.remote.block_potentially_unwanted" = false; - "browser.safebrowsing.downloads.remote.block_uncommon" = false; - "browser.safebrowsing.downloads.remote.enabled" = false; - "browser.safebrowsing.downloads.remote.url" = ""; - "browser.safebrowsing.malware.enabled" = false; - "browser.safebrowsing.phishing.enabled" = false; - "browser.safebrowsing.provider.google.advisoryURL" = ""; - "browser.safebrowsing.provider.google.gethashURL" = ""; - "browser.safebrowsing.provider.google.lists" = ""; - "browser.safebrowsing.provider.google.reportMalwareMistakeURL" = ""; - "browser.safebrowsing.provider.google.reportPhishMistakeURL" = ""; - "browser.safebrowsing.provider.google.reportURL" = ""; - "browser.safebrowsing.provider.google.updateURL" = ""; - "browser.safebrowsing.provider.google4.advisoryURL" = ""; - "browser.safebrowsing.provider.google4.dataSharingURL" = ""; - "browser.safebrowsing.provider.google4.gethashURL" = ""; - "browser.safebrowsing.provider.google4.lists" = ""; - "browser.safebrowsing.provider.google4.reportMalwareMistakeURL" = ""; - "browser.safebrowsing.provider.google4.reportPhishMistakeURL" = ""; - "browser.safebrowsing.provider.google4.reportURL" = ""; - "browser.safebrowsing.provider.google4.updateURL" = ""; - "browser.safebrowsing.provider.mozilla.gethashURL" = ""; - "browser.safebrowsing.provider.mozilla.lists" = ""; - "browser.safebrowsing.provider.mozilla.updateURL" = ""; - "browser.search.suggest.enabled" = false; - "browser.search.widget.inNavBar" = true; - "browser.startup.page" = 0; - "extensions.pocket.enabled" = false; - "extensions.update.enabled" = false; - "identity.fxaccounts.enabled" = false; - "keyword.enabled" = false; - "network.captive-portal-service.enabled" = false; - "network.predictor.enabled" = false; - "privacy.donottrackheader.enabled" = true; - "startup.homepage_welcome_url" = about:blank; - "toolkit.legacyUserProfileCustomizations.stylesheets" = true; - "toolkit.telemetry.archive.enabled" = false; - "toolkit.telemetry.bhrPing.enabled" = false; - "toolkit.telemetry.firstShutdownPing.enabled" = false; - "toolkit.telemetry.newProfilePing.enabled" = false; - "toolkit.telemetry.server" = http://127.0.0.1:4711; - "toolkit.telemetry.server_owner" = ""; - "toolkit.telemetry.shutdownPingSender.enabled" = false; - "toolkit.telemetry.updatePing.enabled" = false; - "urlclassifier.downloadAllowTable" = ""; - "urlclassifier.downloadBlockTable" = ""; - "urlclassifier.malwareTable" = ""; - "urlclassifier.phishTable" = ""; - "datareporting.healthreport.uploadEnabled" = ""; - "app.normandy.api_url" = ""; - "breakpad.reportURL" = ""; - "browser.region.network.url" = ""; - "browser.search.geoSpecificDefaults.url" = ""; - "browser.shell.checkDefaultBrowser" = false; - - "privacy.userContext.enabled" = true; - "privacy.userContext.ui.enabled" = true; - "network.dnsCacheExpiration" = 0; - - # disable disk cache to reduce ssd writes - "browser.cache.disk.enable" = false; - "browser.cache.memory.enable" = true; - "browser.cache.memory.capacity" = -1; - }; - userChrome = builtins.readFile ./userChrome.css; - - }; - }; - }; -} diff --git a/home-manager/modules/fish.nix b/home-manager/modules/fish.nix deleted file mode 100644 index f14154f..0000000 --- a/home-manager/modules/fish.nix +++ /dev/null @@ -1,201 +0,0 @@ -{ config, pkgs, ... }: -{ - home.packages = with pkgs; [ - fzf - ]; - programs.fish = { - enable = true; - plugins = [ - { - name = "theme-agnoster"; - src = pkgs.fetchFromGitHub { - owner = "oh-my-fish"; - repo = "theme-agnoster"; - rev = "c142e802983bd1b34b4d91efac2126fc5913126d"; - sha256 = "0PLx626BWoBp/L6wgkB4o+53q8PymiEE/rTu2mfzHhg="; - fetchSubmodules = true; - }; - } - { - name = "fzf"; - src = pkgs.fetchFromGitHub { - owner = "jethrokuan"; - repo = "fzf"; - rev = "479fa67d7439b23095e01b64987ae79a91a4e283"; - sha256 = "0k6l21j192hrhy95092dm8029p52aakvzis7jiw48wnbckyidi6v"; - fetchSubmodules = true; - }; - } - ]; - shellAliases = { - ls = "ls --color=auto"; - crontab = "crontab -i"; - }; - - shellAbbrs = { - lessr = "less -R"; - jqc = "jq -C"; - }; - - #interactiveShellInit = '' - # echo "programs.fish.interactiveShellInit" - #''; - shellInit = '' - # key bindings - bind \cr '__fzf_reverse_isearch' - - # PATH - set -U fish_user_paths $HOME/.local/bin $HOME/.local/bin/pio - - # pass - #set -x PASSWORD_STORE_ENABLE_EXTENSIONS true - set -x AWS_VAULT_BACKEND pass - set -x AWS_VAULT_PASS_PREFIX aws - complete -c pw --no-files -a '(__fish_pass_print_entries)' - - # colors - set -x GCC_COLORS 'error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01' - - abbr --add v vim - - #alias cal='ncal -b -M' - alias myip='dig +short myip.opendns.com @resolver1.opendns.com' - - function hm -d 'merge history and delete failed commands' - history --merge - - if test -z "$fish_private_mode" && test -e "$__fish_user_data_dir/successful_commands" && test -e "$__fish_user_data_dir/failed_commands" - while read line; - if ! grep -qFx $line "$__fish_user_data_dir/successful_commands" - set hist_command (echo $line | base64 -d) - echo "deleting command: $hist_command" - echo "." - history delete --exact --case-sensitive $hist_command - end - end < "$__fish_user_data_dir/failed_commands" - echo -n > "$__fish_user_data_dir/successful_commands" - echo -n > "$__fish_user_data_dir/failed_commands" - end - end - hm - - # fancy tools - if which exa > /dev/null 2>&1 - alias l=exa - alias ll='exa -l --time-style=long-iso --git' - alias la='exa -la --time-style=long-iso --git' - alias tree='exa --tree' - alias llt='exa -s modified -l' - else - alias l=ls - alias ll='ls -l' - alias la='ls -la' - alias llt='ls -trl' - end - - if which rg > /dev/null 2>&1 - alias g=rg - complete -c g -w rg - else if which ag > /dev/null 2>&1 - alias g=ag - complete -c g -w ag - else - alias g='grep --color=auto' - complete -c g -w grep - end - - function jqless -d 'jq -C [args] | less -R' - jq -C $argv | less -R - end - - # NixOS direnv - if which direnv > /dev/null - eval (direnv hook fish) - end - - function __cut_commandline -d 'cut commandline and paste it later' - set -g commandline_buffer (commandline) - commandline "" - end - - - - function __postexec --on-event fish_postexec - if test $status -ne 0 - if test -z "$hist_cmd" - if test -z "$fish_private_mode" - echo $argv[1] | base64 >> "$__fish_user_data_dir/failed_commands" - end - end - else - if test -z "$fish_private_mode" - echo $argv[1] | base64 >> "$__fish_user_data_dir/successful_commands" - end - commandline $commandline_buffer - set -e commandline_buffer - end - end - - function dirh-nocolor --description "Print the current directory history (the prev and next lists)" - set -l options h/help - argparse -n dirh --max-args=0 $options -- $argv - or return - - if set -q _flag_help - __fish_print_help dirh - return 0 - end - - set -l dirc (count $dirprev) - if test $dirc -gt 0 - set -l dirprev_rev $dirprev[-1..1] - # This can't be (seq $dirc -1 1) because of BSD. - set -l dirnum (seq 1 $dirc) - for i in $dirnum[-1..1] - printf '%s\n' $dirprev_rev[$i] - end - end - - echo $PWD - - set -l dirc (count $dirnext) - if test $dirc -gt 0 - set -l dirnext_rev $dirnext[-1..1] - for i in (seq $dirc) - printf '%s\n' $dirnext_rev[$i] - end - end - end - - function dirh-fzf -d 'directory history fuzzy finder' - builtin cd (dirh-nocolor | uniq | fzf) - end - - bind \ed 'dirh-fzf' - ''; - }; - - xdg.configFile."fish/completions/mycli.fish".text = '' - complete -e -c mycli - complete -c mycli -f -s h -l host -d "Host address of the database." - complete -c mycli -f -s P -l port -d "Port number to use for connection." - complete -c mycli -f -s u -l user -d "User name to connect to the database." - complete -c mycli -f -s S -l socket -d "The socket file to use for connection." - complete -c mycli -f -s p -l pass \ - -l password -d "Password to connect to the database." - complete -c mycli -f -s V -l version -d "Output mycli's version." - complete -c mycli -f -s v -l verbose -d "Verbose output." - complete -c mycli -f -s d -l dsn -d "Use DSN configured into the [alias_dsn] section of myclirc file." - complete -c mycli -f -l list-dsn -d "list of DSN configured into the [alias_dsn] section of myclirc file." - - complete -c mycli -f -s t -l table -d "Display batch output in table format." - complete -c mycli -f -l csv -d "Display batch output in CSV format." - complete -c mycli -f -l warn \ - -l no-warn -d "Warn before running a destructive query." - complete -c mycli -f -s e -l execute -d "Execute command and quit." - - - complete -c mycli -f -s h -l host -r -a '(__fish_print_hostnames)' - complete -c mycli -f -s d -l dsn -r -a '(mycli --list-dsn)' - ''; -} diff --git a/home-manager/modules/gui.nix b/home-manager/modules/gui.nix deleted file mode 100644 index e6b3639..0000000 --- a/home-manager/modules/gui.nix +++ /dev/null @@ -1,14 +0,0 @@ -{ nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.gui.enable { - home.packages = with pkgs; [ - gcr # required for pinentry-gnome - geeqie - mpv - networkmanagerapplet - networkmanagerapplet - pinentry-gnome - streamlink - vlc - xdg_utils - ]; -} diff --git a/home-manager/modules/jameica.nix b/home-manager/modules/jameica.nix deleted file mode 100644 index d8fd7d3..0000000 --- a/home-manager/modules/jameica.nix +++ /dev/null @@ -1,6 +0,0 @@ -{ nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.gui.enable { - home.packages = with pkgs; [ - jameica - ]; -} diff --git a/home-manager/modules/neovim.nix b/home-manager/modules/neovim.nix deleted file mode 100644 index ac3d2d7..0000000 --- a/home-manager/modules/neovim.nix +++ /dev/null @@ -1,168 +0,0 @@ -{ lib, nixosConfig, config, pkgs, ... }: -{ - home.sessionVariables = { - EDITOR = "nvim"; - }; - programs.neovim = { - enable = true; - vimAlias = true; - extraConfig = '' - " use space as leader - let mapleader = " " - - colorscheme NeoSolarized - - """"""""""""""""" - " Swap and undo " - set noswapfile - set nobackup - if has('persistent_undo') - " yay persistent undo - :silent !mkdir -p ~/.local/vim-undo - set undofile - set undodir=~/.local/vim-undo - endif - - cabbr %% expand('%:p:h') - - set listchars=trail:·,precedes:«,extends:»,eol:↲,tab:▸\ - nmap c :set list! - - set smartcase - set hlsearch - nnoremap :nohlsearch:set nolist - - " highlight whitespace - highlight ExtraWhitespace ctermbg=red guibg=red - highlight WrongIndent ctermbg=2 guibg=blue - match ExtraWhitespace /\s\+$/ - augroup highlight_extra_whitespace - autocmd BufWinEnter * match ExtraWhitespace /\s\+$/ - autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@ gd lua vim.lsp.buf.definition() - nnoremap gi lua vim.lsp.buf.implementation() - nnoremap gr lua vim.lsp.buf.references() - nnoremap gD lua vim.lsp.buf.declaration() - nnoremap ge lua vim.lsp.diagnostic.set_loclist() - nnoremap K lua vim.lsp.buf.hover() - nnoremap f lua vim.lsp.buf.formatting() - nnoremap rn lua vim.lsp.buf.rename() - - nnoremap a lua vim.lsp.buf.code_action() - xmap a lua vim.lsp.buf.range_code_action() - - lua require('init') - ''; - - # nix-env -f '' -qaP -A vimPlugins - plugins = with pkgs.vimPlugins; [ - #Valloric/MatchTagAlways - #frankier/neovim-colors-solarized-truecolor-only - #nvie/vim-rst-tables - NeoSolarized - deoplete-nvim - editorconfig-vim - nvim-lspconfig - vim-gitgutter - vim-indent-guides - vim-nix - vim-puppet - vim-terraform - ]; - }; - - xdg.configFile."nvim/lua/init.lua".text = builtins.concatStringsSep "\n" ( - [ - '' - -- init.lua - -- this configuration applies to servers and workstations - '' - ] ++ lib.optional nixosConfig.myConfig.workstation.enable '' - -- this configuration applies to workstations only - -- https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md - local lsp = require('lspconfig') - - -- C and C++ - lsp.ccls.setup { - on_attach = on_attach, - cmd = { "${pkgs.ccls}/bin/ccls" }, - } - - -- Nix - lsp.rnix.setup { - on_attach = on_attach, - cmd = { "${pkgs.rnix-lsp}/bin/rnix-lsp" }, - } - - -- Python - lsp.pylsp.setup { - on_attach = on_attach, - cmd = { "${pkgs.python310Packages.python-lsp-server}/bin/pylsp" }, - settings = { - -- https://github.com/python-lsp/python-lsp-server/blob/develop/CONFIGURATION.md - pylsp = { - plugins = { - flake8 = { - enabled = true; - executable = "${pkgs.python310Packages.flake8}/bin/flake8"; - }; - jedi_completion = { enabled = true; }; - jedi_definition = { enabled = true; }; - jedi_hover = { enabled = true; }; - jedi_references = { enabled = true; }; - jedi_signature_help = { enabled = true; }; - jedi_symbols = { enabled = true; }; - mccabe = { enabled = true; }; - preload = { enabled = true; }; - pycodestyle = { enabled = true; }; - pyflakes = { enabled = true; }; - rope_completion = { enabled = true; }; - yapf = { enabled = true; }; - }; - }; - }; - } - - -- Ruby - lsp.solargraph.setup { - on_attach = on_attach, - cmd = { "${pkgs.solargraph}/bin/solargraph", "stdio" }, - } - - -- Rust - lsp.rust_analyzer.setup { - on_attach = on_attach, - cmd = { "${pkgs.rust-analyzer}/bin/rust-analyzer" }, - } - - -- Bash - lsp.bashls.setup { - on_attach = on_attach, - cmd = { "${pkgs.nodePackages.bash-language-server}/bin/bash-language-server", "start" }, - } - - -- Terraform - lsp.terraform_lsp.setup { - on_attach = on_attach, - cmd = { "${pkgs.terraform-lsp}/bin/terraform-lsp", "serve" }, - } - - -- YAML - lsp.yamlls.setup { - on_attach = on_attach, - cmd = { "${pkgs.nodePackages.yaml-language-server}/bin/yaml-language-server", "--stdio" }, - } - '' - ); -} diff --git a/home-manager/modules/pass.nix b/home-manager/modules/pass.nix deleted file mode 100644 index 67aee6f..0000000 --- a/home-manager/modules/pass.nix +++ /dev/null @@ -1,29 +0,0 @@ -{ nixosConfig, config, pkgs, ... }: - -let - pw = pkgs.writeScriptBin "pw" '' - p="$(${pkgs.pass}/bin/pass show "$1")" - - copy_line() { - echo -n "$p" | ${pkgs.gnused}/bin/sed -n "$1"p | ${pkgs.wl-clipboard}/bin/wl-copy -o -f - } - - echo "username" - copy_line 2 - echo "password" - copy_line 1 - ''; -in -{ - home.packages = [ - pw - ] ++ - ( - if nixosConfig.myConfig.gui.enable - then with pkgs; [ - qtpass - pass-wayland - ] - else [ ] - ); -} diff --git a/home-manager/modules/pcmanfm.nix b/home-manager/modules/pcmanfm.nix deleted file mode 100644 index 0a658c3..0000000 --- a/home-manager/modules/pcmanfm.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.gui.enable { - home.packages = with pkgs; [ - pcmanfm - ]; -} - diff --git a/home-manager/modules/python.nix b/home-manager/modules/python.nix deleted file mode 100644 index b1860db..0000000 --- a/home-manager/modules/python.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.workstation.enable { - home.packages = with pkgs; [ - python310 - python310Packages.virtualenv - python310Packages.ipython - ]; -} diff --git a/home-manager/modules/solarized.nix b/home-manager/modules/solarized.nix deleted file mode 100644 index c814420..0000000 --- a/home-manager/modules/solarized.nix +++ /dev/null @@ -1,23 +0,0 @@ -builtins.mapAttrs - (name: hex: { - inherit hex; - rgb = builtins.concatStringsSep "," (map (f: toString (builtins.fromTOML "i = 0x${f hex}").i) (map (pos: builtins.substring pos 2) [ 1 3 5 ])); - }) -{ - base00 = "#657b83"; - base01 = "#586e75"; - base02 = "#073642"; - base03 = "#002b36"; - base0 = "#839496"; - base1 = "#93a1a1"; - base2 = "#eee8d5"; - base3 = "#fdf6e3"; - blue = "#268bd2"; - cyan = "#2aa198"; - green = "#859900"; - magenta = "#d33682"; - orange = "#cb4b16"; - red = "#dc322f"; - violet = "#6c71c4"; - yellow = "#b58900"; -} diff --git a/home-manager/modules/sound/easyeffects.nix b/home-manager/modules/sound/easyeffects.nix deleted file mode 100644 index 3a9d9ed..0000000 --- a/home-manager/modules/sound/easyeffects.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ nixosConfig, lib, pkgs, ... }: - -lib.mkIf nixosConfig.myConfig.gui.enable { - home.packages = with pkgs; [ - easyeffects - ]; -} diff --git a/home-manager/modules/sway/waybar.nix b/home-manager/modules/sway/waybar.nix deleted file mode 100644 index 4f7056b..0000000 --- a/home-manager/modules/sway/waybar.nix +++ /dev/null @@ -1,463 +0,0 @@ -{ config, lib, nixosConfig, pkgs, ... }: -let - watchUserUnitState = unit: started: stopped: pkgs.writeShellScript "watch-user-unit-${unit}-state" '' - ${pkgs.systemd}/bin/journalctl --user -u ${unit} -t systemd -o cat -f \ - | ${pkgs.gnugrep}/bin/grep --line-buffered -Eo '^(Started|Stopped)' \ - | ${pkgs.jq}/bin/jq --unbuffered -Rc 'if . == "Started" then ${builtins.toJSON started} else ${builtins.toJSON stopped} end' - ''; - - toggleUserUnitState = unit: pkgs.writeShellScript "toggle-user-unit-${unit}-state" '' - if ${pkgs.systemd}/bin/systemctl --user show ${unit} | ${pkgs.gnugrep}/bin/grep -q ActiveState=active; then - ${pkgs.systemd}/bin/systemctl --user stop ${unit} - else - ${pkgs.systemd}/bin/systemctl --user start ${unit} - fi - ''; - - # nerd fonts are abusing arabic which breaks latin text - # context: https://github.com/Alexays/Waybar/issues/628 - lrm = "‎"; - - # for fine-grained control over spacing - thinsp = " "; - - solarized = import ../solarized.nix; -in -{ - # home-manager’s waybar module performs additional checks that are overly strict - xdg.configFile."waybar/config".text = lib.generators.toJSON { } { - layer = "top"; - position = "top"; - height = 24; - - modules-center = [ ]; - modules-left = [ - "sway/workspaces" - "sway/mode" - ]; - modules-right = [ - "tray" - "custom/screencast" - "custom/redshift" - "idle_inhibitor" - "backlight" - "mpd" - "pulseaudio" - "network" - "custom/vpn" - "memory" - "cpu" - "temperature" - "battery" - "clock" - "custom/calendar" - ]; - - "sway/workspaces" = { - disable-scroll = true; - }; - "sway/mode" = { - format = "{}"; - }; - - tray = { - spacing = 5; - }; - "custom/redshift" = { - exec = watchUserUnitState - "gammastep" - { class = "active"; } - { class = "inactive"; }; - on-click = toggleUserUnitState "gammastep"; - return-type = "json"; - format = ""; - tooltip = false; - }; - idle_inhibitor = { - format = "{icon}"; - format-icons = { - activated = " "; - deactivated = " "; - }; - }; - "custom/screencast" = { - exec = pkgs.writeScript "screencast-monitor" /* python */ '' - #!${pkgs.python3}/bin/python3 - import subprocess - import sys - - active_outputs = 0 - - with subprocess.Popen( - ["${pkgs.coreutils}/bin/stdbuf", "-o0", "${nixosConfig.services.pipewire.package}/bin/pw-link", "-m", "-o", "xdg-desktop-portal-wlr"], - stdout=subprocess.PIPE, - text=True, - ) as proc: - for line in proc.stdout: - action = line.split(" ")[0] - if action == "=" or action == "+": - active_outputs += 1 - elif action == "-": - active_outputs -= 1 - else: - print(f"Invalid action {action} (in line {line})", file=sys.stderr) - - if active_outputs > 0: - print("${lrm} ") - else: - print() - - sys.stdout.flush() - ''; - format = "{}"; - tooltip = false; - }; - backlight = { - format = "{percent}% {icon}"; - format-icons = [ " " " " " " " " " " " " " " ]; - on-scroll-up = "${pkgs.brightnessctl}/bin/brightnessctl -q set +5%"; - on-scroll-down = "${pkgs.brightnessctl}/bin/brightnessctl -q set 5%-"; - }; - mpd = { - server = config.services.mpd.network.listenAddress; - format = "{stateIcon} {consumeIcon}{randomIcon}{repeatIcon}{singleIcon}{artist} – {title} ({elapsedTime:%M:%S}/{totalTime:%M:%S}) "; - format-disconnected = "Disconnected "; - format-stopped = "{consumeIcon}{randomIcon}{repeatIcon}{singleIcon}Stopped "; - unknown-tag = "N/A"; - interval = 2; - tooltip-format = "MPD (connected)"; - tooltip-format-disconnected = "MPD (disconnected)"; - on-scroll-up = "${pkgs.mpc_cli}/bin/mpc -q -h ${config.services.mpd.network.listenAddress} volume +2"; - on-scroll-down = "${pkgs.mpc_cli}/bin/mpc -q -h ${config.services.mpd.network.listenAddress} volume -2"; - title-len = 48; - artist-len = 24; - consume-icons = { - on = " "; - }; - random-icons = { - off = "劣 "; - on = "列 "; - }; - repeat-icons = { - on = "凌 "; - }; - single-icons = { - on = "綾 "; - }; - state-icons = { - paused = ""; - playing = "契"; - }; - }; - pulseaudio = { - format = "{volume}% {icon} {format_source}"; - format-bluetooth = "{volume}% {icon} {format_source}"; - format-bluetooth-muted = "${lrm}ﱝ${lrm} {icon} {format_source}"; - format-muted = "${lrm}ﱝ${lrm} {format_source}"; - format-source = "{volume}% ${thinsp}"; - format-source-muted = "${thinsp}"; - format-icons = { - car = " "; - default = [ "奄" "奔" "墳" ]; - hands-free = " "; - headphone = " "; - headset = " "; - phone = " "; - portable = " "; - }; - on-click-right = "${pkgs.pavucontrol}/bin/pavucontrol"; - }; - network = { - format-wifi = "{essid} ({signalStrength}%) 直 "; - format-ethernet = "{ipaddr}/{cidr}  "; - format-linked = "{ifname} (No IP)  "; - format-disconnected = "Disconnected ⚠ "; - format-alt = "{ifname}: {ipaddr}/{cidr}"; - tooltip = false; - on-click-right = "${config.programs.alacritty.package}/bin/alacritty -e ${pkgs.networkmanager}/bin/nmtui"; - }; - "custom/vpn" = { - interval = 10; - exec = pkgs.writeShellScript "vpn-state" '' - ${pkgs.iproute}/bin/ip -j link \ - | ${pkgs.jq}/bin/jq --unbuffered --compact-output ' - [[.[].ifname | select(. | startswith("mullvad"))][] | split("-")[1] + " ${thinsp}"] as $conns - | { text: ($conns[0] // ""), class: (if $conns | length > 0 then "connected" else "disconnected" end) }' - ''; - return-type = "json"; - format = "{}"; - tooltip = false; - }; - memory = { - interval = 2; - format = "{:2}%  "; - }; - cpu = { - interval = 2; - format = "{usage:2}% ﬙ "; - tooltip = false; - }; - temperature = { - critical-threshold = 80; - format = "{temperatureC}°C {icon}"; - format-icons = [ "" "" "" "" "" ]; - } // (lib.optionalAttrs (nixosConfig.networking.hostName == "mayushii") { - hwmon-path = "/sys/class/hwmon/hwmon3/temp1_input"; - }); - battery = { - interval = 5; - format = "{capacity}% {icon}"; - format-charging = "{capacity}% "; - format-plugged = "{capacity}% ${lrm}ﮣ"; - format-alt = "{time} {icon}"; - format-icons = [ "" "" "" "" "" "" "" "" "" "" "" ]; - states = { - critical = 15; - good = 95; - warning = 30; - }; - }; - clock = { - format = "{:%H:%M %Z}"; - format-alt = "{:%Y-%m-%d (%a)}"; - tooltip-format = "{:%Y %B}\n{calendar}"; - }; - "custom/calendar" = { - interval = 300; - exec = pkgs.writeScript "calendar" /* python */ '' - #!${pkgs.python3}/bin/python3 - import json - import subprocess - - - def khal(args): - completed = subprocess.run(["${pkgs.khal}/bin/khal"] + args, capture_output=True) - assert completed.returncode == 0 - return completed.stdout.decode("utf-8") - - - events_today = khal(["list", "today", "today", "-df", "", "-f", "{title}"]).rstrip().split("\n") - events_2d = khal(["list", "today", "tomorrow", "-df", "{name}, {date}"]).rstrip() - - if len(events_today) == 1 and events_today[0] == "No events": - events_today = [] - - if len(events_today) == 0: - text = " " - else: - text = f"{len(events_today)}  " - - print( - json.dumps( - { - "class": "active" if len(events_today) > 0 else "", - "text": text, - "tooltip": events_2d, - } - ) - ) - ''; - return-type = "json"; - format = "{}"; - }; - }; - xdg.configFile."waybar/style.css".text = '' - * { - border-radius: 0; - border: none; - font-family: "Iosevka Nerd Font"; - font-size: 14px; - min-height: 0; - transition-property: none; - } - - window#waybar { - background-color: ${solarized.base03.hex}; - color: ${solarized.base0.hex}; - } - - #workspaces button { - padding: 0 5px; - background-color: ${solarized.base03.hex}; - color: inherit; - border-bottom: 2px solid transparent; - } - - #workspaces button:hover { - background: ${solarized.base02.hex}; - box-shadow: inherit; - text-shadow: inherit; - } - - #workspaces button.focused { - border-bottom: 2px solid ${solarized.green.hex}; - } - - #workspaces button.urgent { - background-color: ${solarized.red.hex}; - } - - #mode { - background-color: ${solarized.base02.hex}; - font-style: italic; - } - - /* all modules on the right */ - #waybar > box > box:nth-child(3) > widget > label { - padding: 0 10px; - } - - #battery.charging { - color: ${solarized.base02.hex}; - background-color: ${solarized.green.hex}; - } - - @keyframes blink { - to { - background-color: ${solarized.base3.hex}; - color: ${solarized.base00.hex}; - } - } - - #battery.critical:not(.charging), - #temperature.critical { - background-color: ${solarized.red.hex}; - animation-name: blink; - animation-duration: 0.5s; - /* FIXME use nearest neighbor interpolation if possible */ - animation-timing-function: cubic-bezier(1, 0, 0, 1); - animation-iteration-count: infinite; - animation-direction: alternate; - } - - #cpu { - background-color: ${solarized.cyan.hex}; - color: ${solarized.base02.hex} - } - - #memory { - background-color: ${solarized.yellow.hex}; - color: ${solarized.base02.hex} - } - - #backlight { - background-color: ${solarized.base3.hex}; - color: ${solarized.base00.hex}; - } - - #network { - background-color: ${solarized.violet.hex}; - color: ${solarized.base02.hex} - } - - #custom-vpn { - background-color: ${solarized.blue.hex}; - color: ${solarized.base02.hex} - } - - #network.disconnected { - background-color: ${solarized.red.hex}; - } - - #pulseaudio { - background-color: ${solarized.base3.hex}; - color: ${solarized.base00.hex}; - } - - #pulseaudio.muted { - background-color: ${solarized.base03.hex}; - color: ${solarized.base0.hex}; - } - - #temperature { - background-color: ${solarized.magenta.hex}; - color: ${solarized.base02.hex}; - } - - #idle_inhibitor.activated { - background-color: ${solarized.base3.hex}; - color: ${solarized.base03.hex}; - } - - #mpd { - background-color: ${solarized.green.hex}; - color: ${solarized.base02.hex}; - } - - #mpd.disconnected { - background-color: ${solarized.red.hex}; - } - - #mpd.stopped { - background-color: ${solarized.orange.hex}; - } - - #mpd.paused { - background-color: ${solarized.yellow.hex}; - } - - #custom-redshift { - color: ${solarized.base02.hex}; - } - - #custom-redshift.active { - background-color: ${solarized.red.hex}; - } - - #custom-redshift.inactive { - background-color: ${solarized.blue.hex}; - } - - #tray { - padding: 0 5px; - } - - #custom-notification_inhibitor.active { - background-color: ${solarized.base3.hex}; - color: ${solarized.base03.hex}; - } - - #custom-screencast { - background-color: ${solarized.red.hex}; - color: ${solarized.base03.hex}; - animation-name: blink; - animation-duration: 0.5s; - animation-timing-function: cubic-bezier(1, 0, 0, 1); - animation-iteration-count: infinite; - animation-direction: alternate; - } - - #custom-calendar.active { - background-color: ${solarized.base3.hex}; - color: ${solarized.base00.hex}; - } - ''; - - systemd.user.services.waybar = { - Unit = { - Description = "Highly customizable Wayland bar for Sway and Wlroots based compositors."; - Documentation = "https://github.com/Alexays/Waybar/wiki/"; - PartOf = [ "sway-session.target" ]; - }; - - Install.WantedBy = [ "sway-session.target" ]; - - Service = { - # ensure sway is already started, otherwise workspaces will not work - ExecStartPre = "${config.wayland.windowManager.sway.package}/bin/swaymsg"; - ExecStart = "${pkgs.waybar}/bin/waybar"; - ExecReload = "${pkgs.utillinux}/bin/kill -SIGUSR2 $MAINPID"; - Restart = "on-failure"; - RestartSec = "1s"; - }; - }; - - # TODO: remove when https://github.com/nix-community/home-manager/issues/2064 - # is resolved - systemd.user.targets.tray = { - Unit = { - Description = "Home Manager System Tray"; - Requires = [ "graphical-session-pre.target" ]; - }; - }; -} diff --git a/home-manager/modules/sway/wofi.nix b/home-manager/modules/sway/wofi.nix deleted file mode 100644 index 2db0add..0000000 --- a/home-manager/modules/sway/wofi.nix +++ /dev/null @@ -1,64 +0,0 @@ -{ nixosConfig, config, lib, pkgs, ... }: - -let - solarized = import ../solarized.nix; -in -{ - xdg.configFile."wofi/style.css".text = - let - # adding it to the header doesn’t work since the defaults overwrite it - commonConfig = /* ini */ '' - background=${lib.substring 1 6 solarized.base3} - border-bottom=${lib.substring 1 6 solarized.base2} - border=${lib.substring 1 6 solarized.base2} - button-background=${lib.substring 1 6 solarized.base3} - button-text=${lib.substring 1 6 solarized.base00} - ''; - in - /* css */ '' - window { - margin: 0px; - border: 3px solid ${solarized.base02.hex}; - border-radius: 8px; - background-color: rgba(${solarized.base03.rgb},0.8); - } - - #input { - margin: 5px; - border: none; - color: ${solarized.base0.hex}; - background-color: rgba(${solarized.base02.rgb},0.8); - } - - #inner-box { - margin: 5px; - border: none; - background: none; - } - - #outer-box { - margin: 5px; - border: none; - background: none; - } - - #scroll { - margin: 0px; - border: none; - } - - #text { - margin: 5px; - border: none; - color: ${solarized.base0.hex}; - } - - #entry:selected { - background-color: rgba(${solarized.base02.rgb},0.8); - } - - #entry:selected #text{ - color: ${solarized.green.hex}; - } - ''; -} diff --git a/home-manager/modules/terraform.nix b/home-manager/modules/terraform.nix deleted file mode 100644 index 32bcb73..0000000 --- a/home-manager/modules/terraform.nix +++ /dev/null @@ -1,11 +0,0 @@ -{ config, pkgs, ... }: - -{ - home.packages = with pkgs; [ - terraform - ]; - - home.sessionVariables = { - TF_PLUGIN_CACHE_DIR = "$HOME/.local/share/terraform/plugins"; - }; -} diff --git a/home-manager/modules/tor-browser.nix b/home-manager/modules/tor-browser.nix deleted file mode 100644 index c362727..0000000 --- a/home-manager/modules/tor-browser.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.gui.enable { - home.packages = with pkgs; [ - ( - tor-browser-bundle-bin.override { - useHardenedMalloc = false; - } - ) - ]; -} diff --git a/home-manager/users/default.nix b/home-manager/users/default.nix deleted file mode 100644 index 2ea4a37..0000000 --- a/home-manager/users/default.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ lib, ... }: - -{ - options.myConfig = { - git = { - user = { - name = lib.mkOption { - type = lib.types.str; - description = "name to use for git commits"; - }; - email = lib.mkOption { - type = lib.types.str; - description = "email to use for git commits"; - }; - }; - signByDefault = lib.mkEnableOption "GPG sign commits per default"; - }; - gpg.defaultKey = lib.mkOption { - type = lib.types.str; - description = "default gpg key id"; - }; - terminalEmulator = lib.mkOption { - type = lib.types.str; - description = "default Terminal emulator name"; - default = "alacritty"; - }; - }; -} diff --git a/home-manager/users/jal.nix b/home-manager/users/jal.nix deleted file mode 100644 index 5c3bc01..0000000 --- a/home-manager/users/jal.nix +++ /dev/null @@ -1,224 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - userName = "jal"; - vpn_routes = [ - "10.18.0.0/16" # OEE VPC - "10.64.64.0/20" # CPS - "10.158.128.0/23" # approval - "10.158.224.0/20" # core production - "10.158.240.0/20" # core development - #"10.96.0.0/24" # CCS infrastructure - #"10.96.8.0/24" # Boomi - #"10.96.10.0/24" # Boomi (new) - "10.96.0.0/16" - "10.170.254.30/32" # core DNS resolver - ]; - vpnc-script = pkgs.writeShellScript "vpnc-script-tb" '' - cisco_split_inc="$CISCO_SPLIT_INC" - export CISCO_SPLIT_INC=0 - unset INTERNAL_IP4_DNS - - route_in_whitelist() { - for route in ${builtins.toString vpn_routes}; do - [ "$1" = "$route" ] && return 0 - done - return 1 - } - - routes() { - for i in $(seq 0 $((cisco_split_inc-1))); do - addr_var="CISCO_SPLIT_INC_''${i}_ADDR" - mask_var="CISCO_SPLIT_INC_''${i}_MASK" - masklen_var="CISCO_SPLIT_INC_''${i}_MASKLEN" - addr="''${!addr_var}" - mask="''${!mask_var}" - masklen="''${!masklen_var}" - if route_in_whitelist "$addr/$masklen"; then - case "$1" in - add) - if [ -n "$NETGW" ]; then - ip route add "$addr/$masklen" metric 100 dev "$TUNDEV" via "$NETGW" - else - ip route add "$addr/$masklen" metric 100 dev "$TUNDEV" - fi - ;; - remove) - ip route del "$addr/$masklen" dev "$TUNDEV" - ;; - esac - echo "allowing route '$addr/$masklen'" - else - echo "ignoring route '$addr/$masklen'" - fi - done - } - - case "$reason" in - pre-init|reconnect|attempt-reconnect) - "${pkgs.vpnc-scripts}/bin/vpnc-script" "$@" - ;; - connect) - "${pkgs.vpnc-scripts}/bin/vpnc-script" "$@" - routes add - ;; - disconnect) - routes remove - "${pkgs.vpnc-scripts}/bin/vpnc-script" "$@" - ;; - *) - echo "reason '$reason' is not implemented" >&2 - exit 1 - ;; - esac - ''; - tradebyte-vpn = pkgs.writeShellScriptBin "tradebyte-vpn" '' - [ $UID -ne 0 ] && exec sudo -- "$0" "$@" - /run/wrappers/bin/sudo -u "$SUDO_USER" ${pkgs.pass}/bin/pass show zalando | openconnect \ - --protocol=pulse \ - -u jlechner \ - --passwd-on-stdin \ - -i pulse \ - --pfs \ - --disable-ipv6 \ - --script=${vpnc-script} \ - https://remote.tradebyte.org | grep -v '^> ' - ''; - aws_defaults = { - sso = { - start_url = "https://d-9967250383.awsapps.com/start"; - region = "eu-central-1"; - role_name = "AdministratorAccess"; - }; - region = "eu-central-1"; - }; -in -{ - imports = [ - ./default.nix - ]; - - myConfig = { - git = { - user = { - name = "Jakob Lechner"; - email = "jal@tradebyte.biz"; - }; - signByDefault = false; - }; - gpg.defaultKey = "FE170812543DF81393EA56BA5042B8317A10617E"; - aws = { - enable = true; - accounts = { - ops_testing = { - sso_account_id = 134848648016; - sso_start_url = aws_defaults.sso.start_url; - sso_region = aws_defaults.sso.region; - sso_role_name = aws_defaults.sso.role_name; - region = aws_defaults.region; - }; - core-production = { - sso_account_id = 455520445575; - sso_start_url = aws_defaults.sso.start_url; - sso_region = aws_defaults.sso.region; - sso_role_name = aws_defaults.sso.role_name; - region = aws_defaults.region; - }; - tbmeta-production = { - sso_account_id = 696695470425; - sso_start_url = aws_defaults.sso.start_url; - sso_region = aws_defaults.sso.region; - sso_role_name = aws_defaults.sso.role_name; - region = aws_defaults.region; - }; - abnahme = { - sso_account_id = 837645089494; - sso_start_url = aws_defaults.sso.start_url; - sso_region = aws_defaults.sso.region; - sso_role_name = aws_defaults.sso.role_name; - region = aws_defaults.region; - }; - core-develop = { - sso_account_id = 934000686307; - sso_start_url = aws_defaults.sso.start_url; - sso_region = aws_defaults.sso.region; - sso_role_name = aws_defaults.sso.role_name; - region = aws_defaults.region; - }; - infrastructure = { - sso_account_id = 994756397773; - sso_start_url = aws_defaults.sso.start_url; - sso_region = aws_defaults.sso.region; - sso_role_name = aws_defaults.sso.role_name; - region = aws_defaults.region; - }; - tbmeta-development = { - sso_account_id = 730951147261; - sso_start_url = aws_defaults.sso.start_url; - sso_region = aws_defaults.sso.region; - sso_role_name = aws_defaults.sso.role_name; - region = aws_defaults.region; - }; - }; - }; - }; - - users.users.${userName} = { - isNormalUser = true; - extraGroups = [ - "dialout" - "podman" - "libvirtd" - "lp" - "networkmanager" - "scanner" - "video" - "wheel" - "wireshark" - ]; # Enable ‘sudo’ for the user. - shell = pkgs.fish; - }; - - home-manager = { - useUserPackages = true; - useGlobalPkgs = true; - users.${userName} = { lib, pkgs, ... }: { - imports = [ ../modules ]; - config = { - home.stateVersion = config.system.stateVersion; - - home.packages = with pkgs; [ - mycli - timetrap - tradebyte-attendance - tradebyte-vpn - - # common - asciinema - bat - docker-compose - envsubst - evince - exa - gnupg - nmap - psutils - pwgen - tig - vlc - xdg_utils - ]; - }; - }; - }; - - security.sudo.extraRules = [{ - users = [ userName ]; - commands = [ - { - command = "${tradebyte-vpn}/bin/tradebyte-vpn"; - options = [ "NOPASSWD" ]; - } - ]; - }]; -} diff --git a/home-manager/users/jalr.nix b/home-manager/users/jalr.nix deleted file mode 100644 index ed7f71f..0000000 --- a/home-manager/users/jalr.nix +++ /dev/null @@ -1,71 +0,0 @@ -{ config, pkgs, ... }: - -{ - imports = [ - ./default.nix - ]; - - myConfig = { - git = { - user = { - name = "Jakob Lechner"; - email = "mail@jalr.de"; - }; - signByDefault = true; - }; - gpg.defaultKey = "66FB54F6081375106EEBF651A222365EB448F934"; - }; - - users.users.jalr = { - isNormalUser = true; - extraGroups = [ - "audio" - "dialout" - "docker" - "libvirtd" - "lp" - "networkmanager" - "scanner" - "video" - "wheel" - "wireshark" - ]; # Enable ‘sudo’ for the user. - shell = pkgs.fish; - }; - - home-manager = { - useUserPackages = true; - useGlobalPkgs = true; - users.jalr = { lib, pkgs, ... }: { - imports = [ ../modules ]; - config = { - home.stateVersion = if config.system.stateVersion == "22.11" then "22.05" else config.system.stateVersion; - - home.packages = with pkgs; [ - cutecom - ghostscript - newsboat - pdftk - platformio - ptouch-print - qrencode - sshfs - tmate - - # common - asciinema - bat - docker-compose - envsubst - evince - exa - gnupg - nmap - psutils - pwgen - tig - ]; - }; - }; - }; -} diff --git a/hosts/aluminium/configuration.nix b/hosts/aluminium/configuration.nix index 0d76fb2..3dfd6a3 100644 --- a/hosts/aluminium/configuration.nix +++ b/hosts/aluminium/configuration.nix @@ -1,21 +1,18 @@ -{ config, lib, pkgs, ... }: +{ config, ... }: -let - iptablesAppendIfMissing = rule: "iptables -C " + rule + " || iptables -A " + rule; - iptablesInsertIfMissing = rule: "iptables -C " + rule + " || iptables -I " + rule; -in { imports = [ ./hardware-configuration.nix - ../../home-manager/users/jalr.nix + ../../users/jalr ./services + ./ports.nix ]; - networking.hostName = "aluminium"; services.openssh.enable = true; security.sudo.wheelNeedsPassword = false; networking = { + hostName = "aluminium"; useDHCP = false; vlans = { lechner = { @@ -26,6 +23,10 @@ in id = 2; interface = "enp1s0"; }; + iot = { + id = 3; + interface = "enp1s0"; + }; pv = { id = 10; interface = "enp1s0"; @@ -34,6 +35,10 @@ in id = 11; interface = "enp1s0"; }; + sprechanlage = { + id = 12; + interface = "enp1s0"; + }; }; interfaces = { lechner.ipv4.addresses = [{ @@ -44,13 +49,21 @@ in address = "192.168.1.1"; prefixLength = 24; }]; + iot.ipv4.addresses = [{ + address = "192.168.2.1"; + prefixLength = 24; + }]; pv.ipv4.addresses = [{ address = "192.168.10.1"; prefixLength = 30; }]; heizung.ipv4.addresses = [{ address = "192.168.10.5"; - prefixLength = 24; + prefixLength = 30; + }]; + sprechanlage.ipv4.addresses = [{ + address = "192.168.10.9"; + prefixLength = 30; }]; enp2s0.useDHCP = false; }; @@ -62,19 +75,22 @@ in "voice" ]; }; - firewall = { - extraCommands = lib.concatStringsSep "\n" [ - (iptablesAppendIfMissing "FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu") - (iptablesInsertIfMissing "INPUT -i voice -p udp -m udp --dport 5060 -j ACCEPT") - (iptablesInsertIfMissing "INPUT -s 217.10.68.150 -p udp --dport 5060 -j ACCEPT") - ]; + firewall.extraInputRules = '' + iifname "voice" udp dport 5059 accept + ip saddr 217.10.68.150 udp dport 5060 accept + ''; + nftables.tables.pppoe = { + family = "ip"; + content = '' + chain clamp { + type filter hook forward priority mangle; + oifname "ppp0" tcp flags syn tcp option maxseg size set rt mtu comment "clamp MSS to Path MTU" + } + ''; }; }; - sops.secrets.pap-secrets = { - sopsFile = ./secrets.yaml; - }; environment.etc."ppp/pap-secrets".source = config.sops.secrets.pap-secrets.path; services.pppd = { enable = true; @@ -100,7 +116,7 @@ in noipv6 novjccomp persist - plugin rp-pppoe.so enp2s0 + plugin pppoe.so enp2s0 user l8545506 ''; }; @@ -111,7 +127,6 @@ in enable = true; algorithm = "zstd"; memoryPercent = 60; - numDevices = 1; priority = 1; }; @@ -121,7 +136,7 @@ in # this value at the release version of the first install of this system. # Before changing this value read the documentation for this option # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "22.05"; # Did you read the comment? + system.stateVersion = "23.11"; # Did you read the comment? } diff --git a/hosts/aluminium/hardware-configuration.nix b/hosts/aluminium/hardware-configuration.nix index c88e569..6b60bb6 100644 --- a/hosts/aluminium/hardware-configuration.nix +++ b/hosts/aluminium/hardware-configuration.nix @@ -1,7 +1,7 @@ # Do not modify this file! It was generated by ‘nixos-generate-config’ # and may be overwritten by future invocations. Please make changes # to /etc/nixos/configuration.nix instead. -{ config, lib, pkgs, modulesPath, ... }: +{ config, lib, modulesPath, ... }: { imports = @@ -35,7 +35,6 @@ }; loader.grub = { enable = true; - version = 2; device = "/dev/sda"; extraConfig = '' serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1 @@ -56,37 +55,36 @@ # boot.loader.efi.efiSysMountPoint = "/boot/efi"; # Define on which hard drive you want to install Grub. - myConfig = { + jalr = { bootloader = "grub2"; }; - fileSystems."/" = - { + fileSystems = { + "/" = { device = "/dev/disk/by-uuid/2c5b0de0-c55f-4327-bd60-1aee6c8ae234"; fsType = "btrfs"; options = [ "subvol=root" ]; }; - - fileSystems."/home" = - { + "/proc" = { + device = "/proc"; + options = [ "nosuid" "noexec" "nodev" "hidepid=2" ]; + }; + "/home" = { device = "/dev/disk/by-uuid/2c5b0de0-c55f-4327-bd60-1aee6c8ae234"; fsType = "btrfs"; - options = [ "subvol=home" ]; + options = [ "subvol=home" "nodev" "nosuid" ]; }; - - fileSystems."/nix" = - { + "/nix" = { device = "/dev/disk/by-uuid/2c5b0de0-c55f-4327-bd60-1aee6c8ae234"; fsType = "btrfs"; - options = [ "subvol=nix" ]; + options = [ "subvol=nix" "nodev" ]; }; - - fileSystems."/boot" = - { + "/boot" = { device = "/dev/disk/by-uuid/695df89b-948d-4659-8f57-335e8b25a8c5"; fsType = "ext2"; + options = [ "nodev" "nosuid" "noexec" ]; }; - + }; swapDevices = [ ]; # Enables DHCP on each ethernet and wireless interface. In case of scripted networking diff --git a/hosts/aluminium/ports.nix b/hosts/aluminium/ports.nix new file mode 100644 index 0000000..f83f360 --- /dev/null +++ b/hosts/aluminium/ports.nix @@ -0,0 +1,16 @@ +{ custom-utils, ... }: + +{ + config.networking.ports = custom-utils.validatePortAttrset { + asterisk-rtp.udp = { from = 10000; to = 10200; }; + doorbell-audiosocket.tcp = 9092; + doorbell-webrtc-ice.tcp = 8189; + doorbell-webrtc.tcp = 8889; + esphome.tcp = 6052; + home-assistant.tcp = 8123; + nginx-http.tcp = 80; + nginx-https.tcp = 443; + unifi-inform.tcp = 8080; + unifi-ui.tcp = 8443; + }; +} diff --git a/hosts/aluminium/secrets.yaml b/hosts/aluminium/secrets.yaml index f94720d..2ecd5a3 100644 --- a/hosts/aluminium/secrets.yaml +++ b/hosts/aluminium/secrets.yaml @@ -1,8 +1,10 @@ duckdns-secret: ENC[AES256_GCM,data:hp4aWnmTYKZhBehY0nuRV+H9bpCdK2uNqY3J0s1w6JsiyXip,iv:X0MtN+lqDqucgHOgS1D/RrMksNydLFW1/wqD47DWhqQ=,tag:+7qsJEJYzI+UUrdC6NZr4Q==,type:str] pap-secrets: ENC[AES256_GCM,data:UyC63/4EXZjypFlH7MLtJXpIBgD9P/Eolg2M1A==,iv:tf8W8rpRa487PIB9NW4NyDKgCoWYV/wDgs9MmKLZ/mc=,tag:r+zgW8XI9TUyoz56irYEdQ==,type:str] +myintercom-doorbell-password: ENC[AES256_GCM,data:waUUvHQ9BZFePQ==,iv:ev21SNOwzdNMc3Opo6Kgdd7daLNUf9e1/C9RtxQKV8U=,tag:aOi2f3VuR49J0sHNrVXW/A==,type:str] asterisk-pjsip: ENC[AES256_GCM,data:PMgHCdo7K1a9/OitWdUonJ66gr70uwYgylCCWAO9cYOeXdPTIFuFLHlgBIUUxfln3UqhquTzoTluZJW9vaSuzZGe1kLIYrb1hRyrM0HLCCQc8m46jN898le/9ZrEivxonWkxf4FTfpENIf7iEr5KHh4vfd4tr4IbORTFdpcbsy8pd5eyvS8G2z9dynIWS19zqrzfGrW6yZICzAJz28IQCiiHgpN16bqSwlcPm1UdX+qi0+ZJ3TAr16Px1F9VFOXtEsu4EZvJSomecJDuhjo3QzBFffXDL971of8KX05BJgtpP6SzIZXKfSWaOxaguctdFr2tScvze0o3FXpDoOn0cvinOdYQt1P2TzjFnBZ4I3N1turpD4be9xJ92coV/j1hBsZHj2mWE/iCdsrzj2uP/74b4Mo1BJZ6l1gXFg3OgyDXaVoMxAOnutelCEG0lf78hsJXF56aQ1LVSUly6ugZP4rMiPFg5oa7WfrIsVVURUt7WRFrDLCYIQVynpfeUxHshPSB+/jVvYLqie5XeNt8B8mgTJYFo5hFB28sa1beqYEA27QMT3gRvWivqDnuf8soVi/r3WREfnSCBujhzXQF/uJZEwqVEn0OQo9ICfJ8hqtvDiAw6Hb4Wn+0maoYQeKjbPHeL3kr1SUE/kU913FNig4Yn66QKYevLLIkd3uQ0GqTLcgn4Ttwu3qArlXXxrI4US8yA7XGQUutVadN7ayyZBbYnw+vUTlPfhSO+ridK3huGKnQfcPAbD31L11EeQBe2820Nba9Bb4d5QAkiGsNj5y9tZ4Vl6l2JErO63fVPKQ9fPxD3yYyZpP8Hm1e7Wl1eRsNtoWqkTRtno7hIpAYFoMYTUk2x5U/qZOgtRX0JHufi6+GXvPPlBaQNfiGzNlJjdmtTT6MGLPRQjsASGi00pSjKd4psAj9Uf8rttsHhJHvIRDRsiNSjae+JGbVlyyauU1JL44Qf+U+MaJDjkLagNqUZ9xgNFmXzr7st6bRFYCJHkmQC8bgJsdpwRMz3HjNzrKZRvRhHIiwT3d+oyrd9hoSQl3JkxcrD7AfEThrBQL9BpGCDcfr5RzfNv8Fb08tR7rlIzyb6Rw3eKlY1obfZRRNTF+iYlBDz8LLI+BwWqJiefbHB2F9nOC0of5Eqm5gjn+MXSKuSIP5ltDsjfO+m6q7c+t7udKwnJVnePtOnuf4uQpKfxjpld4e8Y1N9hyuKSjqEy83UB4yXJb1OoUAOXENvdPhGFDghmSC+ZVcCZRBG2k6d6MdXY6AkdjUAteDQLsDNMwpW8a8RwOXlDoAtxu7yEYP51BrHu2spagNfXMWHThnkcuR/TvqAPmcPlzVjcX+tnuU0k+JK5e4eWc+diTcvo8fpeaKi7A4uyGWRaZsoaauxsK1dEwIgmAAYyWc0Hl+Z49/dLW8kgr/Qh9N5SRRk/SLk4GvS0uyYYClN7G/7LdMDUwWifr32oqXEINDh0NEyehEJ9dEQsIIH5gR3OdlEAuL1C7/Js3/ZCdBREXRYt4y5y4TAO/kMmGgv7Y/Z2XVD0klXVBMvVnil4LJ0H5KF+RZC4j/C6acRBdrPaI0nlE3bfAbmizQN9D7jOj5BkkRzBaYlMaBuFKRKUA6CUanhUWhIn3ZlF3Z+o4PGB2c7EFXZN+PzOSgkQYUD7KtVW/QV94mxkcqN9mKe6mAbj87neN1IHhEkNOj7KJQP60pqDjx6N+WYFpD3sYvDcJDg2WFumR8F2v+jHx09v5AB1r6AzhPJ3TCwnHN4e1+Nexxlb91iPcoSmLRF3Fimn7307260CtaA70hngWHSRaBcKTXi3WL1v9kKOou2kKs1GMy5bjREtqheBxZ1i4x56VtANF9lo9UT+97qxuAqk08Rc4z9j5M8cJK/d1syRT0z/uAuTWlRgxdE/Fj/OlDNr/SnZw9CLkQ0SVJAuJFFg9EY0ru3PC9PDNt9CJiVy0GoeK0mv7ZkTv2o456kdzMpJPBwpKLIO9tpZBbNZrMn1HpLJrfXIvmuVDFmm3EH6FVhGoI+4yB11Eo/2aEMzUOEtn55KNeESkoVel6GgYiwrg1ZlQS7XhdCTGyCOMbFTOLHgUe4vaUfPBNOyLaLWE3ZiyGCxVb+nBltcPSDHrNtbc2fuPqVom3Z1wfmako1BGcwRzbLdaUPwuu6eRa/KxppPh/PoYTttPxOArql25BWAVTI6BIhlvGgZgqDRwihHBGt1uyXjwv4ufES5zgxhMB8mNqVnCSkcLXXyvpmCiB5kEv5+V4nCJIXSNbmym+V9tEzGh+cx8up24IHrg6gG28fHfMcV7Z+JzN86jogr+sgH9wigrcYcDqTE9lHJhaZlmNraTl8viAwEXkPC/dnQuPSTX5V1qeRtKo1oFkf9xnPhdVLq51GoVU+MhQqZsbnqymgKnPWTQq3Kyiux5go/Li0BqfiV+Wwpn+f3WXJ21aMpU2FfIR26z2DULlJUYDKoewmklq8vzk5iZ/tywPFGR1G0z8IM5jwr+qz0uEccAtulCWsQjtvw0kGLnTsoB2WNL4x0Kti/cE14purKaE65wMrBoG/mxd6R7ZHE7u/Uo1MDAsgqsS8MomCqyxC/1yH9BdhpXc6VZJpborqWQjW/kK8/OBxWFjfQgwvDGeQkgv2ShV0c8U6DgnS545Im9aAxQGvu1sXMhnVNQZdZ2Ta3Gz7bTHqkxB4/X7KGHdGSmw5s/RQfo0BkBBBLLTc49pcmJTxG5LPkRebCM8ANX57qj3u/D9wYumFKclTglNdrjaxSdh3zTb1kEQ0rn/D4z7lVNUsw7srUUZeEadg3xTZSmSustbziXvp51juiJeyPjVY2AlmbVVxU0O245kbyWA8lHcEluo+dfk0Rr9hDNHz35NxQRCslPHiSKswxfuPcqyzlSiBMLsMWrJ5/RyQJgaO/XJ/x3R2o4h+MiHtUKj91epxAIpYD8JqQ4eaUkP6GJRNDSNLK3VNP69Qecc7b6AvV5udzt2up0lp7OuzEZeT88Vg8YcZvOv1UTxmkI6dem1xi4imJs+V4OZrcSt9ZTlc34rc6/lvVxVQZs/1vADB0ZVk3jp24KWuRWFGacJqUIxW8TbI8N1DtmZcf7sqoQU1QPRzkOa/UYmzWablAP4B5M5WOjyr3YSJGOzHxN+GSSs4K4jHUon+LbpKxHL5KJUSsD+kZFTfsDauFhAzpFDhR2wW/XYLr0iTvKQ6+26dIpW65P8Egv+n/CXQE0wuJ1R5z0M4FucpUo+FTUIcww8cfqfHqMlMeKEFeu8/QNdZ0uj06Q8/j6E/OUjpxTIVRQBs4qaLWxMZv3zulCUe9Czr6c28NhewIJlLUxOnCVDo5pT1OmzZPghurNyhTBFP8PfJrRXN1h2uvXfGP46dgt9jgeqqQqP9xlq+fzo9cyEZ/n4nQvY+CBuOW9Cqo41zNB0PQ3tC9SU477gQkDrg0M6/bAk+xsqVg1DpZOSuRUQnOfbTdZ1CXhESy+dcri9BeKKcTCZ6aenvW4W4J6OV8en3L4jPFsgqEWJUk1qr9ggM5NXc7RIrR0eCsiR9V1gi4HWMF1roTZ3wK9NvdATj3HWTGssfdpXht/vjedIp+InNWBWjnBfIf7XWuPgiB/ZW9uew8g8vDLULGVtww==,iv:bFKc8e+3rLAHje8UWwY2elof5xqceTTWX1f7nkE91nM=,tag:NWMiljj8urTDoka5bkF0jg==,type:str] asterisk-ari: ENC[AES256_GCM,data:HnY7d3BdScb0bmsBVlsTHAMv2k8tyyA/,iv:q+NsCHcGGOCe6gdAHbFfjKvO4dyWoW/xI5jtngJmdds=,tag:e8kuEsEokf5lAAgO/coxTQ==,type:str] -asterisk-voicemail: ENC[AES256_GCM,data:UbMjSgxZpUDzPpeVPQKfDEwKe/KgZJdMf+1q7YyPtjqiw80flSo3NqLREtuGshHVFqTSS+UFoucpJMx0qB3h4YCqJgSsJQB692q1fra1aLtnw08u1eEkqyi4EKBN4KuaD8E6zggN7Zor7IqEPrAp3H/JAWvoMqGaWZTn4vJbNnzFHp9JPvzyf0emmXTXm+SJFjoSjuT3tgrimQ9UsgUrX4Nxx6ZTMKnRU72+sEaRqtSjlYfk4aYveB82oV+s1iexGidy8vgVEJzRxkkbc87+2BrSTGW63VjIMLSYAFYOQp+hsP263lRJ8ZJYdJqrk49KXMpz8M5/lgk4P78Z/1PQ741CxpzLfo2oXij6jNPaH4jGujrsqK6lUSWi4g9rft76hYZF15absiLbvJZLIXcwTG4OsFkd+c7Ths7j/P/ttjBcnOgykz8ywW8XsSISBXehaO6FwPk37rddUHeEYOvwzKbH1qpgbtS5z/BKONKxqh+8FY5Wf3n80/0SbBQ3JlBnuTuqHQQlKCv2zkzGQv3aeu1WgqmM1+OH3BgSFM/F4o/gINWsOdU0DD9rNobb909mJspY3E1iEdPEElrMeZ/xKc2Zl1sKQtZIyZMRm0gYo/H/hODmBS3bqvTapmeMWpe5SCyw03FTp1O//brPHLdNqglkll9qqgsf4JUnCqefK5qyqqS4kPcDAzSBrYcBZLsKg5DnZbedD/rFNPDrlTaADMo4HkpwtE3U8jznVSDqAL3PfWvGk/bYLxaObXIzPpZybL/ILnnV5iifqSkhqhVwUaCX6nH0uB0mpiwXFbrFRYY6egqKrxtLoMEh5qT3wFdtKTt9a0Nnytvh/00vpv9yiHVEgkXGFkSC4Y3n3rSImWONKO+mYvlPH5kKM9vT9nIKBRBC7rmM3NRCKYCtPyOZlmTb6spADscUC6kux51Abd7GT/XWCmETB6cMNN2IQUVuJvcXX8IJay/mYYdgwY1DtVAWSkJlnJGAuvNQL4/v5v08u8GrnCO89aZwbA56zEulOBVr5iINm0BmqCAg9OHJGjlq4eK0/5bT,iv:ra1EmIfT7Pwz5GVidrlTDN/Ox4ErsAvQCe7hoQoTXRg=,tag:T7nCiJ+8djxFbTJfB9Hhxw==,type:str] +asterisk-voicemail: ENC[AES256_GCM,data:uyXeBP+9WkfVot4Ot3vwv3OEZfoVDK2I+lvaPpGJTZp16YNtP+uxNiW2ynewQlORCTY59bP1jW3bQdT/ASGsErOrhInYSytTyfdZ51BF9+jz0TH6oWxsSuuawTrkC8jvJOpejt6XuGoYbbqlM/VL1xzgDkq3ztTxaHTfdTonQij2Q4cYddMRHWIEuBCK7FU2TlHAJeIFZvtE0MiyNNT3rEWSs1xcljTGfMjkoMd+FI1uZSQT4r0kAaPPkvCWcAGH6R+F0Ue++i9TuLhu+sDV+X6u3N/garDW74H0bOcLJysImtuPXh1aXuBkHQuC1Liss/IF4NDjtDDhpfc0eePR5MWv/Kj0q+VFJiUPY6XnWh6fG9I2yY22+I7eAAg/xWVZBXPWbFHRz8jm1owp4ln6/hcrJOw6Fzw8tZ6Jd9nciOeOmR1KtjEzklPP5kP1YQPtGio/LnOaAAhTHy16MbWf/Ey4S30+eHB+joD8OM93+YxxrdKNE6XXEcAhkdpHYecrvz4Co1fhY7ZoOnNvA8Juup/7PMyNEU/Fy4Pta34aT/j1s7de2vTpRNBeecWvgFA9Qd7Re/2XPqOAkpduxDniwsUdb52oL39MBoOCY8brmXn2J/mMDeOmoqvjRHsPZsajPTAqF/nqRB8VpwoZAKAx59DYBGgmHz7/7JRX9NXOAus1yLbMfVqDftk6+KTFQ9wCqei3jaI/K5AJrSEwlZG0BLoDefIGXT5f8bNNgSn865j2RP+FLa6W3/u5t+k=,iv:/phktIxMdDO5Nrum7hf3oLDmQO04lrkvFuHNw77aRks=,tag:7OUg0BG9X7nBHWiQNaSOEQ==,type:str] +esphome: ENC[AES256_GCM,data:2pFVokO8YTyKa1F7EePo6wIS3y6prL8SSkxypWZkHl3Ye6Qg0eqZ4du/iwLIXQpJoc6R3uU7D6eIQEVOGbwqYp6+F0CW17F89k9c/VLHQHRpWbA20GgLr7X4fZ8xdbp7HCLpVxRsdzDz8aoARfV8Cn6T7Uo80ah1rMDnTj10WI+Yu6xVqVwPNWrSk9NUGKMK32M2slk=,iv:Xla0c4d9rxn06upy7GTbWBQ8pzl+gLnIw+Rf6hqQlhk=,tag:S+clc2ctuOA6lsInSFm93Q==,type:str] sops: kms: [] gcp_kms: [] @@ -12,25 +14,25 @@ sops: - recipient: age1ne08hny30vrkejqhh7dcx4ql6dmkx6jw9dqkf3cz7mzvt53njy0qh59w44 enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBodUx5V25ETmJLTi9EZlRU - ZnYweXliTDl4ZUlvcmliTjhQRkpzU0pkNXlZCjFtYU5ySWFxOGlNL29SR2RJZHNu - UHJ4YWE4UWJVeEJBUXJwaHJBd292REkKLS0tIDV2WlppeUxIOWFPTHlRYTBaMzA0 - MU41eU8zeTRRUlZyUXV0U1N6U0NRNnMKZK3vfyRRr7Iu6HfpdpmDTKzUbEnCnW9l - rGjFmY9VX2q9w3j/4E5uUToQfeGMqqBTOFUB3hNgU8K5ZT7wMbOXAg== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBEZmNOcVlKNmZCdWN5NVBy + U3VRbXl3OUljWCtITXZCQTdrVkIxOEtDZHg4CktBNjVKRmVucjRpYXo3WXFWd1VV + MFpGdWIvTmNHRlJ4akxUQkZzWUtXVUkKLS0tIGs3NlNXREVkT1Nta2prSXk4QkV0 + NEtzRXY1Q1Njelc1YXNWVE9Jd2NnOFEKjOWHaxO5fF5l+c1Hv6QLBQajrvu1VimZ + Hqk0GYrFpfpFtbhBRyrYgmNuX/qIRMHemdXcNKDYcj0WXgsdVqH7Qw== -----END AGE ENCRYPTED FILE----- - lastmodified: "2022-12-06T22:00:11Z" - mac: ENC[AES256_GCM,data:rg0vVhW4rXOJb72LDCLNTQtCHQu165knPZuGT56zoGGWHPDDTWnqt/WaBHyUoIBXWe286jW+aV0hFZMRCrS8cu+XSTztmXDIuNaYU7s8XW8ZzISUTCHDVUiYRSjaZXvCprr0cpiQoJ9dAl+Rm5m50elCdC8E7VaNGEYTIYWYLtk=,iv:7R8xt3WJKdcMrUdL/byMIbS9cv0dxlxou26Q3liIxSQ=,tag:IENmJHOSX9nLhrnSVHZltg==,type:str] + lastmodified: "2024-03-17T20:41:27Z" + mac: ENC[AES256_GCM,data:f7RdcXpu9CGSZpIF8rwuIkn97EWRxJXxoC7KKbkZg4yxSxZJR/S5UXzEC56eY73IdBHap4op3l+cO7pT7p1rkspHQPH/5D225ihVQ8PQ29u2nlyyrrebB5tM1Mt+rJRlizBPxDDKySJYgdqZCWUwB8f5hQudpb0CGra7NfQreRg=,iv:vwpVqib7fyuV83FiyMT4BOeuqyrcspFyieQGWyZZzcU=,tag:zuJVSA2WqzSvM4MBWrdRlQ==,type:str] pgp: - - created_at: "2022-11-02T22:14:19Z" + - created_at: "2024-01-31T01:19:14Z" enc: |- -----BEGIN PGP MESSAGE----- - wV4D3ylLYNOsO+0SAQdASri/Ozm8ibaE1PN8ItRanuAGU4jRQL1g4U8GbsiXWzcw - u7trrk6foY98pfVAP4Z78X4Dp79UagorlDCT6F6yWtfFODFdTVJdbzJsD5QtZ1vK - 0lEBMmTyLDw4lzTpedDhvgkWpNd33TC3WgAfRb/2LCSPmoVp83O7ja6BfuBQDkWY - gP7g815fKYigaihDH8HlNzvRoOOcGC9+6lyQkHTJyRjKsrg= - =WfhH + hF4DY/xpNY5WhB0SAQdAkeQx8NatnRtZUJa/G0zaw+NL5twonTayNH8mmNBXOWgw + EWaC9Yq6yWntxxfkVaJHN5BEzxVVumrKmpKSIkvCkJqFZ5SuYH/DyE9oZZSr7iC/ + 0l4BTKZ8SdxQL8usQPSQVbs9skr7KsYfhtjTeTi823RwZLD1+wZKwqe43AJTE0Hl + b2jIihfXa7wKTfi9jXI/mpxLRpGH8kZnPoQuldkz1zWIU14YKoTKq55My8qwR4uW + =RazZ -----END PGP MESSAGE----- - fp: 66FB54F6081375106EEBF651A222365EB448F934 + fp: 3044E71E3DEFF49B586CF5809BF4FCCB90854DA9 unencrypted_suffix: _unencrypted - version: 3.7.3 + version: 3.8.1 diff --git a/hosts/aluminium/services/asterisk.nix b/hosts/aluminium/services/asterisk.nix deleted file mode 100644 index e3077d6..0000000 --- a/hosts/aluminium/services/asterisk.nix +++ /dev/null @@ -1,161 +0,0 @@ -{ config, lib, ... }: -let - secretConfigFiles = [ - "ari" - "pjsip" - "voicemail" - ]; - rtp = { - start = 10000; - end = 10200; - }; -in -{ - services.asterisk = { - enable = true; - confFiles = { - "extensions.conf" = '' - [sipgate-in] - exten = _499846876,1,Noop(Processing an incoming call) - ;same = n,Dial(PJSIP/10) - same = n,Dial(PJSIP/10&PJSIP/11,45,tT) - ;same = n,VoiceMail(876@lechner,u) - same = n,Hangup() - - exten => _4998469779781,1,Verbose(3,Incoming fax) - ; folder where your incoming faxes will initially be stored - same => n,Set(FAXDEST=/tmp) - - exten = _4998469779782,1,Noop(Test call) - same = n,Dial(PJSIP/10) - - ; put a timestamp on this call so the resulting file is unique - same => n,Set(tempfax=''${STRFTIME(,,%C%y%m%d%H%M)}) - same => n,ReceiveFax(''${FAXDEST}/''${tempfax}.tif) - same => n,Verbose(3,- Fax receipt completed with status: ''${FAXSTATUS}) - same => n,System(echo | s-nail -a ''${FAXDEST}/''${tempfax} -s "Asterisk Fax" mail@jalr.de) - - exten = _499846977892,1,Noop(Processing an incoming call) - same = n,Dial(PJSIP/12,30,tT) - ;same = n,VoiceMail(977892@pauline,u) - same = n,Hangup() - - exten = _499846977891,1,Noop(Processing an incoming call) - same = n,Dial(PJSIP/13&PJSIP/16,30,tT) - same = n,VoiceMail(977891@jakob,u) - same = n,Hangup() - - [dect] - exten = 100,1,Answer() - same = n,Wait(1) - same = n,Playback(hello-world) - same = n,Hangup() - - ;exten = 10,1,Dial(PJSIP/10,10) - ;same = n,VoiceMail(876@lechner,u) - - exten = _1X,1,Dial(PJSIP/''${EXTEN},30) - same = n,Hangup() - - exten = 50,1,Dial(PJSIP/10&PJSIP/11,30,tT) - same = n,Hangup() - - exten = 99,1,Answer() - same = n,VoiceMailMain(876@lechner) - - exten = 98,1,Answer() - same = n,VoiceMailMain(977892@pauline) - - exten = _ZX.,1,Noop(Ortsgespraech) - same = n,Dial(PJSIP/09846''${EXTEN}@sipgate,300) - same = n,Hangup() - - exten = _0ZXXXXX.,1,Noop(Deutsches Festnetz) - same = n,Dial(PJSIP/''${EXTEN}@sipgate,300) - same = n,Hangup() - - exten = _00XXXXX.,1,Noop(Internationales Gespraech) - same = n,Dial(PJSIP/''${EXTEN}@sipgate,300) - same = n,Hangup() - - [voicemail-callback] - exten = s,1,HasNewVoicemail(977892@pauline) - exten = s,2,Hangup - exten = s,102,Dial(PJSIP/12,15) - exten = s,n,VoiceMailMain(977892@pauline) - exten = s,n,Hangup - - - [fax-tx] - exten => s,1,NoOp(**** SENDING FAX ****) - ;exten => s,n,Set(FAXFILE=output.tif) - - exten => s,n,NoOp(**** SETTING FAXOPT ****) - exten => s,n,Set(FAXOPT(ecm)=yes) - ;exten => s,n,Set(FAXOPT(localstationid)=098469779780) - exten => s,n,Set(FAXOPT(maxrate)=14400) - exten => s,n,Set(FAXOPT(minrate)=2400) - exten => s,n,NoOp(**** SENDING FAX : ''${FAXFILE} ****) - exten => s,n,SendFAX(/var/lib/asterisk/fax-outgoing/''${FAXFILE},d) - - ;exten => send,n,Wait(6) - ;exten => send,n,Set(GLOBAL(FAXCOUNT)=''$[ ''${GLOBAL(FAXCOUNT)} + 1 ]) - ;exten => send,n,Set(FAXCOUNT=''${GLOBAL(FAXCOUNT)}) - ;exten => send,n,Set(FAXFILE=dw-faxout.tif) - ; Set FAXOPTs - ;exten => send,n,NoOp(**** SETTING FAXOPT ****) - ;exten => send,n,Set(FAXOPT(ecm)=yes) - ;exten => send,n,Set(FAXOPT(headerinfo)=Fax from ''${GLOBAL(LASTFAXCALLERNAME)} at ''${GLOBAL(LASTFAXCALLERNUM)} was received.) - ;exten => send,n,Set(FAXOPT(maxrate)=14400) - ; Send the fax - exten => send,n,SendFAX(/home/jalr/fax.tif,d) - ''; - "http.conf" = '' - [general] - enabled=yes - bindaddr=127.0.0.1 - - ; Port to bind to for HTTP sessions (default is 8088) - ;bindport=8088 - - tlsdisablev1=yes - tlsdisablev11=yes - tlsdisablev12=yes - - tlsservercipherorder=yes - ''; - "rtp.conf" = '' - [general] - rtpstart=${toString rtp.start} - rtpend=${toString rtp.end} - ''; - "dnsmgr.conf" = '' - [general] - enable=yes - refreshinterval=300 - ''; - }; - useTheseDefaultConfFiles = [ ]; - }; - - sops.secrets = (lib.listToAttrs (map - (name: lib.nameValuePair "asterisk-${name}" { - sopsFile = ../secrets.yaml; - owner = config.users.users.asterisk.name; - }) - secretConfigFiles)); - environment.etc = lib.mapAttrs' - (name: _: lib.nameValuePair - "asterisk/${name}.conf" - { source = config.sops.secrets."asterisk-${name}".path; }) - (lib.listToAttrs (map (name: lib.nameValuePair name { }) secretConfigFiles)); - - networking.firewall = { - allowedUDPPortRanges = [ - { - from = rtp.start; - to = rtp.end; - } - ]; - }; -} diff --git a/hosts/aluminium/services/asterisk/README.md b/hosts/aluminium/services/asterisk/README.md new file mode 100644 index 0000000..dc47981 --- /dev/null +++ b/hosts/aluminium/services/asterisk/README.md @@ -0,0 +1,9 @@ +## custom voicemail greetings + +Place `busy` and/or `unavail` file in users voicemail directory. + +The file can be converted to the fitting format using sox +```bash +sox $input_file -t wav -b 16 -c 1 -r 8k -e signed-integer --endian little unavail.wav +``` + diff --git a/hosts/aluminium/services/asterisk/default.nix b/hosts/aluminium/services/asterisk/default.nix new file mode 100644 index 0000000..463ac5e --- /dev/null +++ b/hosts/aluminium/services/asterisk/default.nix @@ -0,0 +1,267 @@ +{ config, lib, pkgs, ... }: + +let + inherit (config.networking) ports; + secretConfigFiles = [ + "ari" + "pjsip" + "voicemail" + ]; + voicemail-sounds = pkgs.callPackage ./voicemail-sounds { }; +in +{ + services.asterisk = { + enable = true; + confFiles = { + "extensions.conf" = '' + [doorbell] + exten = s,1,Set(__DYNAMIC_FEATURES=doorOpen) + same = n,Dial(PJSIP/10&PJSIP/11,${toString config.services.myintercom-doorbell.dialTime}) + same = n,Hangup() + + [door-open] + exten = s,1,Verbose(0, "opening the door") + same = n,System("${pkgs.myintercom-doorbell}/bin/myintercom-doorbell-open-door") + same = n,Hangup() + + [sipgate-in] + exten = _499846876,1,Noop(Processing an incoming call) + same = n,Dial(PJSIP/10&PJSIP/11,25,tT) + same = n,VoiceMail(876@lechner,uS) + same = n,Hangup() + + exten => _4998469779781,1,Verbose(3,Incoming fax) + ; folder where your incoming faxes will initially be stored + same => n,Set(FAXDEST=/tmp) + + exten = _4998469779782,1,Noop(Test call) + same = n,Dial(PJSIP/10) + + ; put a timestamp on this call so the resulting file is unique + same => n,Set(tempfax=''${STRFTIME(,,%C%y%m%d%H%M)}) + same => n,ReceiveFax(''${FAXDEST}/''${tempfax}.tif) + same => n,Verbose(3,- Fax receipt completed with status: ''${FAXSTATUS}) + same => n,System(echo | s-nail -a ''${FAXDEST}/''${tempfax} -s "Asterisk Fax" mail@jalr.de) + + exten = _499846977892,1,Noop(Processing an incoming call) + same = n,Dial(PJSIP/12,30,tT) + ;same = n,VoiceMail(977892@pauline,u) + same = n,Hangup() + + exten = _499846977891,1,Noop(Processing an incoming call) + same = n,Dial(PJSIP/13&PJSIP/16,30,tT) + same = n,VoiceMail(977891@jakob,u) + same = n,Hangup() + + [dect] + exten = 100,1,Answer() + same = n,Wait(1) + same = n,Playback(hello-world) + same = n,Hangup() + + ;exten = 10,1,Dial(PJSIP/10,10) + ;same = n,VoiceMail(876@lechner,u) + + exten = _1X,1,Dial(PJSIP/''${EXTEN},30) + same = n,Hangup() + + exten = 50,1,Dial(PJSIP/10&PJSIP/11,30,tT) + same = n,Hangup() + + exten = 99,1,Answer() + same = n,VoiceMailMain(876@lechner) + + exten = 98,1,Answer() + same = n,VoiceMailMain(977892@pauline) + + exten = _ZX.,1,Noop(Ortsgespraech) + same = n,Dial(PJSIP/09846''${EXTEN}@sipgate,300) + same = n,Hangup() + + exten = _0ZXXXXX.,1,Noop(Deutsches Festnetz) + same = n,Dial(PJSIP/''${EXTEN}@sipgate,300) + same = n,Hangup() + + exten = _00XXXXX.,1,Noop(Internationales Gespraech) + same = n,Dial(PJSIP/''${EXTEN}@sipgate,300) + same = n,Hangup() + + [voicemail-callback] + exten = s,1,HasNewVoicemail(876@lechner) + exten = s,2,Hangup + exten = s,102,Dial(PJSIP/10&PJSIP/11,15) + exten = s,n,VoiceMailMain(876@lechner) + exten = s,n,Hangup + + + [fax-tx] + exten => s,1,NoOp(**** SENDING FAX ****) + ;exten => s,n,Set(FAXFILE=output.tif) + + exten => s,n,NoOp(**** SETTING FAXOPT ****) + exten => s,n,Set(FAXOPT(ecm)=yes) + ;exten => s,n,Set(FAXOPT(localstationid)=098469779780) + exten => s,n,Set(FAXOPT(maxrate)=14400) + exten => s,n,Set(FAXOPT(minrate)=2400) + exten => s,n,NoOp(**** SENDING FAX : ''${FAXFILE} ****) + exten => s,n,SendFAX(/var/lib/asterisk/fax-outgoing/''${FAXFILE},d) + + ;exten => send,n,Wait(6) + ;exten => send,n,Set(GLOBAL(FAXCOUNT)=''$[ ''${GLOBAL(FAXCOUNT)} + 1 ]) + ;exten => send,n,Set(FAXCOUNT=''${GLOBAL(FAXCOUNT)}) + ;exten => send,n,Set(FAXFILE=dw-faxout.tif) + ; Set FAXOPTs + ;exten => send,n,NoOp(**** SETTING FAXOPT ****) + ;exten => send,n,Set(FAXOPT(ecm)=yes) + ;exten => send,n,Set(FAXOPT(headerinfo)=Fax from ''${GLOBAL(LASTFAXCALLERNAME)} at ''${GLOBAL(LASTFAXCALLERNUM)} was received.) + ;exten => send,n,Set(FAXOPT(maxrate)=14400) + ; Send the fax + exten => send,n,SendFAX(/home/jalr/fax.tif,d) + ''; + "features.conf" = '' + [applicationmap] + doorOpen => 1,peer,Gosub,"door-open,s,1" + ''; + "http.conf" = '' + [general] + enabled=yes + bindaddr=127.0.0.1 + + ; Port to bind to for HTTP sessions (default is 8088) + ;bindport=8088 + + tlsdisablev1=yes + tlsdisablev11=yes + tlsdisablev12=yes + + tlsservercipherorder=yes + ''; + "rtp.conf" = '' + [general] + rtpstart=${toString ports.asterisk-rtp.udp.from} + rtpend=${toString ports.asterisk-rtp.udp.to} + ''; + "dnsmgr.conf" = '' + [general] + enable=yes + refreshinterval=300 + ''; + }; + useTheseDefaultConfFiles = [ ]; + }; + + sops.secrets = lib.listToAttrs (map + (name: lib.nameValuePair "asterisk-${name}" { + owner = config.users.users.asterisk.name; + }) + secretConfigFiles); + environment.etc = lib.mapAttrs' + (name: _: lib.nameValuePair + "asterisk/${name}.conf" + { source = config.sops.secrets."asterisk-${name}".path; }) + (lib.listToAttrs (map (name: lib.nameValuePair name { }) secretConfigFiles)); + + networking.firewall = { + allowedUDPPortRanges = lib.singleton ports.asterisk-rtp.udp; + interfaces.voice = { + allowedTCPPorts = [ 5060 ]; + allowedUDPPorts = [ 5060 ]; + }; + }; + + systemd.services = { + "asterisk-reload-endpoint@" = { + description = "Check if asterisk endpoint is identified and reload it when it is not."; + serviceConfig = { + Type = "oneshot"; + }; + environment = { + ENDPOINT = "%I"; + }; + script = '' + export PATH=${pkgs.lib.makeBinPath [pkgs.asterisk pkgs.gnused pkgs.gnugrep]} + if ! asterisk -r -x "pjsip show endpoint $ENDPOINT" | sed -n '/^===/,/^\s*ParameterName/{//!p}' | grep -q 'Identify:'; then + asterisk -r -x "module reload res_pjsip_endpoint_identifier_ip.so" + fi + ''; + }; + asterisk-voicemail-sounds = { + wantedBy = [ "asterisk.service" ]; + after = [ "asterisk.service" ]; + script = '' + ln -sfn \ + ${voicemail-sounds}/unavail.wav \ + /var/spool/asterisk/voicemail/lechner/876/unavail.wav + ''; + restartTriggers = [ voicemail-sounds ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + }; + "asterisk-voicemail-call@" = { + description = "Check if voicemail exists and place a call to the voicemail application."; + serviceConfig = { + Type = "oneshot"; + }; + scriptArgs = "%I"; + script = '' + export PATH=${pkgs.lib.makeBinPath [pkgs.asterisk pkgs.coreutils pkgs.findutils]} + number="$(echo "$1" | cut -d ':' -f 1)" + user="$(echo "$1" | cut -d ':' -f 2)" + channel="PJSIP/$(echo "$1" | cut -d ':' -f 3)" + + if ! find "/var/spool/asterisk/voicemail/$user/$number/INBOX/" -mindepth 1 -maxdepth 1 | read; then + exit + fi + + callfile="$(mktemp -p /tmp XXXXXXXXXX.call)" + chmod 644 "$callfile" + + cat > "$callfile" << EOF + Channel: $channel + WaitTime: 15 + Application: VoiceMailMain + Data: $number@$user + CallerID: Voicemail + EOF + + mv "$callfile" /var/spool/asterisk/outgoing/ + ''; + }; + }; + + systemd.timers = { + asterisk-reload-endpoint = { + description = "Check if asterisk endpoint is identified and reload it when it is not."; + after = [ "asterisk.service" ]; + wantedBy = [ "timers.target" ]; + timerConfig = { + Persistent = true; + OnCalendar = "*-*-* *:*:00"; + Unit = "asterisk-reload-endpoint@sipgate.service"; + }; + }; + + asterisk-voicemail-call-10 = { + description = "Check if voicemail exists and place a call to the voicemail application."; + after = [ "asterisk.service" ]; + wantedBy = [ "timers.target" ]; + timerConfig = { + Persistent = true; + OnCalendar = "*-*-* 07..22:00,20,40:00"; + Unit = "asterisk-voicemail-call@876:lechner:10.service"; + }; + }; + asterisk-voicemail-call-11 = { + description = "Check if voicemail exists and place a call to the voicemail application."; + after = [ "asterisk.service" ]; + wantedBy = [ "timers.target" ]; + timerConfig = { + Persistent = true; + OnCalendar = "*-*-* 07..22:00,10,30:50"; + Unit = "asterisk-voicemail-call@876:lechner:11.service"; + }; + }; + }; +} diff --git a/hosts/aluminium/services/asterisk/voicemail-sounds/default.nix b/hosts/aluminium/services/asterisk/voicemail-sounds/default.nix new file mode 100644 index 0000000..06451f1 --- /dev/null +++ b/hosts/aluminium/services/asterisk/voicemail-sounds/default.nix @@ -0,0 +1,13 @@ +{ stdenvNoCC }: + +stdenvNoCC.mkDerivation { + name = "voicemail-sounds"; + + src = ./.; + + dontBuild = true; + installPhase = '' + mkdir $out + cp -r * $out + ''; +} diff --git a/hosts/aluminium/services/asterisk/voicemail-sounds/unavail.wav b/hosts/aluminium/services/asterisk/voicemail-sounds/unavail.wav new file mode 100644 index 0000000..35b972e --- /dev/null +++ b/hosts/aluminium/services/asterisk/voicemail-sounds/unavail.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b9d91a693e1343a1dfdbb1e4adc89a5f58182d5c1427254f0976fa09120c9091 +size 149444 diff --git a/hosts/aluminium/services/default.nix b/hosts/aluminium/services/default.nix index 1ab0528..8639a37 100644 --- a/hosts/aluminium/services/default.nix +++ b/hosts/aluminium/services/default.nix @@ -1,8 +1,13 @@ { imports = [ - ./asterisk.nix + ./asterisk ./dnsmasq.nix + ./doorbell.nix ./dyndns.nix + ./esphome + ./home-assistant.nix + ./nginx.nix + ./ntp.nix ./unifi-controller.nix ]; } diff --git a/hosts/aluminium/services/dnsmasq.nix b/hosts/aluminium/services/dnsmasq.nix index 1fb61ef..c6ada0d 100644 --- a/hosts/aluminium/services/dnsmasq.nix +++ b/hosts/aluminium/services/dnsmasq.nix @@ -1,42 +1,59 @@ -{ pkgs, ... }: +{ lib, pkgs, ... }: -let - stateDir = "/var/lib/dnsmasq"; -in { services.dnsmasq = { enable = true; - - extraConfig = '' - listen-address=192.168.0.1 - listen-address=192.168.1.1 - interface=lo - - expand-hosts - domain=lan.kbh.jalr.de - dhcp-range=192.168.0.20,192.168.0.254,4h - dhcp-range=192.168.1.20,192.168.1.254,4h - - #dhcp-boot=lpxelinux.0,aluminium,192.168.0.1 - - cache-size=10000 - dns-forward-max=1000 - - no-hosts - addn-hosts=${pkgs.writeText "hosts.dnsmasq" '' + settings = { + listen-address = [ + "192.168.0.1" + "192.168.1.1" + "192.168.2.1" + "192.168.10.9" + ]; + interface = "lo"; + expand-hosts = true; + domain = [ + "lan.kbh.jalr.de" + "iot.kbh.jalr.de,192.168.2.0/24" + ]; + dhcp-range = [ + "192.168.0.20,192.168.0.254,4h" + "192.168.1.20,192.168.1.254,4h" + "192.168.2.20,192.168.2.254,4h" + "192.168.10.8,static,24h" + ]; + dhcp-host = [ + "AC:CC:8E:40:1C:B9,192.168.10.10,sprechanlage,infinite" + ]; + cache-size = 10000; + dns-forward-max = 1000; + no-hosts = true; + addn-hosts = "${pkgs.writeText "hosts.dnsmasq" '' 192.168.0.1 aluminium unifi - ''} - ''; - - servers = [ - "142.250.185.78" # dns.as250.net - "2001:470:20::2" # ordns.he.net - "74.82.42.42" # ordns.he.net - ]; + ''}"; + server = [ + "142.250.185.78" # dns.as250.net + "2001:470:20::2" # ordns.he.net + "74.82.42.42" # ordns.he.net + ]; + dhcp-option = [ + "option:ntp-server,192.168.0.1" + ]; + }; }; - networking.firewall = { - allowedUDPPorts = [ 53 67 ]; - allowedTCPPorts = [ 53 ]; - }; + networking.firewall.interfaces = lib.attrsets.genAttrs [ + "heizung" + "iot" + "lechner" + "pv" + "sprechanlage" + "voice" + ] + ( + _: { + allowedUDPPorts = [ 53 67 ]; + allowedTCPPorts = [ 53 ]; + } + ); } diff --git a/hosts/aluminium/services/doorbell.nix b/hosts/aluminium/services/doorbell.nix new file mode 100644 index 0000000..01190f4 --- /dev/null +++ b/hosts/aluminium/services/doorbell.nix @@ -0,0 +1,30 @@ +{ config, ... }: + +let + inherit (config.networking) ports; +in +{ + sops.secrets.myintercom-doorbell-password.owner = "asterisk"; + services.myintercom-doorbell = { + enable = true; + host = "sprechanlage.lan.kbh.jalr.de"; + username = "btxpvt0002"; + passwordFile = config.sops.secrets.myintercom-doorbell-password.path; + audiosocket = { + address = "127.0.0.1"; + port = ports.doorbell-audiosocket.tcp; + uuid = "4960ab41-dbef-4773-a25e-90536d97345e"; + }; + callerId = "Sprechanlage"; + cam = { + enable = true; + bindAddress = "192.168.0.1"; + webrtcPort = ports.doorbell-webrtc.tcp; + webrtcIceTcpPort = ports.doorbell-webrtc-ice.tcp; + }; + }; + networking.firewall.interfaces.lechner.allowedTCPPorts = [ + ports.doorbell-webrtc.tcp + ports.doorbell-webrtc-ice.tcp + ]; +} diff --git a/hosts/aluminium/services/dyndns.nix b/hosts/aluminium/services/dyndns.nix index cfca934..546cb07 100644 --- a/hosts/aluminium/services/dyndns.nix +++ b/hosts/aluminium/services/dyndns.nix @@ -1,16 +1,42 @@ -{ config, ... }: +{ config, lib, pkgs, ... }: +let + mkService = config: + lib.mapAttrs' + (name: cfg: lib.nameValuePair "godns-${name}" ( + let + config = cfg.settings // { + login_token_file = "$CREDENTIALS_DIRECTORY/login_token"; + }; + configFile = (pkgs.formats.yaml { }).generate "config.yaml" config; + in + { + description = "GoDNS service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + DynamicUser = true; + ExecStart = "${lib.getExe pkgs.godns} -c ${configFile}"; + LoadCredential = "login_token:${cfg.tokenPath}"; + Restart = "always"; + RestartSec = "2s"; + }; + } + )) + config; +in { - sops.secrets.duckdns-secret = { - sopsFile = ../secrets.yaml; - }; - services.ddclient = { - enable = true; - interval = "1min"; - protocol = "duckdns"; - server = "www.duckdns.org"; - username = "nouser"; - passwordFile = config.sops.secrets.duckdns-secret.path; - domains = [ "jalr-k" ]; - ipv6 = false; + systemd.services = mkService { + ip4 = { + tokenPath = config.sops.secrets.duckdns-secret.path; + settings = { + provider = "DuckDNS"; + domains = [{ domain_name = "www.duckdns.org"; sub_domains = [ "jalr-k" ]; }]; + resolver = "8.8.8.8"; + ip_interface = "ppp0"; + ip_urls = [ "" ]; + ip_type = "IPv4"; + interval = 60; + }; + }; }; } diff --git a/hosts/aluminium/services/esphome/default.nix b/hosts/aluminium/services/esphome/default.nix new file mode 100644 index 0000000..4e63710 --- /dev/null +++ b/hosts/aluminium/services/esphome/default.nix @@ -0,0 +1,25 @@ +{ pkgs +, config +, ... +}: +let + inherit (config.networking) ports; +in +{ + sops.secrets.esphome.restartUnits = [ config.systemd.services.esphome.name ]; + + jalr.esphome = { + enable = true; + port = ports.esphome.tcp; + secretsFile = config.sops.secrets.esphome.path; + configDir = pkgs.stdenvNoCC.mkDerivation { + name = "esphome-config"; + src = ./devices; + dontBuild = true; + installPhase = '' + mkdir $out + cp -r * $out + ''; + }; + }; +} diff --git a/hosts/aluminium/services/esphome/devices/.env b/hosts/aluminium/services/esphome/devices/.env new file mode 100644 index 0000000..640bd9c --- /dev/null +++ b/hosts/aluminium/services/esphome/devices/.env @@ -0,0 +1,2 @@ +ESPHOME_HOST="jalr-k.duckdns.org" +ESPHOME_SECRETS_FILE="esphome_${ESPHOME_HOST}_secrets.yaml" diff --git a/hosts/aluminium/services/esphome/devices/.gitignore b/hosts/aluminium/services/esphome/devices/.gitignore new file mode 100644 index 0000000..d8b4157 --- /dev/null +++ b/hosts/aluminium/services/esphome/devices/.gitignore @@ -0,0 +1,5 @@ +# Gitignore settings for ESPHome +# This is an example and may include too much for your use-case. +# You can modify this file to suit your needs. +/.esphome/ +/secrets.yaml diff --git a/hosts/aluminium/services/esphome/devices/justfile b/hosts/aluminium/services/esphome/devices/justfile new file mode 120000 index 0000000..68d1c45 --- /dev/null +++ b/hosts/aluminium/services/esphome/devices/justfile @@ -0,0 +1 @@ +../../../../../modules/esphome/devices/justfile \ No newline at end of file diff --git a/hosts/aluminium/services/esphome/devices/wasserbett.yaml b/hosts/aluminium/services/esphome/devices/wasserbett.yaml new file mode 100644 index 0000000..8d0e6ab --- /dev/null +++ b/hosts/aluminium/services/esphome/devices/wasserbett.yaml @@ -0,0 +1,64 @@ +esphome: + name: "waterbed" + friendly_name: "Wasserbett" + +esp8266: + board: d1_mini + framework: + version: recommended + +logger: + +api: + encryption: + key: !secret apikey_waterbed + +ota: + - platform: esphome + password: !secret otapass_waterbed + +wifi: + ssid: !secret wifi_ssid_kbh + password: !secret wifi_password_kbh + domain: .iot.kbh.jalr.de + enable_on_boot: true + fast_connect: true + +switch: + - platform: gpio + pin: + number: 13 + id: pump + icon: "mdi:electric-switch" + +dallas: + - pin: 12 + +sensor: + - platform: dallas + #address: 0xb7000802397ccc10 + index: 0 + name: "Temperatur" + id: temperature_waterbed + +climate: + - platform: thermostat + name: "Temperatur" + id: temperature + sensor: temperature_waterbed + heat_deadband: 0.2 + heat_overrun: 0.2 + min_heating_off_time: 300s + min_heating_run_time: 300s + min_idle_time: 30s + heat_action: + - switch.turn_on: pump + idle_action: + - switch.turn_off: pump + default_preset: heizen + on_boot_restore_from: memory + preset: + - name: heizen + default_target_temperature_low: 28.5 °C + - name: abwesend + default_target_temperature_low: 24 °C diff --git a/hosts/aluminium/services/home-assistant.nix b/hosts/aluminium/services/home-assistant.nix new file mode 100644 index 0000000..633b210 --- /dev/null +++ b/hosts/aluminium/services/home-assistant.nix @@ -0,0 +1,141 @@ +{ pkgs, config, ... }: +let + inherit (config.networking) ports; +in +{ + services.home-assistant = { + enable = true; + lovelaceConfig = { + title = "Home"; + views = [ + { + path = "default_view"; + title = "Home"; + cards = [ + { + title = "Heizung"; + type = "entities"; + entities = [ + { entity = "sensor.guntamaticbiostar_betrieb"; } + { entity = "sensor.guntamaticbiostar_pufferladung"; } + { entity = "sensor.guntamaticbiostar_puffer_oben"; } + { entity = "sensor.guntamaticbiostar_puffer_unten"; } + { entity = "sensor.guntamaticbiostar_kesseltemperatur"; } + { entity = "sensor.guntamaticbiostar_vorlauf_ist_1"; } + { entity = "sensor.guntamaticbiostar_aussentemperatur"; } + { entity = "sensor.guntamaticbiostar_co2_gehalt"; } + { entity = "select.guntamaticbiostar_program"; } + { entity = "sensor.guntamaticbiostar_programm"; } + { entity = "sensor.guntamaticbiostar_programm_hk1"; } + { entity = "sensor.guntamaticbiostar_rucklauftemperatur"; } + { entity = "sensor.guntamaticbiostar_servicezeit"; } + ]; + } + { + type = "grid"; + square = false; + columns = 1; + cards = [ + { + title = "Wasserbett"; + type = "entities"; + entities = [ + { + entity = "sensor.waterbed_temperatur"; + name = "Temperatur"; + } + ]; + } + { + type = "thermostat"; + entity = "climate.waterbed_temperatur"; + } + ]; + } + ]; + } + ]; + }; + extraComponents = [ + # See https://www.home-assistant.io/integrations + "esphome" + "openweathermap" + ]; + customComponents = [ + # https://github.com/a529987659852/GuntamaticBiostar + pkgs.home-assistant-custom-components.guntamatic + ]; + lovelaceConfigWritable = false; + configWritable = false; + config = { + http = { + server_host = [ "127.0.0.1" ]; + server_port = ports.home-assistant.tcp; + use_x_forwarded_for = true; + trusted_proxies = [ "127.0.0.1" ]; + }; + homeassistant = { + unit_system = "metric"; + time_zone = "Europe/Berlin"; + temperature_unit = "C"; + inherit (config.location) longitude; + inherit (config.location) latitude; + }; + default_config = { }; + "automation nix" = [ + { + alias = "Nachschüren"; + description = "Benachrichtigung auf iPad bei Wechsel auf Teillast"; + mode = "single"; + trigger = [ + { + platform = "state"; + entity_id = [ "sensor.guntamaticbiostar_betrieb" ]; + from = "VOLLLAST"; + to = "TEILLAST"; + } + ]; + condition = [ + { + condition = "numeric_state"; + entity_id = "sensor.guntamaticbiostar_pufferladung"; + below = "80"; + } + ]; + action = [ + { + device_id = "5612874405fa2ee539ad4518a1bb8e34"; + domain = "mobile_app"; + type = "notify"; + message = '' + Kessel läuft auf Teillast und Puffer ist unter 80%. Vielleicht willst du + nachschüren. + ''; + title = "Nachschüren?"; + } + ]; + } + ]; + "automation ui" = "!include automations.yaml"; + "scene nix" = [ + ]; + "scene ui" = "!include scenes.yaml"; + }; + }; + + systemd.tmpfiles.rules = [ + "f ${config.services.home-assistant.configDir}/automations.yaml 0755 hass hass" + "f ${config.services.home-assistant.configDir}/scenes.yaml 0755 hass hass" + ]; + + services.nginx.virtualHosts."hass.kbh.jalr.de" = { + enableACME = true; + forceSSL = true; + kTLS = true; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString ports.home-assistant.tcp}/"; + recommendedProxySettings = true; + proxyWebsockets = true; + }; + }; +} diff --git a/hosts/aluminium/services/nginx.nix b/hosts/aluminium/services/nginx.nix new file mode 100644 index 0000000..6eb78ee --- /dev/null +++ b/hosts/aluminium/services/nginx.nix @@ -0,0 +1,20 @@ +{ config, ... }: + +let + inherit (config.networking) ports; +in +{ + services.nginx = { + enable = true; + defaultHTTPListenPort = ports.nginx-http.tcp; + defaultSSLListenPort = ports.nginx-https.tcp; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + }; + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; +} diff --git a/hosts/aluminium/services/ntp.nix b/hosts/aluminium/services/ntp.nix new file mode 100644 index 0000000..45917cf --- /dev/null +++ b/hosts/aluminium/services/ntp.nix @@ -0,0 +1,12 @@ +{ + services.chrony = { + enable = true; + extraConfig = '' + allow 192.168.0.0/24 + allow 192.168.10.0/24 + leapsectz right/UTC + ''; + }; + networking.firewall.interfaces.lechner.allowedUDPPorts = [ 123 ]; + networking.firewall.interfaces.heizung.allowedUDPPorts = [ 123 ]; +} diff --git a/hosts/aluminium/services/unifi-controller.nix b/hosts/aluminium/services/unifi-controller.nix index f34d12d..ae39578 100644 --- a/hosts/aluminium/services/unifi-controller.nix +++ b/hosts/aluminium/services/unifi-controller.nix @@ -1,9 +1,16 @@ -{ pkgs, ... }: +{ config, pkgs, ... }: + +let + inherit (config.networking) ports; +in { services.unifi = { enable = true; - openFirewall = true; unifiPackage = pkgs.unifi; + mongodbPackage = pkgs.mongodb-7_0; }; - networking.firewall.allowedTCPPorts = [ 8443 ]; + networking.firewall.interfaces.lechner.allowedTCPPorts = [ + ports.unifi-inform.tcp + ports.unifi-ui.tcp + ]; } diff --git a/hosts/cadmium/configuration.nix b/hosts/cadmium/configuration.nix index 6a3f9e1..32c1151 100644 --- a/hosts/cadmium/configuration.nix +++ b/hosts/cadmium/configuration.nix @@ -1,16 +1,13 @@ -{ config, pkgs, ... }: +{ pkgs, ... }: { imports = [ ./hardware-configuration.nix - ../../home-manager/users/jalr.nix + ../../users/jalr ]; networking = { hostName = "cadmium"; - networkmanager = { - enable = true; - }; useDHCP = false; firewall = { @@ -37,10 +34,6 @@ programs.mtr.enable = true; - hardware.bluetooth.enable = true; - services.blueman.enable = true; - services.ofono.enable = true; - services.udisks2.enable = true; # udevadm info --name /dev/foo --query all @@ -50,12 +43,15 @@ SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", GROUP="users", MODE="0660" ''; - myConfig = { + jalr = { bootloader = "systemd-boot"; + bluetooth.enable = true; uefi.enable = true; - gui.enable = true; + gui = { + enable = true; + sway.enable = true; + }; workstation.enable = true; - sdr.enable = true; libvirt.enable = true; autologin.enable = true; autologin.username = "jalr"; @@ -67,6 +63,6 @@ # this value at the release version of the first install of this system. # Before changing this value read the documentation for this option # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "22.05"; # Did you read the comment? + system.stateVersion = "23.11"; # Did you read the comment? } diff --git a/hosts/cadmium/hardware-configuration.nix b/hosts/cadmium/hardware-configuration.nix index ba6a7f2..1a85863 100644 --- a/hosts/cadmium/hardware-configuration.nix +++ b/hosts/cadmium/hardware-configuration.nix @@ -42,11 +42,13 @@ "/boot" = { device = "/dev/disk/by-uuid/D384-54D8"; fsType = "vfat"; + options = [ "nodev" "nosuid" "noexec" ]; }; "/home" = { device = "/dev/disk/by-uuid/f14ae966-ac3f-467f-9263-ba9136967782"; fsType = "ext4"; noCheck = true; + options = [ "nodev" "nosuid" ]; }; }; diff --git a/hosts/copper/configuration.nix b/hosts/copper/configuration.nix new file mode 100644 index 0000000..841d390 --- /dev/null +++ b/hosts/copper/configuration.nix @@ -0,0 +1,77 @@ +{ lib, ... }: + +{ + imports = [ + ./hardware-configuration.nix + ./disko.nix + ../../users/jalr + ./services + ./framework-fixes.nix + ]; + + networking = { + hostName = "copper"; + extraHosts = lib.concatStringsSep "\n" ( + lib.attrsets.mapAttrsToList + (addr: hosts: + lib.concatStringsSep " " ([ addr ] ++ hosts) + ) + { + #"192.0.2.1" = ["example.com"]; + } + ); + firewall.interfaces.virbr0.allowedTCPPorts = [ 53 64172 ]; + firewall.interfaces.virbr0.allowedUDPPorts = [ 53 67 69 4011 ]; + }; + + zramSwap = { + enable = true; + algorithm = "zstd"; + memoryPercent = 60; + priority = 1; + }; + + services = { + fstrim.enable = true; + flatpak.enable = true; + snapper.configs = { + home = { + SUBVOLUME = "/home"; + ALLOW_USERS = [ "jalr" ]; + TIMELINE_CREATE = true; + TIMELINE_CLEANUP = true; + TIMELINE_LIMIT_HOURLY = 12; + TIMELINE_LIMIT_DAILY = 7; + TIMELINE_LIMIT_WEEKLY = 4; + TIMELINE_LIMIT_MONTHLY = 3; + TIMELINE_LIMIT_YEARLY = 0; + BACKGROUND_COMPARISON = "yes"; + NUMBER_CLEANUP = "no"; + NUMBER_MIN_AGE = "1800"; + NUMBER_LIMIT = "100"; + NUMBER_LIMIT_IMPORTANT = "10"; + EMPTY_PRE_POST_CLEANUP = "yes"; + EMPTY_PRE_POST_MIN_AGE = "1800"; + }; + }; + }; + + jalr = { + bootloader = "lanzaboote"; + bluetooth.enable = true; + uefi.enable = true; + gui = { + enable = true; + sway.enable = true; + }; + workstation.enable = true; + libvirt.enable = true; + autologin = { + enable = true; + username = "jalr"; + }; + }; + + system.stateVersion = "24.05"; +} + diff --git a/hosts/copper/disko.nix b/hosts/copper/disko.nix new file mode 100644 index 0000000..bdbbf17 --- /dev/null +++ b/hosts/copper/disko.nix @@ -0,0 +1,59 @@ +{ + disko.devices = { + disk = { + nvme = { + type = "disk"; + device = "/dev/disk/by-id/nvme-Samsung_SSD_990_PRO_2TB_S7DNNJ0X235226N"; + content = { + type = "gpt"; + partitions = { + esp = { + type = "EF00"; + size = "1024M"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = [ "uid=0" "gid=0" "fmask=0077" "dmask=0077" "nodev" "nosuid" "noexec" ]; + }; + }; + luks = { + size = "100%"; + content = { + type = "luks"; + name = "copper-crypt"; + settings = { + allowDiscards = true; + }; + extraFormatArgs = [ "--hash sha512 --use-random --pbkdf argon2id --iter-time 5000 --pbkdf-memory ${builtins.toString (4*1024*1024)} --pbkdf-parallel 4" ]; + content = { + type = "btrfs"; + extraArgs = [ "-f" ]; + subvolumes = { + "/root" = { + mountpoint = "/"; + mountOptions = [ "compress-force=zstd:1" "noatime" ]; + }; + "/home" = { + mountpoint = "/home"; + mountOptions = [ "compress-force=zstd:1" "noatime" "nodev" "nosuid" ]; + }; + "/home/.snapshots" = { + mountOptions = [ "compress-force=zstd:1" "noatime" "nodev" "nosuid" ]; + }; + "/nix" = { + mountpoint = "/nix"; + mountOptions = [ "compress-force=zstd:1" "noatime" "noatime" "nodev" ]; + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; +} + + diff --git a/hosts/copper/framework-fixes.nix b/hosts/copper/framework-fixes.nix new file mode 100644 index 0000000..f1463d2 --- /dev/null +++ b/hosts/copper/framework-fixes.nix @@ -0,0 +1,14 @@ +{ pkgs, ... }: + +{ + boot.extraModprobeConfig = '' + options cfg80211 ieee80211_regdom="DE" + options mt7921_common disable_clc=1 + options mt7921e disable_aspm=Y + ''; + hardware.firmware = [ pkgs.wireless-regdb ]; + + services.udev.extraRules = '' + ACTION=="add", SUBSYSTEM=="pci", ATTR{power/wakeup}="disabled" + ''; +} diff --git a/hosts/copper/hardware-configuration.nix b/hosts/copper/hardware-configuration.nix new file mode 100644 index 0000000..aa46864 --- /dev/null +++ b/hosts/copper/hardware-configuration.nix @@ -0,0 +1,18 @@ +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = [ + (modulesPath + "/installer/scan/not-detected.nix") + ]; + + boot.initrd = { + availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usb_storage" "sd_mod" ]; + }; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; + + environment.systemPackages = with pkgs; [ + fw-ectool + ]; +} diff --git a/hosts/copper/secrets.yaml b/hosts/copper/secrets.yaml new file mode 100644 index 0000000..5ad148c --- /dev/null +++ b/hosts/copper/secrets.yaml @@ -0,0 +1,32 @@ +ntfy_shiftphone: ENC[AES256_GCM,data:WG/LlELNgEh2BiyrOYLDvYk3AlObSvUYUH8v3Cq9oHOhN1+Iwg==,iv:MVwLBIQjY8Z31V9mXf7Ge/jGb9S7ceLFx2TffcsO+o4=,tag:skeQbBPLYH8D4CPDorJ0fQ==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1rrut5ntrkqmvttvmpa5jcmjhr2pfpyaqgu9dmtx6v07lgjxx5ppsl7e5v3 + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzbXFqbHJFM0xxL284dWZD + TDkzcGVSRGorRWQvV3h3dkJ6UjNOeUxVcGdRCk5jTkZDeVFORVVWdm1vZm5XUHdk + S0ZBTEdEeDgramZNZm5xK3RkVkkxSDgKLS0tIFZ6dysvVm1YNlJzOVFXZXhrdXBE + dU0reGFSUmRxb0ZlUHgyYlpjU0FOQUEKuOMKvkZcynBGyMHmAYmz13Jy32YKyVK0 + ztCWcXbl9qCe6KtI0yW+t8DLk/PaRrmSrB+2ICTMFqPh7HiBoX+KgQ== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2025-03-12T20:36:21Z" + mac: ENC[AES256_GCM,data:BpwQmtqj8NkTNO7cJHMoOeILY4HRcb7OasiCcnXsBwIFvbeDgwj+DMZOeKbitLXwzS5frWhZWg0eBHQ4BZQFjX1K0KReVacH9CblHnSZLxjMg3x6o3upB70YjdmD3KKBisOwfMCjklwk0rKwx0w5vzac3r1nJU+PGtFw1luIiBs=,iv:bYIRVFWVGjwgmaGu6JqvpCa0TIp8idP5Bc5cYV7Bri8=,tag:D2xS1PK9a9Dd1mm8+R9RRA==,type:str] + pgp: + - created_at: "2025-03-12T20:51:07Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hF4DY/xpNY5WhB0SAQdAhB2C4sQhoL04j1RiWoeNCNSbGxDkrqXP+IffdoY8DWgw + x8aogh0b7CpTplBG/4g/WMVB4N/86uvI+mLYxJMyRb9b0f0bDr5dPpnhk//r/MDg + 0l4B9+hcSzmkwXlKh7L8Ds4cZr/z3RlqnR424KSfKbiaaigYttui5l4xgEEPZE1H + 1yfIJ5lBMgG1HTj3HX5mqM9ocA4HVzIkfPPqrFRAgjZdqeDEbLBT3lItMlvsOwy4 + =kS0b + -----END PGP MESSAGE----- + fp: 3044E71E3DEFF49B586CF5809BF4FCCB90854DA9 + unencrypted_suffix: _unencrypted + version: 3.9.4 diff --git a/hosts/copper/services/default.nix b/hosts/copper/services/default.nix new file mode 100644 index 0000000..663c3a8 --- /dev/null +++ b/hosts/copper/services/default.nix @@ -0,0 +1,8 @@ +{ + imports = [ + ./illuminanced.nix + ./ntfy.nix + ./timelog.nix + ./webdev.nix + ]; +} diff --git a/hosts/copper/services/illuminanced.nix b/hosts/copper/services/illuminanced.nix new file mode 100644 index 0000000..dde4c64 --- /dev/null +++ b/hosts/copper/services/illuminanced.nix @@ -0,0 +1,94 @@ +{ lib, pkgs, ... }: + +let + tomlFormat = pkgs.formats.toml { }; + cfg = { + daemonize = { + log_to = "syslog"; + pid_file = "/run/illuminanced/illuminanced.pid"; + #log_level = "OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE" + log_level = "ERROR"; + }; + general = { + check_period_in_seconds = 1; + light_steps = 100; + min_backlight = 20; + step_barrier = 0.1; + max_backlight_file = "/sys/class/backlight/amdgpu_bl1/max_brightness"; + backlight_file = "/sys/class/backlight/amdgpu_bl1/brightness"; + illuminance_file = "/sys/bus/iio/devices/iio:device0/in_illuminance_raw"; + #event_device_mask = "/dev/input/event*"; + #event_device_name = "Asus WMI hotkeys"; + enable_max_brightness_mode = true; + filename_for_sensor_activation = ""; + }; + kalman = { + q = 1; + r = 20; + covariance = 10; + }; + light = { + points_count = 6; + + illuminance_0 = 0; + light_0 = 0; + illuminance_1 = 20; + light_1 = 35; + illuminance_2 = 70; + light_2 = 50; + illuminance_3 = 120; + light_3 = 65; + illuminance_4 = 200; + light_4 = 75; + illuminance_5 = 255; + light_5 = 99; + }; + }; + configFile = tomlFormat.generate "illuminanced.toml" cfg; +in +{ + systemd.services.illuminanced = { + description = "Ambient Light Sensor Daemon"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "exec"; + Restart = "always"; + ExecStart = "${pkgs.illuminanced}/bin/illuminanced -c ${configFile}"; + PIDFile = cfg.daemonize.pid_file; + StandardOutput = "journal"; + BindReadOnlyPaths = [ + "/nix/store" + "/dev/log" + "/run/systemd/journal/socket" + "/run/systemd/journal/stdout" + cfg.general.max_backlight_file + (lib.strings.escape [ ":" ] cfg.general.illuminance_file) + ]; + BindPaths = [ + cfg.general.backlight_file + ]; + CapabilityBoundingSet = null; + IPAddressDeny = "any"; + LockPersonality = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "noaccess"; + ProtectSystem = "strict"; + RestrictAddressFamilies = [ ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RootDirectory = "/run/illuminanced"; + RuntimeDirectory = "illuminanced"; + SystemCallArchitectures = "native"; + SystemCallFilter = "@system-service"; + }; + }; +} diff --git a/hosts/copper/services/ntfy.nix b/hosts/copper/services/ntfy.nix new file mode 100644 index 0000000..e3e7665 --- /dev/null +++ b/hosts/copper/services/ntfy.nix @@ -0,0 +1,3 @@ +{ + sops.secrets.ntfy_shiftphone.owner = "jalr"; +} diff --git a/hosts/copper/services/timelog.nix b/hosts/copper/services/timelog.nix new file mode 100644 index 0000000..3343897 --- /dev/null +++ b/hosts/copper/services/timelog.nix @@ -0,0 +1,10 @@ +{ + powerManagement = { + powerUpCommands = '' + echo "timelog: powerUp" + ''; + powerDownCommands = '' + echo "timelog: powerDown" + ''; + }; +} diff --git a/hosts/copper/services/webdev.nix b/hosts/copper/services/webdev.nix new file mode 100644 index 0000000..5236f67 --- /dev/null +++ b/hosts/copper/services/webdev.nix @@ -0,0 +1,50 @@ +{ pkgs, lib, ... }: +{ + systemd.services = lib.attrsets.mapAttrs' + ( + name: mapping: lib.attrsets.nameValuePair "redir-${name}" { + description = "Port redirection for local development web server (${name})"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + AmbientCapabilities = "CAP_NET_BIND_SERVICE"; + BindReadOnlyPaths = [ "/nix/store" ]; + CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"; + DynamicUser = true; + ExecStart = "${pkgs.redir}/bin/redir -n 127.0.0.1:${toString mapping.to} 127.0.0.1:${toString mapping.from}"; + IPAddressAllow = "localhost"; + IPAddressDeny = "any"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = lib.mkForce true; + PrivateTmp = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "noaccess"; + ProtectSystem = "strict"; + ReadWritePaths = ""; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RootDirectory = "/run/redir-https"; + RuntimeDirectory = "redir-https"; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; + Type = "exec"; + }; + } + ) + { + http = { from = 8080; to = 80; }; + https = { from = 8443; to = 443; }; + }; +} diff --git a/hosts/default.nix b/hosts/default.nix index 2511c21..357302f 100644 --- a/hosts/default.nix +++ b/hosts/default.nix @@ -1,7 +1,10 @@ -{ ... }@inputs: +inputs: +let + hardware = inputs.nixos-hardware.nixosModules; +in { aluminium = { - targetHost = "192.168.0.1"; + targetHost = "jalr-k.duckdns.org"; system = "x86_64-linux"; }; jalr-t520 = { @@ -10,15 +13,20 @@ cadmium = { system = "x86_64-linux"; }; - hafnium = { - system = "x86_64-linux"; - }; - weinturm-pretix-prod = { - system = "x86_64-linux"; - targetHost = "91.107.235.15"; - }; iron = { system = "x86_64-linux"; - targetHost = "192.168.42.1"; + #targetHost = "192.168.42.1"; + targetHost = "jalr-bw.duckdns.org"; + }; + magnesium = { + system = "x86_64-linux"; + targetHost = "magnesium.jalr.de"; + }; + copper = { + system = "x86_64-linux"; + targetHost = "copper.lan.bw.jalr.de"; + extraModules = [ + hardware.framework-16-7040-amd + ]; }; } diff --git a/hosts/hafnium/configuration.nix b/hosts/hafnium/configuration.nix deleted file mode 100644 index 5c91afd..0000000 --- a/hosts/hafnium/configuration.nix +++ /dev/null @@ -1,142 +0,0 @@ -{ lib, config, pkgs, self, system, ... }: - -{ - imports = [ - ./hardware-configuration.nix - ../../home-manager/users/jal.nix - ]; - - networking = { - hostName = "hafnium"; - networkmanager = { - enable = true; - }; - useDHCP = false; - interfaces = { - enp2s0f0.useDHCP = false; - enp5s0.useDHCP = false; - wlp3s0.useDHCP = false; - }; - firewall = { - allowedUDPPorts = [ - 53 - ]; - allowedTCPPorts = [ - 53 - ]; - }; - extraHosts = '' - #10.10.10.10 example.com - ''; - }; - - environment.systemPackages = with pkgs; [ - brightnessctl - gnome3.adwaita-icon-theme - openconnect - redir - tcpdump - ]; - - environment.variables.EDITOR = "nvim"; - - programs.mtr.enable = true; - - hardware.bluetooth.enable = true; - services.blueman.enable = true; - services.ofono.enable = true; - - services.udisks2.enable = true; - - myConfig = { - bootloader = "systemd-boot"; - uefi.enable = true; - gui.enable = true; - workstation.enable = true; - sdr.enable = false; - libvirt.enable = true; - autologin.enable = true; - autologin.username = "jal"; - tradebyte.enable = true; - }; - - - sops.secrets = ( - lib.listToAttrs (map - (name: lib.nameValuePair "wireguard_key_${name}" { - sopsFile = ./secrets.yaml; - }) - [ - "tbcore" - "ops-testing" - ] - ) - ); - - networking.wireguard.interfaces = { - tbcore = { - ips = [ "172.27.27.16/32" ]; - privateKeyFile = config.sops.secrets.wireguard_key_tbcore.path; - listenPort = 51930; - - peers = [{ - publicKey = "K5vF/yTag6NnWjZsMug63DERdCFRfHoqxVkgKH55oFE="; - endpoint = "194.33.184.175:51930"; - #endpoint = "ccs-emergency-vpn.core.tradebyte.com:51930"; - persistentKeepalive = 25; - allowedIPs = [ - "10.158.128.0/23" - "10.158.224.0/20" - "10.18.0.0/16" - "10.64.64.0/20" # CPS - "172.31.1.0/24" - ]; - }]; - }; - ops-testing = { - ips = [ "10.254.254.2/30" ]; - privateKeyFile = config.sops.secrets.wireguard_key_ops-testing.path; - peers = [{ - publicKey = "+jZETJfwaRiM+7ys5eYjgiWEAtxP47RzZSCx0w4l2nI="; - endpoint = "3.68.138.217:2048"; - persistentKeepalive = 25; - allowedIPs = [ - "10.254.254.0/30" - "10.250.0.0/16" - ]; - }]; - }; - }; - - services.dnsmasq.servers = [ - "/vpce-0de71527ea27288f3-9op2d61c-eu-central-1b.s3.eu-central-1.vpce.amazonaws.com/10.170.254.30" - "/vpce-0de71527ea27288f3-9op2d61c.s3.eu-central-1.vpce.amazonaws.com/10.170.254.30" - "/ccs.tradebyte.com/10.170.254.30" - "/corp.ad.zalando.net/10.160.19.100" - "/develop.sys.tradebyte.com/10.0.3.1" - "/instance.tradebyte.com/10.170.254.30" - "/internal.production.core.tradebyte.com/10.158.224.2" - "/internal.development.core.tradebyte.com/10.170.254.30" - "/rds.amazonaws.com/9.9.9.9" - "/tradebyte.com/9.9.9.9" - "/tradebyte.org/9.9.9.9" - ]; - - services.actkbd = { - enable = true; - bindings = [ - { keys = [ 232 ]; events = [ "key" ]; command = "/run/current-system/sw/bin/brightnessctl s -5%"; } - { keys = [ 233 ]; events = [ "key" ]; command = "/run/current-system/sw/bin/brightnessctl s +5%"; } - ]; - }; - - # This value determines the NixOS release from which the default - # settings for stateful data, like file locations and database versions - # on your system were taken. It‘s perfectly fine and recommended to leave - # this value at the release version of the first install of this system. - # Before changing this value read the documentation for this option - # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "22.05"; # Did you read the comment? - -} - diff --git a/hosts/hafnium/hardware-configuration.nix b/hosts/hafnium/hardware-configuration.nix deleted file mode 100644 index a9bc65c..0000000 --- a/hosts/hafnium/hardware-configuration.nix +++ /dev/null @@ -1,43 +0,0 @@ -{ modulesPath, ... }: - -{ - imports = [ - "${modulesPath}/installer/scan/not-detected.nix" - ]; - - hardware.cpu.amd.updateMicrocode = true; - - boot = { - initrd.availableKernelModules = [ - "nvme" - "ehci_pci" - "xhci_pci" - "usb_storage" - "sd_mod" - "rtsx_pci_sdmmc" - ]; - kernelModules = [ "kvm-amd" ]; - }; - - fileSystems = { - "/" = { - device = "/dev/disk/by-uuid/b86310f5-fe3d-4b4d-bc02-ab0d7e9297cf"; - fsType = "btrfs"; - options = [ - "discard=async" - "noatime" - "subvol=/nixos" - "compress=zstd:6" - ]; - }; - "/boot" = { - device = "/dev/disk/by-uuid/564E-26B4"; - fsType = "vfat"; - }; - }; - - boot.initrd.luks.devices.cryptroot = { - device = "/dev/disk/by-uuid/d9b120c1-5e80-4893-92fe-497e5b44c25b"; - allowDiscards = true; - }; -} diff --git a/hosts/hafnium/secrets.yaml b/hosts/hafnium/secrets.yaml deleted file mode 100644 index f2bf06a..0000000 --- a/hosts/hafnium/secrets.yaml +++ /dev/null @@ -1,42 +0,0 @@ -wireguard_key_tbcore: ENC[AES256_GCM,data:/VdCVC6xciihm2suOiuNabAWPhWPGSyWSKbLKRpy8EK7aXpyxZPybnANc1E=,iv:/LxrjPLzUkHdyT45RIfbfc4Xa3vsnQNiamnbiMdubpg=,tag:N5nFx1QsH9FGiK9DrMg2hQ==,type:str] -wireguard_key_ops-testing: ENC[AES256_GCM,data:FiADGmh3GAK6LI9Y5EEErmoVCfx4So6mN3glnzUWk8zDXJbRYP1Uj1kJiss=,iv:7tEWVT6eeHpekgkO17DXtrO7meFvYo6xV4ZLpGG20PQ=,tag:Mtr2gMnCqfJP5ADyordddw==,type:str] -sops: - kms: [] - gcp_kms: [] - azure_kv: [] - hc_vault: [] - age: - - recipient: age1ahnfjspcpwxxk7getcxkj3fypwt37rr6p3xsmp8n2tqqqz8jtg7q2am0et - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtWlZBSFBKNXJ4QmpDZUpT - NE91ek10QkwxSU1XTE81cGxHZXZmL1JncEY0ClZFbVd5dG14L1hqQlRWTDVkZmpx - V1EzSG9rMC80WTNIZExXOXU1VjcrMk0KLS0tIElWdkh4MzNyeTNteDJTY3RvanQx - ai9YdFdleXNNY2pXQzZMem0vdDdSMjgKvngMU5Y1/Pp+G/a9SyewkN9wr22ZcGP6 - XHHadzk6NE7BJWqquY+2B0Rh3B1Ow+rC8yJd7FhJlHw+i0Bp/d/ESw== - -----END AGE ENCRYPTED FILE----- - lastmodified: "2022-04-21T08:09:31Z" - mac: ENC[AES256_GCM,data:+TB7XQPMQCFAR/0jrUKTgjm2yJ7qJ6Jak3DMbFof7mnGE9LKT+xPKYzPwAM+4aDzngHv1fumD6JCXDoJ4DS95frAVfNVNM1bfB0iVmrtf0PX1y+Em189/hs3bt2YBkvvW9kYJMq0g9VBngX6gwGuaBAFHly1gi6SPMZN4vNRF6g=,iv:DK5OYG+BohxllorP0j9mvQ7MtqVNnBjJ3Nf378scJOA=,tag:lBwsHbY9PlJ2/eMtKcxZxA==,type:str] - pgp: - - created_at: "2022-04-20T21:27:25Z" - enc: | - -----BEGIN PGP MESSAGE----- - - hQIMA6jlFWJ+id7kARAArP1hdPwQk2XyKsXYnSj6vxK81GhfZp3tkYEqsU3Jdpwn - OR+0SnuoNWk4dN4JE4ooS5DOhS0ZaVsglLPtiLLohGWYY4OrX33JHZN4oEa5GMBK - t9b0YNb9owow0MSFN679tmiCMvzXGprT0mdWO3/X/HlKvCcTYPRqul4BVeVR/LyG - V94MSaF3BUwFb4p/Q8jcWfsfH5gmMpiFHQsmtci4LjDHvAVCFzI3AjcbRRJUfO5v - ampZ+9yUNo8Y6btrQQWvMoGpOp6U7cj6rTk+eZuW16/7WbHMz6WSpolDyy01QjzQ - szS5RuACnUTMqG4YWQk90H3Srgq/6CFBVLSTm2h8zdO9UZcgkJRYLTFczbYbyqgN - 2Vpjf0UwIv5MHvdo1QZJeBEl8TxjI5UZY2/UDOb9OZXktcAxW5U0Wy6pZIfUsJpk - GJeAb+P3pLvs62hkNSS+rGoGvLX2u0R/Xvw1btTdLLOeIOPNGF8lau32mBuErIZ9 - 2E44N1qV8uQDkDdvaKpj4ikf/0MURPW4GWXST3K/BwD1Gos2SzVD17kXGGOVdeOP - Q19LSo06h2Cq+zNcyKU4C0IdRPvFLKJbyEN3vDYXGnJK7lqGr/UDDcPgYPHVPn1Q - gTdmAk2e8lZY6O0OP5tth5cMjJZj5msvjbww9J1PA3VnBuo8+17zCJ/IYwCUlEbS - XgEWH0LKnwjG7Ufr8eT0DzeCJoD2U/2h+8/+Q2dc4YqokIPW7VuZhR+HZygVAX65 - 1yT/1z+1Hr6kLr9cDLzjyPRu5rNgZJHc8pxkbrQsT764oclvfbgIcmvko9Fsg4o= - =S5XT - -----END PGP MESSAGE----- - fp: FE170812543DF81393EA56BA5042B8317A10617E - unencrypted_suffix: _unencrypted - version: 3.7.2 diff --git a/hosts/iron/configuration.nix b/hosts/iron/configuration.nix index 02c9b68..57a422a 100644 --- a/hosts/iron/configuration.nix +++ b/hosts/iron/configuration.nix @@ -1,24 +1,25 @@ -{ inputs, config, pkgs, lib, ... }: +{ config, pkgs, lib, ... }: let - zfsKernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages; - disks = [ - "ata-Samsung_SSD_870_QVO_8TB_S5SSNG0R103837K" - "ata-Samsung_SSD_870_QVO_8TB_S5SSNG0R103838A" - "ata-Samsung_SSD_870_QVO_8TB_S5SSNG0R104926N" - "ata-Samsung_SSD_870_QVO_8TB_S5SSNG0R104934H" - "ata-Samsung_SSD_870_QVO_8TB_S5SSNJ0W206517Y" - ]; + interfaces = import ./interfaces.nix; + disks = { + slot1 = "ata-Samsung_SSD_870_QVO_8TB_S5SSNG0R103837K"; + slot2 = "ata-Samsung_SSD_870_QVO_8TB_S5SSNG0R103838A"; + slot3 = "ata-Samsung_SSD_870_QVO_8TB_S5SSNG0R104926N"; + slot4 = "ata-Samsung_SSD_870_QVO_8TB_S5SSNG0R104934H"; + slot5 = "ata-Samsung_SSD_870_QVO_8TB_S5SSNJ0W206517Y"; + }; removableEfi = true; devNodes = "/dev/disk/by-id/"; datasets = { "bpool/nixos/root" = "/boot"; + "rpool/filebitch" = "/filebitch"; "rpool/navidrome" = "/var/lib/private/navidrome"; - "rpool/navidrome/music" = "/var/lib/private/navidrome/music"; + "rpool/navidrome/music" = "/var/lib/navidrome/music"; "rpool/nixos/home" = "/home"; "rpool/nixos/root" = "/"; "rpool/nixos/var/lib" = "/var/lib"; - "rpool/nixos/var/lib/qbittorrent" = "/var/lib/qbittorrent"; - "rpool/nixos/var/lib/qbittorrent/downloads" = "/var/lib/qbittorrent/downloads"; + "rpool/nixos/var/lib/qBittorrent" = "/var/lib/qBittorrent"; + "rpool/nixos/var/lib/qBittorrent/downloads" = "/var/lib/qBittorrent/downloads"; "rpool/nixos/var/log" = "/var/log"; }; partitionScheme = { @@ -27,17 +28,16 @@ let luksDev = "-part3"; biosBoot = "-part4"; }; - efiSystemPartitions = (map (diskName: diskName + partitionScheme.efiBoot) disks); - iptablesAppendIfMissing = rule: "iptables -C " + rule + " || iptables -A " + rule; + efiSystemPartitions = map (diskName: diskName + partitionScheme.efiBoot) (lib.attrValues disks); in with lib; { imports = [ - ../../home-manager/users/jalr.nix + ../../users/jalr ./services + ./ports.nix ]; config = { - - system.stateVersion = "22.11"; + system.stateVersion = "25.05"; security.sudo.wheelNeedsPassword = false; @@ -47,35 +47,56 @@ with lib; { useDHCP = false; networkmanager.enable = false; + bridges = { + "${interfaces.lan}" = { + interfaces = [ "enp2s4" "enp3s5" ]; + }; + }; + vlans = { + iot = { + id = 20; + interface = interfaces.lan; + }; + }; interfaces = { - enp3s4.ipv4.addresses = [{ + "${interfaces.lan}".ipv4.addresses = [{ address = "192.168.42.1"; prefixLength = 24; }]; - enp4s5 = { + iot.ipv4.addresses = [{ + address = "10.20.0.1"; + prefixLength = 20; + }]; + "${interfaces.wan}" = { useDHCP = true; }; }; nat = { enable = true; - externalInterface = "enp4s5"; + externalInterface = interfaces.wan; internalInterfaces = [ - "enp3s4" + interfaces.lan + "virbr0" ]; }; firewall = { - extraCommands = lib.concatStringsSep "\n" [ - (iptablesAppendIfMissing "FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu") - ]; + allowedTCPPorts = [ 5201 ]; + extraForwardRules = '' + tcp flags syn tcp option maxseg size set rt mtu + ''; + interfaces.virbr0 = { + allowedTCPPorts = [ 53 ]; + allowedUDPPorts = [ 53 67 ]; + }; }; }; services.radvd = { enable = true; config = '' - interface enp3s4 { + interface ${interfaces.lan} { AdvSendAdvert on; prefix ::/64 { AdvOnLink on; @@ -90,49 +111,60 @@ with lib; { noipv6rs waitip 6 - interface enp4s5 + interface ${interfaces.wan} ipv6rs ia_na 1 - ia_pd 1/::/64 enp3s4/0/64 + ia_pd 1/::/64 ${interfaces.lan}/0/64 ''; + jalr.luksUsbUnlock = { + enable = true; + devices = builtins.mapAttrs + (_: _: { + keyPath = "iron.key"; + usbDevice = "by-label/RAM_USB"; + waitForDevice = 10; + }) + disks; + }; + boot = { - kernelPackages = zfsKernelPackages; kernel.sysctl = { "net.ipv6.conf.all.forwarding" = 1; }; initrd = { - availableKernelModules = [ "ahci" ]; + availableKernelModules = [ + "ahci" + "ehci_pci" + "sd_mod" + "sdhci_pci" + "usb_storage" + "xhci_pci" + ]; systemd.enable = true; - luks.devices = lib.listToAttrs ( - map - (dev: { - name = "LUKS-${dev}${partitionScheme.luksDev}"; - value = { - device = "${devNodes}${dev}${partitionScheme.luksDev}"; - allowDiscards = true; - }; - }) - disks - ); + luks.devices = builtins.mapAttrs + (_: dev: { + device = "${devNodes}${dev}${partitionScheme.luksDev}"; + allowDiscards = true; + }) + disks; }; supportedFilesystems = [ "zfs" ]; zfs = { - devNodes = devNodes; + inherit devNodes; forceImportRoot = false; }; loader = { efi = { - canTouchEfiVariables = (if removableEfi then false else true); - efiSysMountPoint = ("/boot/efis/" + (head disks) - + partitionScheme.efiBoot); + canTouchEfiVariables = if removableEfi then false else true; + efiSysMountPoint = "/boot/efis/" + (head (lib.attrValues disks)) + + partitionScheme.efiBoot; }; generationsDir.copyKernels = true; grub = { enable = true; - devices = (map (diskName: devNodes + diskName) disks); + devices = map (diskName: devNodes + diskName) (attrValues disks); efiInstallAsRemovable = removableEfi; - version = 2; copyKernels = true; efiSupport = true; zfsSupport = true; @@ -141,11 +173,11 @@ with lib; { terminal_input --append serial terminal_output --append serial ''; - extraInstallCommands = (toString (map + extraInstallCommands = toString (map (diskName: '' ${pkgs.coreutils-full}/bin/cp -r ${config.boot.loader.efi.efiSysMountPoint}/EFI /boot/efis/${diskName}${partitionScheme.efiBoot} '') - (tail disks))); + (tail (attrValues disks))); }; }; kernelParams = [ @@ -154,32 +186,66 @@ with lib; { ]; }; - fileSystems = mkMerge (mapAttrsToList - (dataset: mountpoint: { - "${mountpoint}" = { - device = "${dataset}"; - fsType = "zfs"; - options = [ "X-mount.mkdir" "noatime" ]; - neededForBoot = true; - }; - }) - datasets ++ map - (esp: { - "/boot/efis/${esp}" = { - device = "${devNodes}/${esp}"; - fsType = "vfat"; - options = [ - "x-systemd.idle-timeout=1min" - "x-systemd.automount" - "noauto" - "nofail" - "noatime" - "X-mount.mkdir" - ]; - }; - }) - efiSystemPartitions); + fileSystems = mkMerge + (mapAttrsToList + (dataset: mountpoint: { + "${mountpoint}" = { + device = "${dataset}"; + fsType = "zfs"; + options = [ "X-mount.mkdir" "noatime" ]; + neededForBoot = true; + }; + }) + datasets ++ map + (esp: { + "/boot/efis/${esp}" = { + device = "${devNodes}/${esp}"; + fsType = "vfat"; + options = [ + "x-systemd.idle-timeout=1min" + "x-systemd.automount" + "noauto" + "nofail" + "noatime" + "X-mount.mkdir" + ]; + }; + }) + efiSystemPartitions) // { + "/proc" = { + device = "/proc"; + options = [ "nosuid" "noexec" "nodev" "hidepid=2" ]; + }; + }; - hardware.enableRedistributableFirmware = true; + hardware = { + enableRedistributableFirmware = true; + graphics = { + enable = true; + extraPackages = [ + pkgs.intel-vaapi-driver + ]; + }; + }; + + virtualisation.containers.storage.settings = { + storage = { + driver = "zfs"; + graphroot = "/var/lib/containers/storage"; + runroot = "/run/containers/storage"; + options.zfs.fsname = "rpool/nixos/podman"; + }; + }; + + zramSwap = { + enable = true; + algorithm = "zstd"; + memoryPercent = 60; + priority = 1; + }; + + services.zfs = { + trim.enable = false; + }; }; } diff --git a/hosts/iron/interfaces.nix b/hosts/iron/interfaces.nix new file mode 100644 index 0000000..dec89ae --- /dev/null +++ b/hosts/iron/interfaces.nix @@ -0,0 +1,4 @@ +{ + lan = "br0"; + wan = "enp0s25"; +} diff --git a/hosts/iron/luks-passfile.gpg b/hosts/iron/luks-passfile.gpg new file mode 100644 index 0000000..96c9903 Binary files /dev/null and b/hosts/iron/luks-passfile.gpg differ diff --git a/hosts/iron/ports.nix b/hosts/iron/ports.nix new file mode 100644 index 0000000..7a60b1f --- /dev/null +++ b/hosts/iron/ports.nix @@ -0,0 +1,35 @@ +{ custom-utils, ... }: + +{ + config.networking.ports = custom-utils.validatePortAttrset { + calibre-server.tcp = 8081; + calibre-web.tcp = 8082; + esphome.tcp = 6052; + grafana.tcp = 3001; + home-assistant.tcp = 8123; + jellyfin.tcp = 8096; + matrix-synapse.tcp = 8008; + mautrix-signal.tcp = 29319; + mautrix-whatsapp.tcp = 29318; + mqtt.tcp = 1883; + navidrome.tcp = 4533; + nginx-http.tcp = 80; + nginx-https.tcp = 443; + photoprism.tcp = 2342; + postfix-relay.tcp = 25; + postfix-submission.tcp = 465; + prometheus-vodafone-station-exporter.tcp = 9420; + qbittorrent-torrent.tcp = 59832; + qbittorrent-webui.tcp = 8099; + radicale.tcp = 5232; + rmfakecloud.tcp = 3000; + snapserver.tcp = 1704; + snapserverHttp.tcp = 1780; + snapserverTcp.tcp = 1705; + tvproxy.tcp = 64321; + unifi-http.tcp = 8080; + unifi-https.tcp = 8443; + wireguard-esphome.udp = 51001; + wireguard-public-ip-tunnel.udp = 51000; + }; +} diff --git a/hosts/iron/secrets.yaml b/hosts/iron/secrets.yaml index 2842df3..3a51f2f 100644 --- a/hosts/iron/secrets.yaml +++ b/hosts/iron/secrets.yaml @@ -1,34 +1,54 @@ duckdns-secret: ENC[AES256_GCM,data:SAf/xZ28tgmvqcVKC2tMNRm838AVMMNCC3fpYLXBEIoTl7E7,iv:+KTEpNMj0+aVCGKB1dRFFslgjpBhSzBZFdee+VIAt4o=,tag:C/eSyoQjAgD7Qv4J4jsp4g==,type:str] +calibre-htpasswd: ENC[AES256_GCM,data:+WW5A3/GZIk0p5CJ8RnK/gcYpJIXXsfrCpqFUWz2PzFZwf5xOlUeTGxqZdNorKq5xip4sT3/brrG4mqGDJ7iXfXJ,iv:D7CqUlbX4XGuUhjRLKytgvLa+jF4zuTZGV1NZCehf8Y=,tag:TAjSyBCIMZC4LBm0Q/2aXQ==,type:str] +radicale-htpasswd: ENC[AES256_GCM,data:Q0WnleP9I4xozsL/H+5oV3Ag7khfalV40A6ub+DA07U8UKna3/ju533RmjWOnETzSNa6XK140nfCcfGZCiqGyF9tfuuXcKFu+j4=,iv:87PSvHyKF7QUQZmEuxM+IT0VKSGnS0MjoUmCqJ+6tzI=,tag:yrP3TgxE8aSZf0MrCF9dsQ==,type:str] sturzbach-htpasswd: ENC[AES256_GCM,data:qqBwu6mASnRqjy65knU4uIvBNXXgrfcmvWnbmOH4tVQ7vRbpEhe/GQDwAg==,iv:OQnDOzezjajGl35m/u5StQeMRR+1sNDD5u1my1wTngQ=,tag:7zjVRWI1IzZ5iS3sFHLubg==,type:str] navidrome-password-encryption-key: ENC[AES256_GCM,data:ynQsFyGDEBnlWhTlv0mF7mLiXOjijq9ixWWEa1OXsTOYAd74dU0dp3Fo532WtD4fPvIWEf8Y2dYmY7zPVLuydQ==,iv:GJqPVL5OIFPLMcCVOjWvMjyFR4iTXo3uGE8R0keTzG0=,tag:RTERQgYRxBBevlL2H1lIWA==,type:str] +wireguard_key_hetzner-ha: ENC[AES256_GCM,data:ak/KpQIHBNRPriJ1IeKYXIp4CcnygRHSj5MzZNnuxQnVunmmtzGu0lBEajA=,iv:aNw3EooT6XE1zC+g37WSJasRCfnNUaKQrYCDBMTxRrg=,tag:KXc70tVFc7xDLlefk1Hzow==,type:str] +wireguard_key: + esphome: ENC[AES256_GCM,data:sMA/a0YsS/9ReJDY6gpIw+nTjkMyhs3GyEy6nA3Fiw2mvBdZCyNg0q8tdy4=,iv:WPVk4BlY7eKTjLuT/Li0oRhA9N16WFBnuuGKFjHIhLQ=,tag:0x3+3+ts2zMg2Q4eqySNnA==,type:str] +rspamd-worker-controller: ENC[AES256_GCM,data:7tS8bEr9i5F+YZoj3uPQa6Xd2SCsuC+jE531AbKEmPHNeL3qMyO0pQZ/P1ONaPHTVMOPQHYABihDJcZv0BKW,iv:pFBVi4F661fnYPcCPwuetiGL1H+RAnJiFQhTUqGNwjU=,tag:xQoHIEQpnrMOnXqsH8anxQ==,type:str] +dkim-keys: + jalr.de.default: ENC[AES256_GCM,data:mnApsYKXYGtUAHddccmNmU9yZQtekDkTiTXbJ0UJxC0rFxzQCtGsinQslIROJdNUxsxciR1ilNzxawzjJD7AaWJbcAq2TYObGJJOQZBif7t/XEN/rIxEmnAFmdeAyrSONmFb9DiEn59m6DpsU+/9Y+hnc/uwwbzueO34WHJnTqmmsxFVNQZfGR+cbSckHS3wZrfjZSKKzCRt+9DU/xxJ4voyowXLO77w00LHVkyU5liwONi0v2XJ+QeP/jIMmJeKjujZcH+qvUm/kukijqyWKGrZoAYPC2cBlL/UrNECuVdSLMXvr4KBDDTCRZCSMRgUPJ0TAfpQPTPitKJ/0igK7qQl9n/6hckY7VyP8KDS7J7G2Z2XVxfZrAR4X/7ya9B2kneVr2CNx3w954EdTcV1/lD7rcKRjKynyl3ddf8gxJFJ21k1ybo2RLnftGCRVq25qNwhyfjU8x5c7AEs+YTPDrcnmxZ/Ui276eLwpMj61oZzTp8QQhiBVwS/+ruRLC+78pu2gb1gBF/Oo3nuvQD1SOpCRikLVewCYDvfXj/hrjo+oCsjTOj+9tWRcRAEDVlhkXWCMuPXDYrdt3HrIWbQuP8NW1ezd1Ll0r1ujjtPJeSwdd8cVcUSBIoA5gU+eXnYjFaSx9BZ+sIfKqG//W3S+aBYDqAEK/z4N5q66sReb5mtSQYfbZuIZDmox9bwNMG3tJmQX0lJZgEIiuJ5/ef4ra0sj9JsRFldmIn9KUmjW9OlIwzQ42cNNvQSMD/6haNiYsE6TPzVylJ/B2kNu9Qh5FfpCIPtVORv2BAGoNvZlyhjyEiXBEZ4x2hx1l5cBwGOaGhoJ0p+1wqn2zDalIBaEFjbBVdIB6DPC6/lccvpqSwF7HvW2ugyYhW+u92vgic71/BsI4i0OlsJV18gU/zVg0Yj8SK69kEwm4wkJTrkM/I4+kkUIc5OiSAknRfjOFJc0etkh3nO34xpHLOkSv9DrKfXSAGmGZtCLtVL5LGdZeCd/g6EK0JJh6bd9Gu9koSJVq5vjdDJJFf+sgk39TCvHAvk8k1/FgdK5jMJ+pR8heJtP8G96ay3DFVm5hpbjuNKqfBvbf2rkyV6++ywRFnAQGPUiMn9g6Q4F5Ks7CC1D0Ubl7b3dCUk6BDi8rHjxy9QS0/25Yz9cF0bFd6XQDfblnyRLMi9aB36M9Vp38Oh5aB16MyvNUHzcxpaAak0yknE6OuuEMBPQZgFVADCITfy9eUXl2FoXrMWEnBO78GybQ+cV8nhynn5t0U+3koMy2E8ju5kiEofQxXylys3Q76iKRRUbQqFkh/ndWtJVVfGNpi1GrUr1w1YZM0hBY9FqqeBjf7ckj+9BdiwWJ0XauuR70o7odm02mydk1/T3Hfzt3OE5nHIXnVbum9KyPx8wXj9qc6JGFm558pQOcRUgGUi+EzGoGckkoLx4Onl+XeGysW5sXP9dbYgMBug0Tjmdo9xkoBti6znDnN/zh93bbzWITNvxMgVs8zSWEhlM0c7F02UeUXSekbTFue5FOaMdYObMvPeb53jAKBOYLr34GVFvucJhKajIaNzDvfiI6fGCMxcSsWk+P3co7gdbRlWYZELsKDu2scktZsHr/gRwRiDZXAWOLiWZL4jswQ1vXSFXJgdblEV//hr2DwsAtCAsyFcgO/LGq30xi3xNqHTkUZXo6cZYSb6EVaIywMCI5ySEnTLAp/xedySANHuo8yyVqyLxkDPI7CnnSS7JcnQF3K5z+NZ0KnIpc1ewGupOhS0fKj31XxUkoSsHEY/iWJPLNA8+4VsBkADnGdkYXHTvy/yAGV6w1k1qtjiWhDAGcE9/o6NOHctYm3cx8CVsLpve/WFUaCkGgjWJdC8XP92xsUQoE6PENn6ZzFaqGHs7hgQqE1kBcEj8N5WkEqkoMo82giHE33iYoVUdkjOTkV4iDGEqyjg1BoM0GedR2A832LseDkP7u4DjIAQfpIDu7PaeiDh7xWkPRwIMV0oDTakXTdPkPGdgFikzTaxkTzRlpCbQuV769eITqVT04kJDp7+0Rb6dtjeXc0Ennv68wZSiyrlmXbrJntg7g1wrebq28q9NMIZETAPugfK6wNDu/Iw1q1kZn2ELo6xaDlcIxHDcpzK7e2VAYYuP1k3sYnSLU3oeq54j3/yS2z1me5FEqWlPOCrjdnLkE3/GjbeMsYo2YTYJEUEd2ncacSCoXUaUoxpBnjRYcHLRUV+6jy7Amp0/52rAPzSeVlBzc+SdNiKLYA2UQ74WrMU596Gkhw1SD8jSM5QqSBhH9sL+oE4GjhjLhstMUPdkNgiwxXDTZLKcIyjN1cn+RSmvNA2KXMH6MoXrkqSkJ9u2s0QAhla51zR/LZwWbzwGOO0dkh3rwh2x+pcCfuzvlk3lYr/x5XOF2k1n8yvehXY5zIX8nk6djjLbvAzzSr/yalS7R0WYIc6CjzoUl3qz+PlneMfKHcaX00hkOlIub/ZFQf1RE+JzZxi0qQq4M8Nt1XRKGDeS448Z6znDpedStUH29krZcnjMtyLmPX7ETTsjr3HLpCOd7MQ2K1rfhmvh5BtJkn1KSUf94puZbkLH7X+WnWN0hsc+KbSXnYZvqwJ8G0/7ptp/Q+wGljqhjv+HhOeA3NUwANv1xWgbiymVIlxCodXtQwn8mxS+jxSvslGwOnyUkTT76IbFbv/IpW6PNvj/xqwOqey8a/4WCGcqs403Y7TKQ+xCflG6K3tL7U5UbMnMgXTeZvoK+DooS2eIepF2WB5XqTuOZJV2OQ6GHfaBMjXN9iGVNLi6XgkbpmcMLQ4TZq+dVmgleJb14IaTFD3n74OfmbcT9lmRfPRJEpFEMNeL3ghH54P2a91zJFASgE7x+Uv2cGcmKFtMbyc/rrhH1F/Ixlv/R37huFo1T2dPMEZ/1ouuPpbUQ5oz/JlOWw3NOxd0O6oG0x9Xib+9KxSFOusLWcFEgx70jrBQKj8s2Jj+W0gZYv+BJtPMPY0KAkRj1amt4Fd6ZrPOEXJ392EHSAEv5jssO5ba52OHKA+QkYvPPL04rwkxSAQiTl57scnEj2WEIP+Lz0/qsMnwF+3rWuz856doJZcXX+U9iuzBCaYQqA1P3BojAYhEHnXBPeolHOA3BmhT9E2TJsZ6P9SQ+GaqyLm0i4vRXGlArlkLwRBs9EZv/l4DT8q0YHha53O4rhRzGJZKAOO252Dpha1YN7+FubYGAZjaUT5O0R/7xSPrGyBejddtM8asW8+NClAn4Y6xvj1IgUg6VRpEy7ZIpZEQ+UyDWt0A4nsipaz2NyZKZ5Vxza2v1qZDdYODK8nm/zj7fR/JykaNVEVj7ceTSHdaQlajfeEWWTs92msIBcqPUXqlaR005hoVvXm+WCnzIMIXLGiyRKRsAPIDYh2hGCtvfXLSq5TYm3bnGAImL0KW3Yllt1qSqSbOYsvm5QfDmTrrccvtSLGRj0rOU3Z8f4WXjf+1YgxjZ9h8fKL+LKA8x1S6M8fl0JVGBIAU8Xe8c4+r2F1VcygJp7h+0v8o8GudM6in4djAdeMLWBgXid7r0q744joFucP56opwYQp3Lu0oFEo0omS6Rh9yPfOjdGBU2eUdjcCNXXuEJD9yHSyebviSAvDw/KH1AxYSWYnjMWACCfcbOlXf3ej7PuQgq5MdFwF7+QawXm0john4YusUon4/0fqd/IFLd6oHYYesxcFdm1jN6DeS4SAqRgeEPuEWDFERgXjLHBxl5Xdi5n+NOR3Vc7ziJ9j9/CA1DKdwmsFBBDcVKMnr2FibXpN5WsSdlBng0L2zhkL22wRH9xbz8Xk5shN20/EHoxHB5HJvwfOgHIC7ooWKOUUuNTZH43+gVN+wzRzlMfiF4X71Edw+lTnQRp6Lh03M2k9do6JPoX2+UU0h6mOYiAFkhHKzCmK3DY12c4Smx+qLJNbUGhoMgthu/WnXObm0Hr+myCooTYSVNTJx6vVjI3GZtMcat2o8B9k38u/Y5/FxqTYmyXhROwS4v3W5fXwTAaxBqQy6Xj5s4V37omBBh/Z9a43nc2VlT7dKR1wIvNB/gqhiYyYrVMtYMJqGLkeCbu50LUWT4qXyR8uaqbZTVjyJCQRxZd6fd3Zfe9wIeYe3N5qKIXkFD3n1U2Q/EyRfb3TpiA+eYkAtl6JGK0vpeWpN5M2LJ3/V79e3cIG7B7/p6BrRxKxHDnBZcu57KKaN8XM+v2KTz7XdF8bjgeu1V/B9WoBwnpzCM+3s5ffNceuUcb2gJgRAUpZvcSDLYy+9aluGU2Tvsm49fCzr851p3VSEJepgPpnvuq874AX/MbPvqidF8Y21Kss1RUbl5wrlq5IihKdM+xCSq6mjvtSPVHRvw==,iv:2NBiTTW9slOH9BvM+kVbMB/+8EiS/Dc/eaqrtiwn4HY=,tag:0rc2+ZWy9XZYE7RK/oSo3g==,type:str] +synapse-turn-shared-secret: ENC[AES256_GCM,data:Q1XRds3Zud1kYkvD6s9WUzP+kNDNsxB5SHd6oCAaLCHhHhYENSAYTZOF+rGjCPNyKFL0e/A=,iv:zScRQrz+pXHNUh/BGOaV+TVnDR3wu1Z/UO1zXarKwtA=,tag:ckpVziE+yb0FjctcT7tAkg==,type:str] +rmfakecloud: ENC[AES256_GCM,data:ktKBKb6cRv1VF8tRvXIpxIy9hPinVPKK05mgvYzz18PEdcrCLpldm5xf7ffHtY5XzDOAMXDCiz6x4xyv7071frrF0spOEPnIzVhxwG8H2Ck=,iv:qJdHjv0RziAs4G9UGeRwGQ4GE5kaObJWpIYWpRKhr9c=,tag:PXgvU1hZK/gvWGyFJaHekg==,type:str] +esphome: ENC[AES256_GCM,data:SPZ/4GqNuz3Lk0Jor815jWaw9YYiIS/u79qmdjd9eNhPUiT4PpM7gQJHLwa/KHbjYAHBICVd5dAFdBsZ5UsYww6bTZcEjM+aDcSXvrXE7NKUdYwrKfOeGdsIX/l79AlNfaPma1+3dDULj6/5ElXZJFsuurMWaWc+KLOUTBTMNRwylW1pxShywz40wqxtTQmFBLhIbk/yYLfJMuFaPnctoJnW6cutbzw/fwSitAvYEq9Ch2ZOkujOOTP/NlDAsxxBV6tFJj7UnJtGJ9lucJ7BURU0eb3Bp3K3ef1XgiyTtp0g4m2EdN/XTzzA6fI4/Vhf6giDvrbXxSM5pPTbY4fjv5cpKROaHS58il24OA0S7zccFY8XoXkl2QjiJrNnsJvNrfxn21i+LFWKCC1CMZbfEMkg+FNjzrp6nkr/kpjOlEet7umhlkxXdEUSjUBmb7luig55ICSxCH5yzRD8TwsP4fhjQgAQ8C+5fojQ/YH3VkFboY4sS5mkamDeUJ94pLy3GcfKPB5I/qipi5kUxEo7AvvxK2PHNM/0VC3S/23StQwkrH2+mrU5mo4+ngutoxyegSaqJzMJZ6KORPbDo9ETxehwWGZTuP+kpjT8Sz3J0A1LxCZwQsbXJMpABWkg2c3URJZYS/1q1o+16PocE9mbPthiayrBETss6zsYB7xYjaRMN74C1yuEL/55xRqqlrNgfk/svW4XTNRl+zkRfyxz1awWEa4zSpbqSTZT5M97uxuSHwWW64dOb7lCxfVZtpBuEMTN2iITUYWfNnyv3Ipgv2AN3vZUoYiehS8Vlv+j0SHrTAjLqUv/opTNB02PMpE9wWKsOiktDQ6VwbPkd/k9xCPqdaT4fN/yinji+91BkgeaNgyu3KRTnaQlFOxmYzQAoPHwhZSwT+s0/rOPP4wtKIUkI2IcMjEWd8nw0PEo9J/wqzZxcFZ8VKSJofrVMmo0nQF4BK0/86KXOTk2bo/5HaXE4d2s6xc3qhyGnWdWUjm1X8CLoVNx49EsVRhrJKkqrvgFH0P26qNno4m7KWtzZGt/e/e3B4+TeaVgFS5o7IY8hcnIslsbic9DW/rSAxkXdumgvumRHHtBf0WA89K84/Qhs0Ufx4z9ijZdojqTX4rRPpIpV79jaJmjS72adUjnVWMMdQWyW7Y7e0Cp0u7dcbhl1T2S8QRMsJiIOv8syiUFEy8krC7R8Lf7/kZca1MYohEud/QeNsIWq0FfpyYN/vGMoTZ0Hn5npuXap5HVlmSoPX8Bve+tgR3ZFBpTS+/uXhJ70v93IXBt0fWe5tNl518Di8k5h6xAp/8Rl9EJdUuKjVnpto8bO8LIefOQLpeTo282ykPlqxsaEimeasExNAMyi/dzX9SKuSr9D5zKzU8/0zhUA+lhKtwkUkBuxeaeqF1mlffWyjVhL/3/il9Xd7kTT461889AOKt20BH1rzqVQcUiLgoi79r4kIDs2MJE8QU+Ne3fcaP+DCvjjfiTGQ+0icZvVmgKYRpJaD7MsQWN0LvzoqbkIlXrSJprsDt9JNX4IOp6egh5bxAMwZM2V7uCF/WRDXSlHYa6XJId6fY7PMAhDsTYuD4/glFzeD121yajoeyb71AgoKy62mvgExe6nnzh6qNEAQEX8bl+L6sVtwzdYpvc3j0PXbtbEs7zwncyhphj0EOag0N3E236Ck3fW6EWIKkqg7qTBDdXrCKgv7BKl6aYIDQiaUP2hwlqpegklTRkfOrAP0tOYps9DCKGa+Bm6fK2ekG/b8OCPQdp72PtyNv5vumzaV2L6v+nc/0+5VsjL66CdJ9tvwIR9NxUC6M7pp3gMQMyxW95rj4JxZkW8f8r6EU33rQJtNeeo45Uorp3l466s//GoEmcRNO08VcFJaEdd8rocm9dsexd+0QNN9xQVDOwF+KgJAUJi+Qygvzcy+nVibow2sRUPGai6/rbNBPrVEKjfHck+s794XjVGXPAoAMk7SsNKrjSpvEHws0dnC3Faa3PA8UKZalOzmNi6A1ngK/wn5SAQ/LfXRDY/oTJa4ND/C8vJfzywR8=,iv:3nnHepX48XKuKQzO4zHcGj0VNQR2edmQ2DqvAJi5W6M=,tag:UJHGnWXrwCUgyRMGc16Ntg==,type:str] +home-assistant: ENC[AES256_GCM,data:wcFMxDdRCHf/shO9v2WaGgrsa9J2WP62xFs=,iv:9ckeIO62cFZUo8fPyQj445CrJVTooNlwLapM/oTsrkk=,tag:mlfxtXDPsB3T79P9BX9oJQ==,type:str] +mqtt-users: + home-assistant: ENC[AES256_GCM,data:oIjCw7ZnA5iOBmQdW1jcy3QQnpjT32pY,iv:5HFRkXJBdMXQbjk2ubQs3sEy5qEteiqSe2hrNc8+H40=,tag:7B6yI4oCHanE0JE/gHaKnQ==,type:str] + valetudo: ENC[AES256_GCM,data:+HRz6X+A5dhmx43G99ka0u9VozuzOFWR,iv:SPw5yoiBqN7sBH5EofevacTtu45jmuTPqToKrar0aJ0=,tag:lf+usB/eNNP1yuWW/QyTqQ==,type:str] +photoprism: + oidc-secret: ENC[AES256_GCM,data:XTAiUiGZJfSZHNbz6fePl3iMDdbxFSE7+SQH2ECRFqlo7w8TAhLyNXBxlEfGvu+8vttbKdkEm0r7132Q4ftOtA==,iv:WGsQXolbtRWIq4EDgODWNmkXdOZCsA9A3Fqoo4lJyec=,tag:5zJftwB5If/RZB3hI0Ly8A==,type:str] +prometheus: + exporters: + vodafone-station: ENC[AES256_GCM,data:eaFqYEuK3UU=,iv:BauymCkvj33TmZLyii367uVEc4Iq4GGcik4nbyT9Fpk=,tag:poB+qh5tAdv/dEt3WN6yVw==,type:str] + unpoller: ENC[AES256_GCM,data:WI1oUKHW4ef4pBk+mGM=,iv:C1LykPf1/ypUmy3ZCQzjfSjkpxhUukDNnfJnZLp2CJg=,tag:mSnZJKl9IHcx7I7GpFherw==,type:str] +tvproxy: ENC[AES256_GCM,data:MbXEmgerpUiwDgcUKF2y1+Cc+d43sKPfGGTEkvNoZFFS4rzDWw4Udg==,iv:ZDsfSb3HK008e7/J/61iqVRafIzKbtPEdhH7ixo9lSY=,tag:3JbJ+2DJKQ9G2ui6VuWbOw==,type:str] +grafana: + secret-key: ENC[AES256_GCM,data:RX0ox0r3Jwm9DMIfBnsL7ydarlrYSVBjbVXbooHR1Ms=,iv:l8Aud8VyGtz3dNARh6s8/Y6MBtc4xj1Wu/LLJv1e+KA=,tag:+7TFyRPhBS1Tvn2JLBEeAQ==,type:str] sops: - kms: [] - gcp_kms: [] - azure_kv: [] - hc_vault: [] age: - recipient: age1hx7fdu4mcha7kkxe7yevtvs6xgzgaafgenm3drhvr609wlj94sgqm497je enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsdnRlZktJNHQwSWdlb2l6 - aFNRS3U0UHM5UFVkTUtqMHU1Y093ZjdNMUhrCmZVMlBNSTlwYjlQaklzZENnR013 - UXFNaWp3WXhQOC81dGFFQXNwVHVYajAKLS0tIGh5ek95NnNRbWFsVkRncFJ3VUdE - TjdZRldhSzVtMkVoTzY1NjdGbCswRVUK0pi+8UuLqRmytcR2ikxOAM02iccl8P1y - ixv0PKPLd+vQ23QeeQy/TfoGx16XttaDUnUrPLZR3TUKtAcld8+m6w== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZSnhmN0RTMDcxR3hWMWFq + TjZNNHEzM214Tm9hK2RENm9hSlRFUy9aNW5FCm1paEx2TEEzcEd6cVIzaDk1Sk92 + NXNORGlONHQ1Uk9ocGlScXFIWmlwUDAKLS0tIDhTeDQ1KzhreEpMVFVvbFdiRjVR + SU1USkxFUUY2NVhmUHBhZkdrNDR1Q0kKiXIicInELRjDR3tuyA+lnXeCcd9lYvbV + GnBRGPM7BNO/6AA7HhAei48Kt+XE6+jQX66yTXyviKhK7Lpjrlb2YQ== -----END AGE ENCRYPTED FILE----- - lastmodified: "2023-05-04T08:42:31Z" - mac: ENC[AES256_GCM,data:30AkNRIZ/w0rn2Q4CTggRRyj1rsE0+Hzvu2HH4s4IXOlgjLqR7TUVqiVjthuJd0XqcwAaYUxVnXtumVXcjYpDi6umjBvZNTDXhB6XnmIIbETmfLppKJiogebF86scS8SSOPWbwS9VbIPhbBUcTLPzAh3KgMCjCNzT+REdZGhsWc=,iv:meLH8Fq7E+nuwQqbU3xcAg05xgbW8GoOgMnQ7MK5NEo=,tag:evY1vmSb749s7VvVErb87A==,type:str] + lastmodified: "2025-11-04T17:52:25Z" + mac: ENC[AES256_GCM,data:/q98uwoYJsPRLlWxxDn7gJQ0jRxlAfVxEmUw8ayP8gIkWzGN1DCR0jx2LFlSlWEuaPScThw5IhGxbBlBxX2wV952MC7tEoHAAMvMJberG1a6do8zSvotDHocdXVlyj4jJZhQvjUVAmeVsYBY3oRwOHdzis0JO5IW0hxgs3x+xoQ=,iv:9BR0ws9ZzukjxLpPjvl73B3RmLA+c9e7F3AVk5l0SGc=,tag:OJ/iGy/Umlj/82EtZxjLSg==,type:str] pgp: - - created_at: "2023-05-02T19:30:42Z" - enc: | + - created_at: "2024-01-31T01:20:30Z" + enc: |- -----BEGIN PGP MESSAGE----- - hF4D3ylLYNOsO+0SAQdA16evFPF5J4wB4iw3y6rQbjpyVKiU/M7qZmdsKOBpLQgw - CccmnhDpRDvQ2pTlHh674o0flfXTvFQ2H2a7KuVDLerdUuw+aBGD1RB+Ob0Vvfoi - 0l4BKpYchtdJQpQuL2Gy5LToty9EZUVVRvhyIfSasWCdDH9ajNWHFcKn6MX9wj46 - ly6CeFgZKAyyeQ6qWQnft7inEQk7krl53NBrbzDN3Rfz71zmpO97h/av7y7ilffa - =DpnU + hF4DY/xpNY5WhB0SAQdAVIYE8wlQqo3HcaT37fSDHQ5i0CxUv9kyPT9BiDgwxA4w + hJlT2XPeSK6Ob1P++oGUrVhkZCuFEnV+6ZtNjwIu9EfU6azyPZEcmffmi3PdlSvW + 0l4BHCRW14iBbixIlZxloBUEEMCg5n5HNQ5vB4jRyq4af0uYxOgE3doZbb5gaVe2 + sODYeeb0u1DdJRlvfyPgqPvit4tkovzLKsO/RUpHqTuh8cXHA8ibLIXlIUbS7FoN + =aGjt -----END PGP MESSAGE----- - fp: 66FB54F6081375106EEBF651A222365EB448F934 + fp: 3044E71E3DEFF49B586CF5809BF4FCCB90854DA9 unencrypted_suffix: _unencrypted - version: 3.7.3 + version: 3.11.0 diff --git a/hosts/iron/secrets/mail-users.nix b/hosts/iron/secrets/mail-users.nix new file mode 100644 index 0000000..bc181b7 Binary files /dev/null and b/hosts/iron/secrets/mail-users.nix differ diff --git a/hosts/iron/services/avahi.nix b/hosts/iron/services/avahi.nix new file mode 100644 index 0000000..f15827b --- /dev/null +++ b/hosts/iron/services/avahi.nix @@ -0,0 +1,20 @@ +let + interfaces = import ../interfaces.nix; +in +{ + services.avahi = { + enable = true; + allowInterfaces = [ interfaces.lan ]; + openFirewall = false; + publish = { + domain = true; + enable = true; + userServices = true; + workstation = true; + }; + }; + + networking.firewall.interfaces."${interfaces.lan}".allowedUDPPorts = [ + 5353 + ]; +} diff --git a/hosts/iron/services/calibre.nix b/hosts/iron/services/calibre.nix new file mode 100644 index 0000000..816da0b --- /dev/null +++ b/hosts/iron/services/calibre.nix @@ -0,0 +1,94 @@ +{ lib, config, ... }: +let + inherit (config.networking) ports; +in +{ + sops.secrets.calibre-htpasswd.owner = "nginx"; + + services = { + calibre-server = { + enable = true; + port = ports.calibre-server.tcp; + host = "127.0.0.1"; + }; + + calibre-web = { + enable = true; + inherit (config.services.calibre-server) user; + inherit (config.services.calibre-server) group; + listen = { + ip = "127.0.0.1"; + port = ports.calibre-web.tcp; + }; + options = { + enableBookUploading = true; + reverseProxyAuth = { + enable = true; + header = "X-Remote-User"; + }; + }; + }; + }; + + systemd.services.calibre-web = { + serviceConfig = { + BindPaths = [ + "/var/lib/calibre-web" + "/var/lib/calibre-server" + ]; + BindReadOnlyPaths = [ + "/nix/store" + ]; + CapabilityBoundingSet = ""; + IPAddressAllow = "localhost"; + IPAddressDeny = "any"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = lib.mkForce true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "noaccess"; + ProtectSystem = "strict"; + ReadWritePaths = ""; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RootDirectory = "/run/calibre-web"; + RuntimeDirectory = "calibre-web"; + StateDirectory = "calibre-web"; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + ]; + WorkingDirectory = "/var/lib/calibre-web"; + }; + }; + + + services.nginx.virtualHosts."books.jalr.de" = { + enableACME = true; + forceSSL = true; + kTLS = true; + basicAuthFile = config.sops.secrets.calibre-htpasswd.path; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString ports.calibre-web.tcp}/"; + recommendedProxySettings = true; + extraConfig = '' + client_max_body_size 200M; + proxy_set_header X-Remote-User $remote_user; + ''; + }; + }; +} diff --git a/hosts/iron/services/default.nix b/hosts/iron/services/default.nix index 95ddfc2..29cd267 100644 --- a/hosts/iron/services/default.nix +++ b/hosts/iron/services/default.nix @@ -1,11 +1,29 @@ { imports = [ + ./avahi.nix + ./calibre.nix ./dnsmasq.nix ./dyndns.nix - ./jellyfin.nix + ./esphome + ./grafana.nix + ./home-assistant.nix + ./jellyfin + ./mail.nix + ./matrix.nix ./navidrome.nix ./nginx.nix + ./ntp.nix + ./photoprism.nix + ./prometheus.nix + ./public-ip-tunnel.nix + ./radicale.nix + ./remarkable.nix + ./snapcast ./sturzbach.nix - ./unifi-controller.nix + ./tts.nix + ./tvproxy.nix + ./unifi-controller + ./whatsapp.nix + ./wireguard-esphome.nix ]; } diff --git a/hosts/iron/services/dnsmasq.nix b/hosts/iron/services/dnsmasq.nix index 05e15f8..72b5f61 100644 --- a/hosts/iron/services/dnsmasq.nix +++ b/hosts/iron/services/dnsmasq.nix @@ -1,38 +1,54 @@ -{ pkgs, ... }: +{ lib, pkgs, ... }: let - stateDir = "/var/lib/dnsmasq"; + interfaces = import ../interfaces.nix; in { services.dnsmasq = { enable = true; - - extraConfig = '' - listen-address=192.168.42.1 - interface=lo - - expand-hosts - domain=lan.bw.jalr.de - dhcp-range=192.168.42.20,192.168.42.254,4h - - cache-size=10000 - dns-forward-max=1000 - - no-hosts - addn-hosts=${pkgs.writeText "hosts.dnsmasq" '' + settings = { + bind-interfaces = true; + listen-address = [ + "192.168.42.1" + "10.20.0.1" + ]; + interface = "lo"; + expand-hosts = true; + domain = [ + "lan.bw.jalr.de,192.168.42.0/24" + "iot.bw.jalr.de,10.20.0.0/22" + ]; + dhcp-range = [ + "192.168.42.20,192.168.42.254,4h" + "10.20.1.1,10.20.3.254,12h" + ]; + cache-size = 10000; + dns-forward-max = 1000; + no-hosts = true; + addn-hosts = "${pkgs.writeText "hosts.dnsmasq" '' 192.168.42.1 aluminium unifi - ''} - ''; - - servers = [ - "142.250.185.78" # dns.as250.net - "2001:470:20::2" # ordns.he.net - "74.82.42.42" # ordns.he.net - ]; + 10.20.0.10 kuechentisch.iot.bw.jalr.de + 10.20.0.11 led-panel-schreibtisch.iot.bw.jalr.de + ''}"; + server = [ + "142.250.185.78" # dns.as250.net + "2001:470:20::2" # ordns.he.net + "74.82.42.42" # ordns.he.net + ]; + dhcp-option = [ + "option:ntp-server,192.168.42.1" + ]; + }; }; - networking.firewall = { - allowedUDPPorts = [ 53 67 ]; - allowedTCPPorts = [ 53 ]; - }; + networking.firewall.interfaces = lib.attrsets.genAttrs [ + interfaces.lan + "iot" + ] + ( + _: { + allowedUDPPorts = [ 53 67 ]; + allowedTCPPorts = [ 53 ]; + } + ); } diff --git a/hosts/iron/services/dyndns.nix b/hosts/iron/services/dyndns.nix index 2c4b7a1..e53e235 100644 --- a/hosts/iron/services/dyndns.nix +++ b/hosts/iron/services/dyndns.nix @@ -1,17 +1,56 @@ -{ config, ... }: +{ config, lib, pkgs, ... }: +let + interfaces = import ../interfaces.nix; + + mkService = config: + lib.mapAttrs' + (name: cfg: lib.nameValuePair "godns-${name}" ( + let + config = cfg.settings // { + login_token_file = "$CREDENTIALS_DIRECTORY/login_token"; + }; + configFile = (pkgs.formats.yaml { }).generate "config.yaml" config; + in + { + description = "GoDNS service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + DynamicUser = true; + ExecStart = "${lib.getExe pkgs.godns} -c ${configFile}"; + LoadCredential = "login_token:${cfg.tokenPath}"; + Restart = "always"; + RestartSec = "2s"; + }; + } + )) + config; +in { - sops.secrets.duckdns-secret = { - sopsFile = ../secrets.yaml; - }; - services.ddclient = { - enable = true; - interval = "1min"; - protocol = "duckdns"; - server = "www.duckdns.org"; - username = "nouser"; - passwordFile = config.sops.secrets.duckdns-secret.path; - domains = [ "jalr-bw" ]; - use = "if, if=enp4s5"; - #usev6=ifv6, ifv6=enp3s4 + systemd.services = mkService { + ip4 = { + tokenPath = config.sops.secrets.duckdns-secret.path; + settings = { + provider = "DuckDNS"; + domains = [{ domain_name = "www.duckdns.org"; sub_domains = [ "jalr-bw" ]; }]; + resolver = "8.8.8.8"; + ip_interface = interfaces.wan; + ip_urls = [ "" ]; + ip_type = "IPv4"; + interval = 60; + }; + }; + ip6 = { + tokenPath = config.sops.secrets.duckdns-secret.path; + settings = { + provider = "DuckDNS"; + domains = [{ domain_name = "www.duckdns.org"; sub_domains = [ "jalr-bw" ]; }]; + resolver = "2001:4860:4860::8888"; + ip_interface = interfaces.lan; + ip_urls = [ "" ]; + ip_type = "IPv6"; + interval = 60; + }; + }; }; } diff --git a/hosts/iron/services/esphome/default.nix b/hosts/iron/services/esphome/default.nix new file mode 100644 index 0000000..4e63710 --- /dev/null +++ b/hosts/iron/services/esphome/default.nix @@ -0,0 +1,25 @@ +{ pkgs +, config +, ... +}: +let + inherit (config.networking) ports; +in +{ + sops.secrets.esphome.restartUnits = [ config.systemd.services.esphome.name ]; + + jalr.esphome = { + enable = true; + port = ports.esphome.tcp; + secretsFile = config.sops.secrets.esphome.path; + configDir = pkgs.stdenvNoCC.mkDerivation { + name = "esphome-config"; + src = ./devices; + dontBuild = true; + installPhase = '' + mkdir $out + cp -r * $out + ''; + }; + }; +} diff --git a/hosts/iron/services/esphome/devices/.env b/hosts/iron/services/esphome/devices/.env new file mode 100644 index 0000000..10c24e2 --- /dev/null +++ b/hosts/iron/services/esphome/devices/.env @@ -0,0 +1,2 @@ +ESPHOME_HOST="jalr-bw.duckdns.org" +ESPHOME_SECRETS_FILE="esphome_${ESPHOME_HOST}_secrets.yaml" diff --git a/hosts/iron/services/esphome/devices/.gitignore b/hosts/iron/services/esphome/devices/.gitignore new file mode 100644 index 0000000..163bec7 --- /dev/null +++ b/hosts/iron/services/esphome/devices/.gitignore @@ -0,0 +1,2 @@ +/.esphome/ +/secrets.yaml diff --git a/hosts/iron/services/esphome/devices/README.md b/hosts/iron/services/esphome/devices/README.md new file mode 100644 index 0000000..4b38621 --- /dev/null +++ b/hosts/iron/services/esphome/devices/README.md @@ -0,0 +1,12 @@ +## Generating passwords + + +Generate home-assistant API key +```sh +dd if=/dev/random bs=32 count=1 | base64 +``` + +Generate OTA secret +```sh +pwgen 14 1 +``` diff --git a/hosts/iron/services/esphome/devices/badspiegel.yaml b/hosts/iron/services/esphome/devices/badspiegel.yaml new file mode 100644 index 0000000..7f262ed --- /dev/null +++ b/hosts/iron/services/esphome/devices/badspiegel.yaml @@ -0,0 +1,134 @@ +esphome: + name: "badspiegel" + friendly_name: "Badspiegel" + platformio_options: + board_build.flash_mode: dio + on_boot: + then: + - light.turn_on: + id: front_light + brightness: 30% + color_temperature: 2700 K + - light.turn_on: + id: background_light + brightness: 20% + color_temperature: 2700 K + +esp32: + board: az-delivery-devkit-v4 + framework: + type: arduino + version: recommended + platform_version: 5.4.0 + +logger: + +api: + encryption: + key: !secret apikey_badspiegel + +ota: + - platform: esphome + password: !secret otapass_badspiegel + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + power_save_mode: none + enable_on_boot: True + fast_connect: On + output_power: 8.5 + +output: + - platform: ledc + pin: GPIO33 + id: output_background_warm + - platform: ledc + pin: GPIO32 + id: output_background_cold + - platform: ledc + pin: GPIO25 + id: output_front_warm + - platform: ledc + pin: GPIO14 + id: output_front_cold + +light: + - platform: cwww + name: "Background light" + id: background_light + cold_white: output_background_cold + warm_white: output_background_warm + cold_white_color_temperature: 6500 K + warm_white_color_temperature: 2700 K + constant_brightness: true + gamma_correct: 0 + - platform: cwww + name: "Front light" + id: front_light + cold_white: output_front_cold + warm_white: output_front_warm + cold_white_color_temperature: 6500 K + warm_white_color_temperature: 2700 K + constant_brightness: true + gamma_correct: 0 + +switch: + - platform: gpio + name: "Heating" + id: heating + pin: GPIO26 + icon: "mdi:thermometer" + - platform: gpio + name: "Soundsystem" + id: soundsystem + pin: GPIO23 + icon: "mdi:speaker" + +esp32_touch: +# setup_mode: true + + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + - platform: dht + pin: GPIO22 + model: DHT22 + temperature: + name: "Temperatur" + id: temperature + humidity: + name: "Feuchtigkeit" + id: humidity + accuracy_decimals: 1 + update_interval: 30s + +binary_sensor: +# ESP32 touch pins: 4, 13, 27, 32, 33 + # Touch Pad T4 + - platform: esp32_touch + name: "touch pad GPIO13" + pin: GPIO13 + threshold: 902 + on_press: + then: + - light.toggle: + id: front_light + internal: true + filters: + settle: 1s + # Touch Pad T7 + - platform: esp32_touch + name: "touch pad GPIO27" + pin: GPIO27 + threshold: 1086 + on_press: + then: + - light.toggle: + id: background_light + internal: true + filters: + settle: 1s diff --git a/hosts/iron/services/esphome/devices/chinafrickeldeckenleuchte.yaml b/hosts/iron/services/esphome/devices/chinafrickeldeckenleuchte.yaml new file mode 100644 index 0000000..88224a9 --- /dev/null +++ b/hosts/iron/services/esphome/devices/chinafrickeldeckenleuchte.yaml @@ -0,0 +1,60 @@ +esp8266: + board: d1_mini + framework: + version: recommended + +logger: + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + +bp5758d: + data_pin: GPIO4 + clock_pin: GPIO5 + +output: + - platform: bp5758d + id: output_ch1 + channel: 1 + current: 20 + min_power: 0.02 + zero_means_zero: true + - platform: bp5758d + id: output_ch2 + channel: 2 + current: 20 + min_power: 0.02 + zero_means_zero: true + - platform: bp5758d + id: output_ch3 + channel: 3 + current: 20 + min_power: 0.02 + zero_means_zero: true + - platform: bp5758d + id: output_ch4 + channel: 4 + current: 80 + min_power: 0.02 + zero_means_zero: true + - platform: bp5758d + id: output_ch5 + channel: 5 + current: 80 + min_power: 0.02 + zero_means_zero: true + +light: + - platform: rgbww + name: Deckenleuchte + id: ceiling_light + red: output_ch1 + green: output_ch2 + blue: output_ch3 + warm_white: output_ch4 + cold_white: output_ch5 + warm_white_color_temperature: 2700 K + cold_white_color_temperature: 6500 K + color_interlock: true diff --git a/hosts/iron/services/esphome/devices/components/miele_w433/__init__.py b/hosts/iron/services/esphome/devices/components/miele_w433/__init__.py new file mode 100644 index 0000000..6610048 --- /dev/null +++ b/hosts/iron/services/esphome/devices/components/miele_w433/__init__.py @@ -0,0 +1 @@ +CODEOWNERS = ["@jalr"] diff --git a/hosts/iron/services/esphome/devices/components/miele_w433/miele_w433.cpp b/hosts/iron/services/esphome/devices/components/miele_w433/miele_w433.cpp new file mode 100644 index 0000000..9dd06da --- /dev/null +++ b/hosts/iron/services/esphome/devices/components/miele_w433/miele_w433.cpp @@ -0,0 +1,155 @@ +#include "miele_w433.h" +#include "esphome/core/log.h" +#include + +namespace esphome { +namespace miele_w433 { + +static const char *const TAG = "miele_w433"; + +static const std::string OPERATION_STATES[] = {"inaktiv", "Einw./Vorwäsche", "Waschen", "Spülen", "Spülstop", "Pumpen", "Endschleudern", "Knitterschutz/Ende"}; + +void MieleW433Sensor::setup() { + this->pin_clock_->setup(); + this->store_.pin_clock = this->pin_clock_->to_isr(); + this->pin_clock_->attach_interrupt(MieleW433SensorStore::gpio_intr_clock, &this->store_, gpio::INTERRUPT_RISING_EDGE); + + this->pin_data_->setup(); + this->store_.pin_data = this->pin_data_; + + if (this->pin_en_lamps_ != nullptr) { + this->pin_en_lamps_->setup(); + this->store_.pin_en_lamps = this->pin_en_lamps_->to_isr(); + this->pin_en_lamps_->attach_interrupt(MieleW433SensorStore::gpio_intr_en_lamps, &this->store_, gpio::INTERRUPT_RISING_EDGE); + } + if (this->pin_en_7seg_ != nullptr) { + this->pin_en_7seg_->setup(); + this->store_.pin_en_7seg = this->pin_en_7seg_->to_isr(); + this->pin_en_7seg_->attach_interrupt(MieleW433SensorStore::gpio_intr_en_7seg, &this->store_, gpio::INTERRUPT_RISING_EDGE); + } +} + +void MieleW433Sensor::loop() { + if (millis() - this->last_update_ > 1000) { + uint32_t dpy_7seg; + uint32_t dpy_lamps; + { + InterruptLock lock; + dpy_7seg = this->store_.dpy_7seg; + dpy_lamps = this->store_.dpy_lamps; + } + uint8_t minutes = (dpy_7seg & 0xf) * 60; + minutes += ((dpy_7seg>>8) &0xf); + minutes += ((dpy_7seg>>4) & 0xf) * 10; + if (this->time_remaining_sensor_ != nullptr) { + this->time_remaining_sensor_->publish_state(minutes); + } + + uint8_t state = 0; + bool is_active = false; + + switch(dpy_lamps & 0x1f007) { + case 1<<0: + // Pumpen + state = 5; + is_active = true; + break; + case 1<<1: + // Endschleudern + state = 6; + is_active = true; + break; + case 1<<2: + // Knitterschutz/Ende + state = 7; + break; + case 1<<12: + // Einw./Vorwäsche + state = 1; + is_active = true; + break; + case 1<<13: + // Waschen + state = 2; + is_active = true; + break; + case 1<<14: + // Spülen + state = 3; + is_active = true; + break; + case 1<<15: + // Spülstop + state = 4; + is_active = true; + break; + } + + if (this->current_operation_sensor_ != nullptr) { + this->current_operation_sensor_->publish_state(OPERATION_STATES[state]); + } + + if (this->active_sensor_ != nullptr) { + this->active_sensor_->publish_state(is_active); + } + + this->last_update_ = millis(); + } +} + +void MieleW433Sensor::dump_config() { + LOG_SENSOR("", "Miele W433", this); + LOG_PIN(" Pin Clock: ", this->pin_clock_); + LOG_PIN(" Pin Data: ", this->pin_data_); + LOG_PIN(" Pin Enable 7-Segment: ", this->pin_en_7seg_); + LOG_PIN(" Pin Enable Lamps: ", this->pin_en_lamps_); +}; + +void IRAM_ATTR HOT MieleW433SensorStore::gpio_intr_clock(MieleW433SensorStore *arg) { + if (!arg->pin_en_7seg.digital_read()) { + if (arg->count_cycles_7seg <= 31) { + if (arg->pin_data->digital_read()) { + arg->tmp_7seg |= (1<<(31-arg->count_cycles_7seg)); + } + else { + arg->tmp_7seg &= ~(1<<(31-arg->count_cycles_7seg)); + } + arg->count_cycles_7seg++; + } + } + if (!arg->pin_en_lamps.digital_read()) { + if (arg->count_cycles_lamps <= 31) { + if (arg->pin_data->digital_read()) { + arg->tmp_lamps |= (1<<(31-arg->count_cycles_lamps)); + } + else { + arg->tmp_lamps &= ~(1<<(31-arg->count_cycles_lamps)); + } + arg->count_cycles_lamps++; + } + } +} + +void IRAM_ATTR HOT MieleW433SensorStore::gpio_intr_en_lamps(MieleW433SensorStore *arg) { + if (arg->count_cycles_lamps == 8) { + arg->ctrl_lamps = arg->tmp_lamps>>24; + } + else if (arg->count_cycles_lamps == 24) { + arg->dpy_lamps = arg->tmp_lamps>>8; + } + + arg->count_cycles_lamps = 0; +} + +void IRAM_ATTR HOT MieleW433SensorStore::gpio_intr_en_7seg(MieleW433SensorStore *arg) { + if (arg->count_cycles_7seg == 8) { + arg->ctrl_7seg = arg->tmp_7seg>>24; + } + else if (arg->count_cycles_7seg == 24) { + arg->dpy_7seg = arg->tmp_7seg>>8; + } + arg->count_cycles_7seg = 0; +} + +} // namespace miele_w433 +} // namespace esphome diff --git a/hosts/iron/services/esphome/devices/components/miele_w433/miele_w433.h b/hosts/iron/services/esphome/devices/components/miele_w433/miele_w433.h new file mode 100644 index 0000000..55c1f38 --- /dev/null +++ b/hosts/iron/services/esphome/devices/components/miele_w433/miele_w433.h @@ -0,0 +1,62 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/core/automation.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/text_sensor/text_sensor.h" +#include "esphome/components/binary_sensor/binary_sensor.h" + +namespace esphome { +namespace miele_w433 { + + struct MieleW433SensorStore { + ISRInternalGPIOPin pin_clock; + InternalGPIOPin *pin_data; + ISRInternalGPIOPin pin_en_7seg; + ISRInternalGPIOPin pin_en_lamps; + + static void gpio_intr_clock(MieleW433SensorStore *arg); + static void gpio_intr_en_7seg(MieleW433SensorStore *arg); + static void gpio_intr_en_lamps(MieleW433SensorStore *arg); + + volatile int count_cycles_lamps = 0; + volatile int count_cycles_7seg = 0; + volatile uint32_t tmp_lamps; + volatile uint32_t tmp_7seg; + volatile uint32_t dpy_lamps; + volatile uint8_t ctrl_lamps; + volatile uint32_t dpy_7seg; + volatile uint8_t ctrl_7seg; + }; + + class MieleW433Sensor : public sensor::Sensor, public Component { + public: + void set_pin_clock(InternalGPIOPin *pin_clock) { pin_clock_ = pin_clock; } + void set_pin_data(InternalGPIOPin *pin_data) { pin_data_ = pin_data; } + void set_pin_en_7seg(InternalGPIOPin *pin_en_7seg) { pin_en_7seg_ = pin_en_7seg; } + void set_pin_en_lamps(InternalGPIOPin *pin_en_lamps) { pin_en_lamps_ = pin_en_lamps; } + void setup() override; + void loop() override; + void dump_config() override; + + void set_time_remaining_sensor(sensor::Sensor *sensor) { this->time_remaining_sensor_ = sensor; } + void set_active_sensor(binary_sensor::BinarySensor *sensor) { this->active_sensor_ = sensor; } + void set_current_operation_sensor(text_sensor::TextSensor *sensor) { this->current_operation_sensor_ = sensor; } + + protected: + InternalGPIOPin *pin_clock_; + InternalGPIOPin *pin_data_; + InternalGPIOPin *pin_en_7seg_{nullptr}; + InternalGPIOPin *pin_en_lamps_{nullptr}; + MieleW433SensorStore store_{}; + uint32_t last_update_ = 0; + sensor::Sensor *time_remaining_sensor_{nullptr}; + binary_sensor::BinarySensor *active_sensor_{nullptr}; + text_sensor::TextSensor *current_operation_sensor_{nullptr}; + + //enum ErrorCode { NONE = 0, COMMUNICATION_FAILED, CRC_CHECK_FAILED } error_code_{NONE}; + }; + +} // namespace miele_w433 +} // namespace esphome diff --git a/hosts/iron/services/esphome/devices/components/miele_w433/sensor.py b/hosts/iron/services/esphome/devices/components/miele_w433/sensor.py new file mode 100644 index 0000000..2881286 --- /dev/null +++ b/hosts/iron/services/esphome/devices/components/miele_w433/sensor.py @@ -0,0 +1,88 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +from esphome.components import binary_sensor, sensor, text_sensor +from esphome.const import ( + CONF_ACTIVE, + CONF_BEEPER, + CONF_CLOCK_PIN, + CONF_CURRENT_OPERATION, + CONF_DATA_PIN, + CONF_DURATION, + CONF_ID, + CONF_STATUS, + DEVICE_CLASS_LOCK, + DEVICE_CLASS_PROBLEM, + DEVICE_CLASS_RUNNING, + DEVICE_CLASS_SWITCH, + ICON_ROTATE_RIGHT, + ICON_TIMELAPSE, + UNIT_MINUTE, +) + +CONF_EN_7SEG_PIN = "enable_7segment_pin" +CONF_EN_LAMPS_PIN = "enable_lamps_pin" +CONF_TIME_REMAINING = "time_remaining" +CONF_WATER_INLET = "water_inlet" +CONF_WATER_OUTLET = "water_outlet" +ICON_PLAY = "mdi:play" +ICON_PLAYLIST_PLAY = "mdi:playlist-play" +ICON_WATER_OFF = "mdi:water-off" + +AUTO_LOAD = ["text_sensor", "binary_sensor"] +DEPENDENCIES = [] + +miele_w433_ns = cg.esphome_ns.namespace("miele_w433") +MieleW433Sensor = miele_w433_ns.class_("MieleW433Sensor", sensor.Sensor, cg.Component) + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(MieleW433Sensor), + cv.Required(CONF_CLOCK_PIN): cv.All(pins.internal_gpio_input_pin_schema), + cv.Required(CONF_DATA_PIN): cv.All(pins.internal_gpio_input_pin_schema), + cv.Optional(CONF_EN_7SEG_PIN): pins.internal_gpio_input_pin_schema, + cv.Optional(CONF_EN_LAMPS_PIN): pins.internal_gpio_input_pin_schema, + cv.Optional(CONF_TIME_REMAINING): sensor.sensor_schema( + unit_of_measurement=UNIT_MINUTE, + icon=ICON_TIMELAPSE, + accuracy_decimals=0, + ), + cv.Optional(CONF_ACTIVE): binary_sensor.binary_sensor_schema( + device_class=DEVICE_CLASS_RUNNING, icon=ICON_PLAY + ), + cv.Optional(CONF_CURRENT_OPERATION): text_sensor.text_sensor_schema( + icon=ICON_PLAYLIST_PLAY + ), + # cv.Optional(CONF_WATER_INLET): binary_sensor.binary_sensor_schema( + # device_class=DEVICE_CLASS_PROBLEM, + # icon=ICON_WATER_OFF + # ), + } +).extend(cv.COMPONENT_SCHEMA) + + +async def to_code(config): + var = cg.new_Pvariable( + config[CONF_ID], + ) + await cg.register_component(var, config) + + pin_clock = await cg.gpio_pin_expression(config[CONF_CLOCK_PIN]) + cg.add(var.set_pin_clock(pin_clock)) + pin_data = await cg.gpio_pin_expression(config[CONF_DATA_PIN]) + cg.add(var.set_pin_data(pin_data)) + if CONF_EN_7SEG_PIN in config: + pin_en_7seg = await cg.gpio_pin_expression(config[CONF_EN_7SEG_PIN]) + cg.add(var.set_pin_en_7seg(pin_en_7seg)) + if CONF_TIME_REMAINING in config: + sens = await sensor.new_sensor(config[CONF_TIME_REMAINING]) + cg.add(var.set_time_remaining_sensor(sens)) + if CONF_EN_LAMPS_PIN in config: + pin_en_lamps = await cg.gpio_pin_expression(config[CONF_EN_LAMPS_PIN]) + cg.add(var.set_pin_en_lamps(pin_en_lamps)) + if CONF_ACTIVE in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_ACTIVE]) + cg.add(var.set_active_sensor(sens)) + if CONF_CURRENT_OPERATION in config: + sens = await text_sensor.new_text_sensor(config[CONF_CURRENT_OPERATION]) + cg.add(var.set_current_operation_sensor(sens)) diff --git a/hosts/iron/services/esphome/devices/eingang-deckenleuchte.yaml b/hosts/iron/services/esphome/devices/eingang-deckenleuchte.yaml new file mode 100644 index 0000000..b39f719 --- /dev/null +++ b/hosts/iron/services/esphome/devices/eingang-deckenleuchte.yaml @@ -0,0 +1,25 @@ +<<: !include chinafrickeldeckenleuchte.yaml + +esphome: + name: "eingang-deckenleuchte" + friendly_name: "Eingang Deckenleuchte" + on_boot: + then: + - light.turn_on: + id: ceiling_light + brightness: 50% + color_brightness: 0% + color_temperature: 2700 K + +api: + encryption: + key: !secret apikey_eingang_deckenleuchte + +ota: + - platform: esphome + password: !secret otapass_eingang_deckenleuchte + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de diff --git a/hosts/iron/services/esphome/devices/fussbodenheizung.yaml b/hosts/iron/services/esphome/devices/fussbodenheizung.yaml new file mode 100644 index 0000000..0e9019a --- /dev/null +++ b/hosts/iron/services/esphome/devices/fussbodenheizung.yaml @@ -0,0 +1,195 @@ +esphome: + name: "fussbodenheizung" + friendly_name: "Fußbodenheizung" + +esp8266: + board: d1_mini + framework: + version: recommended + +logger: + +api: + encryption: + key: !secret apikey_fussbodenheizung + +ota: + - platform: esphome + password: !secret otapass_fussbodenheizung + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + enable_on_boot: True + fast_connect: On + +switch: + - platform: gpio + pin: + number: 16 + inverted: true + id: relay_1 + icon: "mdi:electric-switch" + - platform: gpio + pin: + number: 14 + inverted: true + id: relay_2 + icon: "mdi:electric-switch" + - platform: gpio + pin: + number: 12 + inverted: true + id: relay_3 + icon: "mdi:electric-switch" + - platform: gpio + pin: + number: 13 + inverted: true + id: relay_4 + icon: "mdi:electric-switch" + - platform: gpio + pin: + number: 0 + inverted: true + id: relay_5 + icon: "mdi:electric-switch" + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + - platform: dht + model: DHT22 + pin: GPIO2 + temperature: + name: "Temperatur" + id: temperature_local + humidity: + name: "Feuchtigkeit" + id: humidity + accuracy_decimals: 1 + update_interval: 60s + - platform: homeassistant + id: temperature_kitchen + entity_id: sensor.kueche_leiste_temperatur + - platform: homeassistant + id: humidity_kitchen + entity_id: sensor.kueche_leiste_feuchtigkeit + - platform: homeassistant + id: temperature_bathroom + entity_id: sensor.badspiegel_temperatur + - platform: homeassistant + id: humidity_bathroom + entity_id: sensor.badspiegel_feuchtigkeit + - platform: homeassistant + entity_id: sensor.bthome_sensor_e8e8_temperature + id: temperature_bedroom + - platform: homeassistant + entity_id: sensor.bthome_sensor_e8e8_humidity + id: humidity_bedroom + +climate: + - platform: thermostat + name: "Bad" + sensor: temperature_bathroom + humidity_sensor: humidity_bathroom + min_heating_off_time: 1s + min_heating_run_time: 1s + min_idle_time: 30s + heat_action: + - switch.turn_on: relay_1 + idle_action: + - switch.turn_off: relay_1 + default_preset: tag + on_boot_restore_from: memory + preset: + - name: morgens + default_target_temperature_low: 23 °C + - name: tag + default_target_temperature_low: 20 °C + - name: nacht + default_target_temperature_low: 18 °C + - name: abwesend + default_target_temperature_low: 16 °C + - platform: thermostat + name: "Schlafzimmer" + sensor: temperature_bedroom + humidity_sensor: humidity_bedroom + min_heating_off_time: 1s + min_heating_run_time: 1s + min_idle_time: 30s + heat_action: + - switch.turn_on: relay_2 + idle_action: + - switch.turn_off: relay_2 + default_preset: tag + on_boot_restore_from: memory + preset: + - name: tag + default_target_temperature_low: 22 °C + - name: nacht + default_target_temperature_low: 18 °C + - name: abwesend + default_target_temperature_low: 15 °C + - platform: thermostat + name: "West" + sensor: temperature_kitchen + humidity_sensor: humidity_kitchen + min_heating_off_time: 1s + min_heating_run_time: 1s + min_idle_time: 30s + heat_action: + - switch.turn_on: relay_3 + idle_action: + - switch.turn_off: relay_3 + default_preset: tag + on_boot_restore_from: memory + preset: + - name: tag + default_target_temperature_low: 19 °C + - name: nacht + default_target_temperature_low: 17 °C + - name: abwesend + default_target_temperature_low: 16 °C + - platform: thermostat + name: "Mitte" + sensor: temperature_local + humidity_sensor: humidity + min_heating_off_time: 1s + min_heating_run_time: 1s + min_idle_time: 30s + heat_action: + - switch.turn_on: relay_4 + idle_action: + - switch.turn_off: relay_4 + default_preset: tag + on_boot_restore_from: memory + preset: + - name: tag + default_target_temperature_low: 19 °C + - name: nacht + default_target_temperature_low: 17 °C + - name: abwesend + default_target_temperature_low: 16 °C + - platform: thermostat + name: "Ost" + sensor: temperature_local # FIXME + #humidity_sensor: + min_heating_off_time: 1s + min_heating_run_time: 1s + min_idle_time: 30s + heat_action: + - switch.turn_on: relay_5 + idle_action: + - switch.turn_off: relay_5 + default_preset: tag + on_boot_restore_from: memory + preset: + - name: tag + default_target_temperature_low: 21 °C + - name: nacht + default_target_temperature_low: 19 °C + - name: abwesend + default_target_temperature_low: 16 °C diff --git a/hosts/iron/services/esphome/devices/justfile b/hosts/iron/services/esphome/devices/justfile new file mode 120000 index 0000000..68d1c45 --- /dev/null +++ b/hosts/iron/services/esphome/devices/justfile @@ -0,0 +1 @@ +../../../../../modules/esphome/devices/justfile \ No newline at end of file diff --git a/hosts/iron/services/esphome/devices/kueche-leiste.yaml b/hosts/iron/services/esphome/devices/kueche-leiste.yaml new file mode 100644 index 0000000..996c412 --- /dev/null +++ b/hosts/iron/services/esphome/devices/kueche-leiste.yaml @@ -0,0 +1,116 @@ +esphome: + name: "kueche-leiste" + friendly_name: "Küche Leiste" + platformio_options: + board_build.flash_mode: dio + on_boot: + then: + - light.control: + id: led_light + brightness: 50% + color_temperature: 2700 K + +esp32: + board: esp32-c3-devkitm-1 + variant: ESP32C3 + framework: + type: esp-idf + +logger: + +api: + encryption: + key: !secret apikey_kueche_leiste + +ota: + - platform: esphome + password: !secret otapass_kueche_leiste + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + enable_on_boot: True + fast_connect: On + +esp32_ble_tracker: + scan_parameters: + active: false + +bluetooth_proxy: + active: true + +xiaomi_ble: + +output: + - platform: ledc + pin: GPIO0 + id: output_warm + - platform: ledc + pin: GPIO1 + id: output_cold + +light: + - platform: cwww + name: "LED light" + id: led_light + cold_white: output_cold + warm_white: output_warm + cold_white_color_temperature: 6500 K + warm_white_color_temperature: 2700 K + constant_brightness: true + gamma_correct: 0 + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + - platform: dht + model: DHT22 + pin: GPIO5 + temperature: + name: "Temperatur" + id: temperature + humidity: + name: "Feuchtigkeit" + id: humidity + accuracy_decimals: 1 + update_interval: 60s + +binary_sensor: + - platform: gpio + pin: + number: GPIO6 + mode: + input: true + pullup: true + inverted: true + name: "Physical Power Button" + on_press: + then: + - light.toggle: + id: led_light + internal: True + +spi: + - id: spi_bus_main + clk_pin: GPIO21 + mosi_pin: GPIO10 + miso_pin: GPIO20 + +pn532_spi: + spi_id: spi_bus_main + # FIXME: GPIO9 is a strapping pin + cs_pin: GPIO9 + update_interval: 1s + on_tag: + then: + - homeassistant.tag_scanned: !lambda 'return x;' + - switch.turn_on: buzzer + - delay: 250ms + - switch.turn_off: buzzer + +switch: + - platform: gpio + pin: GPIO7 + id: buzzer diff --git a/hosts/iron/services/esphome/devices/kuechentisch.yaml b/hosts/iron/services/esphome/devices/kuechentisch.yaml new file mode 100644 index 0000000..5d0f27a --- /dev/null +++ b/hosts/iron/services/esphome/devices/kuechentisch.yaml @@ -0,0 +1,111 @@ +esphome: + name: "kuechentisch" + friendly_name: "Küchentisch" + on_boot: + then: + - light.turn_on: + id: ceiling_light + brightness: 50% + color_temperature: 2700 K + +esp32: + board: esp32dev + framework: + type: esp-idf + sdkconfig_options: + CONFIG_FREERTOS_UNICORE: y + advanced: + ignore_efuse_mac_crc: true + +logger: + +api: + encryption: + key: !secret apikey_kuechentisch + +ota: + - platform: esphome + password: !secret otapass_kuechentisch + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + fast_connect: true + manual_ip: + static_ip: 10.20.0.10 + gateway: 10.20.0.1 + subnet: 255.255.240.0 + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + +output: + - platform: ledc + pin: GPIO19 + id: output_warm + power_supply: power + min_power: 0.13 + max_power: 0.77 + frequency: 1220Hz + zero_means_zero: true + - platform: ledc + pin: GPIO21 + id: output_cold + power_supply: power + frequency: 1220Hz + zero_means_zero: true + min_power: 0.13 + max_power: 0.76 + + - platform: ledc + pin: GPIO23 + id: output_nightlight + power_supply: power + + - platform: ledc + pin: GPIO33 + id: output_red + power_supply: power + - platform: ledc + pin: GPIO26 + id: output_green + power_supply: power + - platform: ledc + pin: GPIO27 + id: output_blue + power_supply: power + +power_supply: + - id: power + pin: GPIO22 + enable_time: 0s + keep_on_time: 0s + +light: + - platform: monochromatic + name: "night light" + id: night_light + output: output_nightlight + gamma_correct: 0 + on_turn_on: + - light.turn_off: ceiling_light + - platform: cwww + name: "ceiling light" + id: ceiling_light + cold_white: output_cold + warm_white: output_warm + cold_white_color_temperature: 6500 K + warm_white_color_temperature: 2700 K + constant_brightness: true + gamma_correct: 0 + on_turn_on: + - light.turn_off: night_light + - platform: rgb + name: "ambient light" + red: output_red + green: output_green + blue: output_blue + gamma_correct: 0 diff --git a/hosts/iron/services/esphome/devices/led-panel-schreibtisch.yaml b/hosts/iron/services/esphome/devices/led-panel-schreibtisch.yaml new file mode 100644 index 0000000..ceff0b0 --- /dev/null +++ b/hosts/iron/services/esphome/devices/led-panel-schreibtisch.yaml @@ -0,0 +1,74 @@ +esphome: + name: "led-panel-schreibtisch" + friendly_name: "LED Panel Schreibtisch" + platformio_options: + board_build.flash_mode: dio + on_boot: + then: + - light.turn_on: + id: panel + brightness: 50% + color_brightness: 0% + color_temperature: 2700 K + +api: + encryption: + key: !secret apikey_panel_schreibtisch + +ota: + - platform: esphome + password: !secret otapass_panel_schreibtisch + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + power_save_mode: none + enable_on_boot: True + fast_connect: true + manual_ip: + static_ip: 10.20.0.11 + gateway: 10.20.0.1 + subnet: 255.255.240.0 + output_power: 8.5 + +esp32: + board: esp32-c3-devkitm-1 + variant: ESP32C3 + framework: + type: esp-idf + +logger: + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + +output: + - platform: ledc + pin: GPIO1 + id: output_warm + power_supply: power + frequency: 2kHz + - platform: ledc + pin: GPIO3 + id: output_cold + power_supply: power + frequency: 2kHz + +power_supply: + - id: power + pin: GPIO0 + enable_time: 0s + keep_on_time: 0s + +light: + - platform: cwww + name: "panel" + id: panel + cold_white: output_cold + warm_white: output_warm + cold_white_color_temperature: 6500 K + warm_white_color_temperature: 2700 K + gamma_correct: 0 diff --git a/hosts/iron/services/esphome/devices/pflanzenleuchte.yaml b/hosts/iron/services/esphome/devices/pflanzenleuchte.yaml new file mode 100644 index 0000000..4c7af8b --- /dev/null +++ b/hosts/iron/services/esphome/devices/pflanzenleuchte.yaml @@ -0,0 +1,60 @@ +esphome: + name: "pflanzenleuchte" + friendly_name: "Pflanzenleuchte" + platformio_options: + board_build.flash_mode: dio + +esp32: + board: esp32-c3-devkitm-1 + variant: ESP32C3 + framework: + type: esp-idf + +logger: + +api: + encryption: + key: !secret apikey_pflanzenleuchte + +ota: + - platform: esphome + password: !secret otapass_pflanzenleuchte + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + enable_on_boot: True + fast_connect: On + output_power: 8.5 + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + +output: + - platform: ledc + pin: GPIO0 + id: output_royal_blue + inverted: true + min_power: 0.25 + zero_means_zero: true + - platform: ledc + pin: GPIO1 + id: output_deep_red + inverted: true + min_power: 0.25 + zero_means_zero: true + +light: + - platform: monochromatic + name: "Royalblau" + id: royal_blue + output: output_royal_blue + gamma_correct: false + - platform: monochromatic + name: "Tiefrot" + id: deep_red + output: output_deep_red + gamma_correct: false diff --git a/hosts/iron/services/esphome/devices/pinspot.yaml b/hosts/iron/services/esphome/devices/pinspot.yaml new file mode 100644 index 0000000..b7adc49 --- /dev/null +++ b/hosts/iron/services/esphome/devices/pinspot.yaml @@ -0,0 +1,53 @@ +esphome: + name: "pinspot" + friendly_name: "Pinspot" + platformio_options: + board_build.flash_mode: dio + +esp32: + board: esp32-c3-devkitm-1 + variant: ESP32C3 + framework: + type: esp-idf + +logger: + +api: + encryption: + key: !secret apikey_pinspot + +ota: + - platform: esphome + password: !secret otapass_pinspot + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + enable_on_boot: True + fast_connect: On + output_power: 8.5 + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + +output: + - platform: ledc + pin: GPIO1 + id: output_led_brightness + min_power: 0.028 + zero_means_zero: true + - platform: ledc + pin: GPIO4 + inverted: true + id: output_led_colortemp + +light: + - platform: color_temperature + name: "Pinspot" + color_temperature: output_led_colortemp + brightness: output_led_brightness + cold_white_color_temperature: 6000 K + warm_white_color_temperature: 2700 K diff --git a/hosts/iron/services/esphome/devices/shroombox.yaml b/hosts/iron/services/esphome/devices/shroombox.yaml new file mode 100644 index 0000000..4f4de46 --- /dev/null +++ b/hosts/iron/services/esphome/devices/shroombox.yaml @@ -0,0 +1,128 @@ +esphome: + name: "shroombox" + friendly_name: "shroombox" + +esp32: + board: esp-wrover-kit + +api: + encryption: + key: !secret apikey_shroombox + +ota: + - platform: esphome + password: !secret otapass_shroombox + +ethernet: + type: LAN8720 + mdc_pin: GPIO23 + mdio_pin: GPIO18 + clk_mode: GPIO0_IN + phy_addr: 1 + power_pin: GPIO16 + +logger: + +i2c: + sda: GPIO4 + scl: GPIO14 + scan: true + +sensor: + - platform: scd30 + id: scd30_sensor + co2: + name: "Shroombox CO2" + accuracy_decimals: 1 + temperature: + name: "Shroombox Temperature" + accuracy_decimals: 2 + humidity: + name: "Shroombox Humidity" + accuracy_decimals: 1 + id: humidity + address: 0x61 + update_interval: 5s + automatic_self_calibration: false + + - platform: hx711 + name: "Water tank weight" + dout_pin: GPIO32 + clk_pin: GPIO33 + gain: 128 + update_interval: 5s + filters: + - calibrate_linear: + - -35884 -> 0 + - 334800 -> 887 + unit_of_measurement: g + - platform: pulse_counter + pin: + number: GPIO36 + #mode: INPUT_PULLUP + mode: INPUT + unit_of_measurement: 'RPM' + id: fan_rpm + name: Fan Speed + accuracy_decimals: 0 + +output: + - platform: ledc + id: fan_duty + pin: GPIO15 + frequency: "25000 Hz" + min_power: 10% + max_power: 100% + zero_means_zero: true + +fan: + - platform: speed + output: fan_duty + name: "Fan" + id: fan1 + +switch: + - platform: gpio + pin: GPIO2 + id: humidifier + name: "Humidifier" + +number: + - platform: template + name: "CO2 calibration value" + optimistic: true + min_value: 350 + max_value: 4500 + step: 1 + id: co2_cal + icon: "mdi:molecule-co2" + entity_category: "config" + +button: + - platform: template + name: "SCD30 Force manual calibration" + entity_category: "config" + on_press: + then: + - scd30.force_recalibration_with_reference: + value: !lambda 'return id(co2_cal).state;' + +climate: + - platform: thermostat + name: "Humidistat" + sensor: humidity + min_idle_time: 20s + min_heating_off_time: 60s + min_heating_run_time: 60s + visual: + min_temperature: 0 + max_temperature: 100 + preset: + - name: default + mode: heat + default_target_temperature_low: 80 + + heat_action: + - switch.turn_on: humidifier + idle_action: + - switch.turn_off: humidifier diff --git a/hosts/iron/services/esphome/devices/tuerschloss.yaml b/hosts/iron/services/esphome/devices/tuerschloss.yaml new file mode 100644 index 0000000..1560071 --- /dev/null +++ b/hosts/iron/services/esphome/devices/tuerschloss.yaml @@ -0,0 +1,85 @@ +esphome: + name: "tuerschloss" + friendly_name: "Türschloss" + platformio_options: + board_build.flash_mode: dio + +esp32: + board: esp32-c3-devkitm-1 + variant: ESP32C3 + framework: + type: esp-idf + +logger: + +api: + encryption: + key: !secret apikey_tuerschloss + +ota: + - platform: esphome + password: !secret otapass_tuerschloss + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + enable_on_boot: True + fast_connect: On + output_power: 8.5 + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + +output: + - platform: gpio + pin: + number: GPIO0 + inverted: true + mode: + output: true + open_drain: true + id: btn_unlock + - platform: gpio + pin: + number: GPIO1 + inverted: true + mode: + output: true + open_drain: true + id: btn_lock + - platform: gpio + pin: + number: GPIO3 + id: btn_open + +lock: + - platform: template + name: "Türschloss" + id: tuerschloss + assumed_state: true + lock_action: + - output.turn_on: btn_lock + - delay: 250ms + - output.turn_off: btn_lock + - delay: 5s + - lambda: id(tuerschloss).publish_state(LOCK_STATE_LOCKED); + unlock_action: + - output.turn_on: btn_unlock + - delay: 250ms + - output.turn_off: btn_unlock + - delay: 5s + - lambda: id(tuerschloss).publish_state(LOCK_STATE_UNLOCKED); + +button: + - platform: template + name: "Türöffner" + id: tueroeffner + icon: mdi:lock-open + on_press: + - logger.log: "Button pressed" + - output.turn_on: btn_open + - delay: 250ms + - output.turn_off: btn_open diff --git a/hosts/iron/services/esphome/devices/tuersprechanlage.yaml b/hosts/iron/services/esphome/devices/tuersprechanlage.yaml new file mode 100644 index 0000000..d40d2c4 --- /dev/null +++ b/hosts/iron/services/esphome/devices/tuersprechanlage.yaml @@ -0,0 +1,124 @@ +esphome: + name: "tuersprechanlage" + friendly_name: "Türsprechanlage" + +esp32: + board: lolin_s2_mini + variant: ESP32S2 + framework: + type: esp-idf + sdkconfig_options: + CONFIG_ESP_CONSOLE_USB_CDC: y + +logger: + hardware_uart: USB_CDC + +api: + encryption: + key: !secret apikey_tuersprechanlage + +ota: + - platform: esphome + password: !secret otapass_tuersprechanlage + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + enable_on_boot: True + fast_connect: True + power_save_mode: none + output_power: 10 + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + +binary_sensor: + - platform: gpio + name: Etagenklingel + id: floor_bell + pin: + number: GPIO16 + mode: + input: true + pullup: true + filters: + - delayed_off: 10s + + - platform: gpio + name: Treppenlicht + id: staircase_light + pin: + number: GPIO18 + mode: + input: true + pullup: true + filters: + - invert + - delayed_off: 10s + on_press: + then: + - output.turn_on: output_staircase_light_ssr + - delay: 200ms + - output.turn_off: output_staircase_light_ssr + +output: + - platform: gpio + pin: GPIO15 + id: output_door_opener + - platform: ledc + pin: + number: GPIO33 + inverted: true + id: output_staircase_light_ssr + frequency: 50000 + min_power: 0 + max_power: 0.12 + - platform: template + type: binary + id: output_staircase_light_permanent + write_action: + - if: + condition: + lambda: 'return state;' + then: + - script.execute: script_staircase_permanent + else: + - script.stop: script_staircase_permanent + +button: + - platform: template + name: "Türöffner" + id: btn_tueroeffner + icon: mdi:lock-open + on_press: + - output.turn_on: output_door_opener + - delay: 500ms + - output.turn_off: output_door_opener + - platform: template + name: "Treppenlicht" + id: btn_staircase_light + icon: mdi:stairs + on_press: + - output.turn_on: output_staircase_light_ssr + - delay: 200ms + - output.turn_off: output_staircase_light_ssr + +light: + - platform: binary + id: staircase_permanent + name: Treppenlicht permanent + output: output_staircase_light_permanent + +script: + - id: script_staircase_permanent + mode: restart + then: + - while: + condition: + lambda: 'return true;' + then: + - button.press: btn_staircase_light + - delay: 90s diff --git a/hosts/iron/services/esphome/devices/waschmaschine.yaml b/hosts/iron/services/esphome/devices/waschmaschine.yaml new file mode 100644 index 0000000..fad1aea --- /dev/null +++ b/hosts/iron/services/esphome/devices/waschmaschine.yaml @@ -0,0 +1,124 @@ +esphome: + name: "waschmaschine" + friendly_name: "Waschmaschine" + +api: + encryption: + key: !secret apikey_waschmaschine + +ota: + - platform: esphome + password: !secret otapass_waschmaschine + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + +esp32: + board: esp32doit-devkit-v1 + framework: + type: arduino + version: recommended + +logger: + +external_components: + - source: components + components: [ miele_w433 ] + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + - platform: miele_w433 + enable_7segment_pin: 27 + clock_pin: 14 + enable_lamps_pin: 26 + data_pin: 13 + time_remaining: + name: "verbleibende Zeit" + active: + name: "in Betrieb" + current_operation: + name: "aktueller Vorgang" + id: current_operation + +number: + - platform: template + name: "Waschmittelmenge" + id: detergent_dosing + min_value: 25.0 + max_value: 150.0 + step: 1 + unit_of_measurement: "ml" + icon: 'mdi:cup-water' + restore_value: true + initial_value: 75.0 + optimistic: true + - platform: template + name: "Waschmittelvorrat" + id: detergent_supply + min_value: 0.0 + max_value: 5000 + step: 1.0 + unit_of_measurement: "ml" + icon: 'mdi:cup-water' + restore_value: true + initial_value: 0 + optimistic: true + +stepper: + - platform: a4988 + id: detergent_stepper + step_pin: GPIO32 + sleep_pin: GPIO33 + dir_pin: GPIO25 # not used + max_speed: 600 steps/s + acceleration: 125 steps/s^2 + deceleration: 125 steps/s^2 + +globals: + - id: dosing_enabled + type: bool + restore_value: false + initial_value: 'true' + +interval: + - interval: 1s + then: + if: + condition: + and: + #- lambda: return id(current_operation).state == "Einw/Vorwäsche"; + - lambda: return id(current_operation).state == "Waschen"; + - lambda: return id(dosing_enabled); + then: + - lambda: &dosing |- + float dose = id(detergent_dosing).state; + float current_supply = id(detergent_supply).state; + if (current_supply >= dose) { + id(detergent_stepper).set_target(dose * 125); + id(detergent_stepper).report_position(0); + id(detergent_supply).publish_state(current_supply - dose); + ESP_LOGD("custom", "Waschmitteldosierung durchgeführt: %.2f ml, verbleibender Vorrat: %.2f ml", dose, current_supply - dose); + } else { + ESP_LOGW("custom", "Nicht genug Waschmittelvorrat! Aktueller Vorrat: %.2f ml, gewünschte Dosierung: %.2f ml", current_supply, dose); + } + - lambda: |- + id(dosing_enabled) = false; + - interval: 1s + then: + if: + condition: + lambda: return id(current_operation).state == "Knitterschutz/Ende"; + then: + - lambda: |- + id(dosing_enabled) = true; + +button: + - platform: template + name: "Waschmitteldosierung auslösen" + icon: "mdi:cup-water" + on_press: + - lambda: *dosing diff --git a/hosts/iron/services/esphome/devices/water-bottle.yaml b/hosts/iron/services/esphome/devices/water-bottle.yaml new file mode 100644 index 0000000..456b973 --- /dev/null +++ b/hosts/iron/services/esphome/devices/water-bottle.yaml @@ -0,0 +1,238 @@ +substitutions: + tolerance: "10" + minimum_sip: "20" + default_bottle_tare: "198" + +esphome: + name: "water-bottle" + friendly_name: "Water bottle" + +esp32: + board: lolin_s2_mini + variant: ESP32S2 + framework: + type: esp-idf + sdkconfig_options: + CONFIG_ESP_CONSOLE_USB_CDC: y + +logger: + hardware_uart: USB_CDC + +api: + encryption: + key: !secret apikey_water_bottle + on_client_connected: + - light.turn_on: + id: pixels + effect: "scan" + red: 0% + green: 25% + blue: 0% + - delay: 2s + - light.turn_off: + id: pixels + +ota: + - platform: esphome + password: !secret otapass_water_bottle + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + enable_on_boot: True + fast_connect: True + power_save_mode: none + output_power: 10 + +globals: + - id: volume_when_removed + type: int + restore_value: no + initial_value: "NAN" + - id: bottle_returned + type: bool + restore_value: no + initial_value: "false" + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + - platform: hx711 + internal: true + id: scale + dout_pin: GPIO16 + clk_pin: GPIO17 + gain: 128 + update_interval: 0.1s + unit_of_measurement: g + filters: + - quantile: + window_size: 10 + send_every: 5 + send_first_at: 5 + quantile: .9 + - calibrate_linear: + - 197155 -> 0 + - 246676 -> 50 + - platform: copy + name: "absolute volume" + id: volume_absolute + icon: "mdi:water" + source_id: scale + filters: + - lambda: "return x - id(bottle_tare).state;" + - clamp: + min_value: 0 + ignore_out_of_range: true + - quantile: + window_size: 5 + send_every: 5 + send_first_at: 5 + quantile: .9 + on_value: + then: + - if: + condition: + lambda: |- + if (id(bottle_returned)) { + id(bottle_returned) = false; + return true; + } + return false; + then: + - lambda: |- + ESP_LOGI("main", "Bottle returned, waiting to settle"); + ESP_LOGI("main", "Volume absolute: %f", id(volume_absolute).state); + - delay: 3s + - lambda: |- + ESP_LOGI("main", "Bottle settled"); + ESP_LOGI("main", "Volume when removed: %d", id(volume_when_removed)); + ESP_LOGI("main", "Volume absolute: %f", id(volume_absolute).state); + if (id(volume_when_removed) > id(volume_absolute).state + ${minimum_sip}) { + ESP_LOGI("main", "Volume total before: %f", id(volume_total).state); + auto call = id(volume_total).make_call(); + call.set_value(id(volume_when_removed) - id(volume_absolute).state + id(volume_total).state); + call.perform(); + ESP_LOGI("main", "Volume total now: %f", id(volume_total).state); + } + - platform: template + name: "Water consumption rate" + id: "volume_total_derivative" + unit_of_measurement: "ml/h" + icon: "mdi:water-check" + lambda: |- + static float last_value = 0; + static float last_time = 0; + float time = (float) millis(); + if (last_time == 0){ + last_value = id(volume_total).state; + last_time = time; + return {}; + } + float change = ( ( id(volume_total).state - last_value ) / ( time - last_time ) ) *1000*60*60; + last_value = id(volume_total).state; + last_time = time; + return change; + filters: + - sliding_window_moving_average: + window_size: 3 + send_every: 2 + - or: + - delta: 0.01 + - heartbeat: 120minutes + - throttle: 30s + +number: + - platform: template + name: "Bottle tare" + id: bottle_tare + icon: mdi:weight-gram + optimistic: true + initial_value: "${default_bottle_tare}" + unit_of_measurement: "g" + step: 1 + min_value: 0 + max_value: 1000 + - platform: template + name: "total volume" + id: volume_total + optimistic: true + icon: "mdi:water" + unit_of_measurement: "ml" + step: 1 + min_value: 0 + max_value: 10000 + +binary_sensor: + - platform: template + name: "Bottle present" + id: bottle_present + lambda: |- + if (id(scale).state > id(bottle_tare).state - ${tolerance}) { + return true; + } else { + return false; + } + on_release: + then: + - lambda: |- + id(volume_when_removed) = id(volume_absolute).state; + ESP_LOGI("main", "Volume absolute: %f ml", id(volume_absolute).state); + ESP_LOGI("main", "Bottle removed with %d ml", id(volume_when_removed)); + - light.turn_on: + id: pixels + effect: "normal_pulse" + red: 75% + green: 0% + blue: 0% + on_press: + then: + - globals.set: + id: bottle_returned + value: "true" + - if: + condition: + lambda: "return id(volume_absolute).state > 250;" + then: + - light.turn_on: + id: pixels + effect: "normal_pulse" + red: 0% + green: 50% + blue: 50% + else: + - light.turn_on: + id: pixels + effect: "normal_pulse" + red: 50% + green: 25% + blue: 0% + - delay: 2s + - light.turn_off: + id: pixels + +e131: + method: multicast + +light: + - platform: esp32_rmt_led_strip + id: pixels + rgb_order: GRB + pin: GPIO12 + num_leds: 8 + rmt_channel: 0 + chipset: ws2812 + effects: + - addressable_scan: + name: scan + move_interval: 20ms + scan_width: 1 + - pulse: + name: slow_pulse + transition_length: 500ms + update_interval: 10s + - pulse: + name: normal_pulse + min_brightness: 20% diff --git a/hosts/iron/services/esphome/devices/wohnungstuer.yaml b/hosts/iron/services/esphome/devices/wohnungstuer.yaml new file mode 100644 index 0000000..11c0675 --- /dev/null +++ b/hosts/iron/services/esphome/devices/wohnungstuer.yaml @@ -0,0 +1,253 @@ +esphome: + name: "wohnungstuer" + friendly_name: "Wohnungstür" + +api: + encryption: + key: !secret apikey_wohnungstuer + +ota: + - platform: esphome + password: !secret otapass_wohnungstuer + +wifi: + ssid: !secret wifi_ssid_bw + password: !secret wifi_password_bw + domain: .iot.bw.jalr.de + +esp32_ble_tracker: + scan_parameters: + active: false + +bluetooth_proxy: + active: true + +xiaomi_ble: + +esp32: + board: esp32doit-devkit-v1 + framework: + type: arduino + version: recommended + +logger: + +sensor: + - platform: wifi_signal + name: "WiFi Signal Sensor" + update_interval: 60s + +globals: + - id: leaving + type: int + restore_value: no + initial_value: '0' + +binary_sensor: + - platform: template + name: "At home" + id: presence + device_class: presence + - platform: gpio + name: Tür + id: door + pin: + number: GPIO32 + mode: + input: true + pullup: true + filters: + - delayed_on_off: 1s + device_class: door + on_press: # on opening door + then: + - lambda: |- + id(leaving) |= 1; + - if: + condition: + # bowl is occupied + binary_sensor.is_on: key_bowl + then: + - light.turn_on: + id: pixels + effect: strobe_red + - script.execute: + id: beep + on_ms: 250 + off_ms: 250 + + else: + - if: + condition: + binary_sensor.is_off: presence # when away + then: + - light.turn_on: + id: pixels + effect: "None" + red: 75% + green: 25% + + on_release: # on closing door + then: + - if: + condition: + binary_sensor.is_off: presence # when away + then: + - light.turn_on: + id: pixels + effect: "scan" + red: 0% + green: 0% + blue: 100% + - script.execute: + id: beep + on_ms: 100 + off_ms: 1000 + - if: + condition: + lambda: |- + return id(leaving) == 3; + then: + - light.turn_off: + id: pixels + - script.stop: beep + - switch.turn_off: buzzer + - binary_sensor.template.publish: # set away state + id: presence + state: OFF + else: + - if: + condition: + # when keys are in the bowl + binary_sensor.is_on: key_bowl + then: + - script.stop: beep + - switch.turn_off: buzzer + - light.turn_on: + id: pixels + effect: "None" + red: 0% + green: 50% + blue: 0% + - delay: 30s + - light.turn_off: + id: pixels + transition_length: 3s + - lambda: |- + id(leaving) = 0; + - if: + condition: + binary_sensor.is_on: presence # when at home + then: + - light.turn_off: + id: pixels + - script.stop: beep + - switch.turn_off: buzzer + + - platform: gpio + name: Schlüsselschale + id: key_bowl + pin: + number: GPIO23 + mode: + input: true + pullup: true + device_class: occupancy + filters: + - invert: + - delayed_on_off: 250ms + on_press: # when keys fall in bowl + then: + - binary_sensor.template.publish: # set at home state + id: presence + state: ON + - lambda: |- + id(leaving) = 0; + - if: + condition: + # door is closed + binary_sensor.is_off: door + then: + - script.stop: beep + - switch.turn_off: buzzer + - light.turn_on: + id: pixels + effect: "None" + red: 0% + green: 50% + blue: 0% + - delay: 30s + - light.turn_off: + id: pixels + transition_length: 3s + on_release: # when keys are removed from bowl + then: + - if: + condition: + lambda: |- + return id(leaving) == 1; + then: + - light.turn_off: + id: pixels + - script.stop: beep + - switch.turn_off: buzzer + - lambda: |- + id(leaving) |= 2; + - if: + condition: + # door is closed + binary_sensor.is_off: door + then: + - light.turn_on: + id: pixels + effect: "rainbow" + +light: + - platform: esp32_rmt_led_strip + id: pixels + rgb_order: GRB + pin: GPIO12 + num_leds: 5 + rmt_channel: 0 + chipset: ws2812 + effects: + - strobe: + name: strobe_red + colors: + - state: true + brightness: 100% + red: 100% + green: 0% + blue: 0% + duration: 250ms + - state: false + duration: 250ms + - addressable_rainbow: + name: rainbow + speed: 20 + width: 10 + - addressable_scan: + name: scan + move_interval: 20ms + scan_width: 1 + +switch: + - platform: gpio + pin: GPIO22 + id: buzzer + +script: + - id: beep + mode: restart + parameters: + on_ms: int + off_ms: int + then: + - while: + condition: + lambda: 'return true;' + then: + - switch.turn_on: buzzer + - delay: !lambda return on_ms; + - switch.turn_off: buzzer + - delay: !lambda return off_ms; diff --git a/hosts/iron/services/grafana.nix b/hosts/iron/services/grafana.nix new file mode 100644 index 0000000..15e1571 --- /dev/null +++ b/hosts/iron/services/grafana.nix @@ -0,0 +1,146 @@ +{ config +, lib +, pkgs +, ... +}: +let + inherit (config.networking) ports; + domain = "grafana.jalr.de"; + cfg = config.services.grafana; +in +{ + sops.secrets = { + "grafana/secret-key" = { + sopsFile = ../secrets.yaml; + owner = config.systemd.services.grafana.serviceConfig.User; + }; + }; + + services.grafana = { + enable = true; + settings = { + server = { + inherit domain; + root_url = "https://%(domain)s"; + http_addr = "127.0.0.1"; + http_port = ports.grafana.tcp; + }; + security = { + content_security_policy = true; + cookie_samesite = "strict"; + cookie_secure = true; + secret_key = "$__file{${config.sops.secrets."grafana/secret-key".path}}"; + strict_transport_security = true; + strict_transport_security_preload = true; + strict_transport_security_subdomains = true; + }; + analytics = { + reporting_enabled = false; + check_for_updates = false; + check_for_plugin_updates = false; + }; + }; + provision = { + datasources.settings = { + apiVersion = 1; + datasources = with config.services.prometheus; + ( + lib.lists.optional enable { + name = "Prometheus"; + type = "prometheus"; + url = "http://${listenAddress}:${toString port}"; + orgId = 1; + } + ) + ++ (with config.services.prometheus.alertmanager; ( + lib.lists.optional enable { + name = "Alertmanager"; + type = "alertmanager"; + url = "http://${listenAddress}:${toString port}"; + orgId = 1; + } + )); + deleteDatasources = [ + { + name = "Prometheus"; + orgId = 1; + } + { + name = "Alertmanager"; + orgId = 1; + } + ]; + }; + + dashboards.settings.providers = + let + # https://grafana.com/grafana/dashboards/ + fetchDashboard = + { name + , hash + , id + , version + , + }: + pkgs.fetchurl { + inherit name hash; + url = "https://grafana.com/api/dashboards/${toString id}/revisions/${toString version}/download"; + recursiveHash = true; + postFetch = '' + mv "$out" temp + mkdir -p "$out" + mv temp "$out/${name}.json"; + ''; + }; + dashboard = name: fetchArgs: { + inherit name; + options.path = fetchDashboard fetchArgs; + }; + in + [ + (dashboard "Node Exporter Full" + { + name = "node-exporter-full"; + hash = "sha256-QTHG9ioy7E8U8O8x/qFabOxK2qBjlGlzuEvwYKug0CQ="; + id = 1860; + version = 36; + }) + (dashboard "Node Exporter" + { + name = "node-exporter"; + hash = "sha256-2xgE0m3SUFiux501uCVb4aH3zGfapW/SmfxRsFC/514="; + id = 13978; + version = 2; + }) + (dashboard "AlertManager" + { + name = "alertmanager"; + hash = "sha256-Yvw0DGQJpqBYNzE4ES/x7ZAYF7iJ4SUNBKB+sJRuGBw="; + id = 9578; + version = 4; + }) + ]; + }; + }; + services.nginx.virtualHosts = { + "${domain}" = { + enableACME = true; + forceSSL = true; + + locations."/" = { + proxyPass = "http://${cfg.settings.server.http_addr}:${toString cfg.settings.server.http_port}"; + proxyWebsockets = true; + recommendedProxySettings = true; + }; + }; + }; + + environment.persistence."/persist".directories = [ + { + directory = "/var/lib/grafana"; + user = "grafana"; + group = "grafana"; + mode = "u=rwx,g=,o="; + } + ]; +} diff --git a/hosts/iron/services/home-assistant.nix b/hosts/iron/services/home-assistant.nix new file mode 100644 index 0000000..d62f792 --- /dev/null +++ b/hosts/iron/services/home-assistant.nix @@ -0,0 +1,272 @@ +{ lib, pkgs, config, ... }: +let + inherit (config.networking) ports; + interfaces = import ../interfaces.nix; + domain = "hass.jalr.de"; +in +{ + sops.secrets.home-assistant = { + owner = "root"; + group = "hass"; + mode = "0640"; + }; + + networking.firewall.interfaces = { + "${interfaces.lan}".allowedTCPPorts = [ ports.mqtt.tcp ]; + iot.allowedTCPPorts = [ ports.mqtt.tcp ]; + }; + + services = { + home-assistant = { + enable = true; + lovelaceConfig = { + title = "Home"; + views = [ + { + path = "default_view"; + title = "Home"; + cards = [ + { + title = "Eingang"; + type = "entities"; + entities = [ + { + entity = "light.eingang_deckenleuchte_deckenleuchte"; + name = "Deckenleuchte"; + } + ]; + } + { + name = "Esstisch"; + type = "entities"; + entities = [ + { + entity = "light.yeelight_meteorite_ambient_light"; + name = "Ambient light"; + } + { + entity = "light.yeelight_meteorite_ceiling_light"; + name = "Ceiling light"; + } + { + entity = "light.yeelight_meteorite_night_light"; + name = "Night light"; + } + ]; + } + ]; + } + ]; + }; + extraComponents = [ + # See https://www.home-assistant.io/integrations + "bthome" + "caldav" + "esphome" + "local_todo" + "openweathermap" + "wyoming" + "xiaomi_ble" + "vlc_telnet" + ]; + customComponents = with pkgs.home-assistant-custom-components; [ + adaptive_lighting + ]; + customLovelaceModules = with pkgs.home-assistant-custom-lovelace-modules; [ + valetudo-map-card + ]; + lovelaceConfigWritable = false; + configWritable = false; + config = { + http = { + server_host = [ "127.0.0.1" ]; + server_port = ports.home-assistant.tcp; + use_x_forwarded_for = true; + trusted_proxies = [ "127.0.0.1" ]; + }; + homeassistant = { + unit_system = "metric"; + time_zone = "Europe/Berlin"; + temperature_unit = "C"; + inherit (config.location) longitude; + inherit (config.location) latitude; + external_url = "https://${domain}/"; + internal_url = "https://${domain}/"; + }; + default_config = { }; + adaptive_lighting = { + lights = [ + "light.yeelight_meteorite_ceiling_light" + "light.eingang_deckenleuchte_deckenleuchte" + "light.led_panel_schreibtisch_panel" + "light.kueche_leiste_led_light" + "light.badspiegel_background_light" + "light.badspiegel_front_light" + ]; + }; + "automation nix" = [ + { + alias = "Waschmaschine fertig Benachrichtigung"; + trigger = { + platform = "state"; + entity_id = "sensor.waschmaschine_aktueller_vorgang"; + to = "Knitterschutz/Ende"; + }; + action = [ + { + service = "notify.mobile_app_shift6mq"; + data = { + message = "Die Waschmaschine hat das Programm beendet."; + title = "Wäsche fertig"; + }; + } + ]; + } + ]; + "automation ui" = "!include automations.yaml"; + "scene nix" = [ + ]; + "scene ui" = "!include scenes.yaml"; + bluetooth = { }; + device_tracker = [ + { + platform = "bluetooth_le_tracker"; + } + ]; + "script nix" = [ + { + lights_off_except = { + icon = "mdi:home-lightbulb"; + fields.exclude_lights.description = "Excluded lights as list"; + sequence = [ + { + service = "logbook.log"; + data_template = { + entity_id = "script.turn_off_lights"; + name = "Exclude log"; + message = "Turning of all lights except: {{ exclude_lights }}"; + }; + } + { + service = "light.turn_off"; + data_template.entity_id = '' + {{ states.light | rejectattr('entity_id', 'in', exclude_lights) | rejectattr('state', 'in', 'off') | join(',', attribute='entity_id') }} + ''; + } + ]; + }; + } + ]; + "script ui" = "!include scripts.yaml"; + calendar = [ + { + platform = "caldav"; + username = "jalr@jalr.de"; + password = "!secret radicale"; + url = "https://cal.jalr.de/radicale"; + } + ]; + mqtt = { }; + media_player = [ + { + platform = "mpd"; + name = "mpd@iron"; + host = "127.0.0.1"; + } + ]; + }; + }; + + mosquitto = { + enable = true; + persistence = true; + listeners = [ + { + port = ports.mqtt.tcp; + users = { + valetudo = { + passwordFile = config.sops.secrets."mqtt-users/valetudo".path; + acl = [ + "readwrite homeassistant/+/donald/#" + "readwrite valetudo/donald/#" + ]; + }; + home-assistant = { + passwordFile = config.sops.secrets."mqtt-users/home-assistant".path; + acl = [ "readwrite #" ]; + }; + }; + } + ]; + }; + }; + + systemd.services = { + home-assistant.serviceConfig.ExecStartPre = [ + ( + pkgs.writeShellScript "home-assistant-secrets" '' + ln -sf "${config.sops.secrets.home-assistant.path}" "${config.services.home-assistant.configDir}/secrets.yaml" + '' + ) + ]; + hass-vlc = { + script = '' + exec ${pkgs.vlc}/bin/cvlc \ + --no-video \ + -I telnet \ + --telnet-password=vlc \ + --sout='#transcode{acodec=s16le,channels=2,samplerate=48000}:std{access=file,mux=raw,dst=/run/snapserver/hass.fifo}' \ + --aout=none + ''; + wants = [ "snapserver.service" ]; + after = [ "snapserver.service" ]; + serviceConfig = { + BindPaths = [ "/run/snapserver/hass.fifo" ]; + BindReadOnlyPaths = [ "/nix/store" "/etc/ssl/certs" "/etc/static/ssl/certs" "/bin/sh" ]; + CapabilityBoundingSet = ""; + DynamicUser = "true"; + Group = "snapserver"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = lib.mkForce true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "noaccess"; + ProtectSystem = "strict"; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RootDirectory = "/run/hass-vlc"; + RuntimeDirectory = "hass-vlc"; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged" ]; + }; + wantedBy = [ "multi-user.target" ]; + }; + }; + + systemd.tmpfiles.rules = [ + "f ${config.services.home-assistant.configDir}/automations.yaml 0755 hass hass" + "f ${config.services.home-assistant.configDir}/scenes.yaml 0755 hass hass" + ]; + + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + kTLS = true; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString ports.home-assistant.tcp}/"; + recommendedProxySettings = true; + proxyWebsockets = true; + }; + }; +} diff --git a/hosts/iron/services/jellyfin/broflix.svg b/hosts/iron/services/jellyfin/broflix.svg new file mode 100644 index 0000000..7653de9 --- /dev/null +++ b/hosts/iron/services/jellyfin/broflix.svg @@ -0,0 +1,57 @@ + + + + + + + + + unrar and chill + + diff --git a/hosts/iron/services/jellyfin.nix b/hosts/iron/services/jellyfin/default.nix similarity index 71% rename from hosts/iron/services/jellyfin.nix rename to hosts/iron/services/jellyfin/default.nix index 5cebfb1..eb4e918 100644 --- a/hosts/iron/services/jellyfin.nix +++ b/hosts/iron/services/jellyfin/default.nix @@ -1,37 +1,54 @@ -{ lib, pkgs, ... }: +{ config, lib, pkgs, ... }: +let + inherit (config.networking) ports; + logoPng = pkgs.stdenvNoCC.mkDerivation { + name = "broflix.png"; + src = ./broflix.svg; + dontBuild = true; + dontUnpack = true; + installPhase = '' + export PATH="$PATH:${pkgs.lib.makeBinPath [pkgs.imagemagick]}" + convert \ + -background transparent \ + $src \ + -resize 1302x \ + $out + ''; + }; +in { + imports = [ + ./rar2fs.nix + ]; + services.jellyfin = { enable = true; - package = pkgs.jellyfin.override { - jellyfin-web = pkgs.jellyfin-web.overrideAttrs (oa: rec { - version = "10.8.9"; - src = pkgs.fetchFromGitHub { - owner = "jellyfin"; - repo = "jellyfin-web"; - rev = "v${version}"; - sha256 = "hHZ8HVf8fidd5VPs06kB3/BHBHFxoV3fVObBesqfRJo="; - }; - }); - }; }; + systemd.services.jellyfin = { serviceConfig = { ###MemoryDenyWriteExecute = true; BindPaths = [ + "/dev/dri/renderD128" "/var/cache/jellyfin" "/var/lib/jellyfin" ]; BindReadOnlyPaths = [ - "/nix/store" + "/etc/resolv.conf" + "/etc/ssl" + "/etc/static/ssl" "/filebitch/pub/Filme" "/filebitch/pub/Serien" - "/var/lib/qbittorrent/downloads" + "/nix/store" + "/run/opengl-driver" + "/var/lib/qBittorrent/downloads" ]; CapabilityBoundingSet = ""; + DeviceAllow = "/dev/dri/renderD128 rw"; #IPAddressAllow = "localhost"; #IPAddressDeny = "any"; LockPersonality = true; - PrivateDevices = lib.mkForce true; + PrivateDevices = false; PrivateUsers = true; ProtectClock = true; ProtectControlGroups = true; @@ -68,7 +85,7 @@ # add_header X-XSS-Protection "1; mode=block"; # add_header X-Content-Type-Options "nosniff"; location / { - proxy_pass http://127.0.0.1:8096; + proxy_pass http://127.0.0.1:${toString ports.jellyfin.tcp}; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -77,8 +94,17 @@ proxy_set_header X-Forwarded-Host $http_host; proxy_buffering off; } + location = /web/broflix.svg { + alias ${./broflix.svg}; + } + location = /web/assets/img/banner-light.png { + alias ${logoPng}; + } + location = /web/assets/img/banner-dark.png { + alias ${logoPng}; + } location = /web/ { - proxy_pass http://127.0.0.1:8096/web/index.html; + proxy_pass http://127.0.0.1:${toString ports.jellyfin.tcp}/web/index.html; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -87,7 +113,7 @@ proxy_set_header X-Forwarded-Host $http_host; } location /socket { - proxy_pass http://127.0.0.1:8096; + proxy_pass http://127.0.0.1:${toString ports.jellyfin.tcp}; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; diff --git a/hosts/iron/services/jellyfin/rar2fs.nix b/hosts/iron/services/jellyfin/rar2fs.nix new file mode 100644 index 0000000..dc634cf --- /dev/null +++ b/hosts/iron/services/jellyfin/rar2fs.nix @@ -0,0 +1,62 @@ +{ lib, pkgs, ... }: + +let + rar2fs = pkgs.rar2fs.override { unrar = pkgs.unrar_6; }; + rar2fs_mounts = pkgs.writeScriptBin "rar2fs_mounts" (lib.strings.concatLines [ + "#!${pkgs.python3}/bin/python" + (builtins.readFile ./rar2fs_mounts.py) + ]); + rar_path = "/var/lib/qBittorrent/downloads"; + mount_path = "/run/jellyfin/rar2fs"; +in +{ + programs.fuse = { + userAllowOther = true; + mountMax = 1000; + }; + + environment.systemPackages = [ + rar2fs + ]; + + systemd.services.jellyfin-rar2fs = { + after = [ "jellyfin.service" ]; + wantedBy = [ "multi-user.target" ]; + path = [ rar2fs "/run/wrappers/bin" ]; + environment.USER = "jellyfin"; + serviceConfig = { + AmbientCapabilities = "CAP_SYS_ADMIN CAP_SETUID CAP_SETGID"; + CapabilityBoundingSet = "CAP_SYS_ADMIN CAP_SETUID CAP_SETGID"; + DeviceAllow = "/dev/fuse rw"; + ExecStart = "${rar2fs_mounts}/bin/rar2fs_mounts ${rar_path} ${mount_path}"; + Group = "jellyfin"; + IPAddressDeny = "any"; + LockPersonality = true; + NoNewPrivileges = "no"; + PrivateDevices = false; + PrivateMounts = false; + PrivateTmp = false; + PrivateUsers = false; + ProtectClock = true; + ProtectControlGroups = false; # implies MountAPIVFS + ProtectHome = false; + ProtectHostname = true; + ProtectKernelLogs = false; + ProtectKernelModules = false; + ProtectKernelTunables = false; # implies MountAPIVFS + #ProtectProc = "noaccess"; # implies MountAPIVFS + ProtectSystem = false; + RestrictAddressFamilies = "none"; + RestrictNamespaces = true; + RestrictRealtime = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "@mount" + "@setuid" + "umount2" + ]; + User = "jellyfin"; + }; + }; +} diff --git a/hosts/iron/services/jellyfin/rar2fs_mounts.py b/hosts/iron/services/jellyfin/rar2fs_mounts.py new file mode 100644 index 0000000..aacbf38 --- /dev/null +++ b/hosts/iron/services/jellyfin/rar2fs_mounts.py @@ -0,0 +1,112 @@ +from pathlib import Path +import argparse +import errno +import os +import signal +import subprocess +import sys +import time + + +mounts = {} + + +class RarMount: + process = None + + @property + def mountpoint(self): + result = self.mount_root / self.rar_file.relative_to(self.rar_root).parent + return result + + def __init__(self, mount_root: str, rar_file: Path, rar_root: Path): + self.mount_root = mount_root + self.rar_file = rar_file + self.rar_root = rar_root + + os.makedirs(self.mountpoint, exist_ok=True) + + print(f"Mounting '{self.rar_file}' at '{self.mountpoint}'") + self.process = subprocess.Popen( + [ + "rar2fs", + "-f", + "-o", + "auto_unmount", + "-o", + "allow_other", + "--no-inherit-perm", + self.rar_file, + self.mountpoint, + ] + ) + + def __del__(self): + if self.process: + self.process.terminate() + self.process.communicate() + + for i in range(10): + try: + os.rmdir(self.mountpoint) + except FileNotFoundError: + pass + except OSError as ex: + # if ex.errno == errno.ENOEMPTY: + # break + if ex.errno == errno.EBUSY: + time.sleep(1) + raise + else: + break + + for dir in self.mountpoint.relative_to(self.mount_root).parents: + try: + os.rmdir(self.mount_root.joinpath(dir)) + except OSError as ex: + pass + + +def signal_handler(sig, frame): + for rar_file, mount in mounts.items(): + del mount + + sys.exit(0) + + +def parse_args(): + parser = argparse.ArgumentParser( + description="Recursively globs a path containing rar files and mounts them under a given mount path." + ) + + parser.add_argument("rar_path", type=Path, help="Path to the RAR directory") + + parser.add_argument("mount_path", type=Path, help="Path to the mount directory") + + return parser.parse_args() + + +def main(): + args = parse_args() + + if not args.rar_path.is_dir(): + parser.error(f"RAR path '{args.rar_path}' is not a valid directory.") + + signal.signal(signal.SIGINT, signal_handler) + + for rar_file in args.rar_path.rglob("*.rar"): + if rar_file in mounts: + continue + if len(rar_file.parts) >= 2 and rar_file.parts[-2].lower() in ["subs", "proof"]: + continue + + mounts[rar_file] = RarMount(args.mount_path, rar_file, args.rar_path) + + while True: + time.sleep(600) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/hosts/iron/services/mail.nix b/hosts/iron/services/mail.nix new file mode 100644 index 0000000..c5d855e --- /dev/null +++ b/hosts/iron/services/mail.nix @@ -0,0 +1,41 @@ +{ config, ... }: + +let + inherit (config.networking) ports; +in +{ + #sops.secrets."domain_key_jalr.de".owner = "rspamd"; + jalr = { + mailserver = { + enable = true; + fqdn = "hha.jalr.de"; + relayPort = ports.postfix-relay.tcp; + domains = [ + { + domain = "jalr.de"; + enableDKIM = true; + } + { + domain = "fablab-nea.de"; + enableDKIM = false; + } + ]; + users = import ../secrets/mail-users.nix; + messageSizeLimit = 50 * 1024 * 1024; + }; + }; + services.postfix = { + config = { + smtp_bind_address = "159.69.103.126"; + smtp_bind_address_enforce = true; + }; + masterConfig.smtp.args = [ + "-o" + "inet_protocols=ipv4" + ]; + }; + services.nginx.virtualHosts."hha.jalr.de" = { + enableACME = true; + forceSSL = true; + }; +} diff --git a/hosts/iron/services/matrix.nix b/hosts/iron/services/matrix.nix new file mode 100644 index 0000000..aa050d9 --- /dev/null +++ b/hosts/iron/services/matrix.nix @@ -0,0 +1,55 @@ +{ config, pkgs, ... }: + +let + inherit (config.networking) ports; + signalPhoneNumber = "+4915566437153"; + signalUser = "jalr"; +in +{ + sops.secrets.synapse-turn-shared-secret.owner = "matrix-synapse"; + jalr.matrix = { + enable = true; + fqdn = "matrix.jalr.de"; + domain = "jalr.de"; + synapse.port = ports.matrix-synapse.tcp; + turn = { + host = "turn.jalr.de"; + sharedSecretFile = config.sops.secrets.synapse-turn-shared-secret.path; + }; + mautrix-signal = { + enable = true; + port = ports.mautrix-signal.tcp; + settings.bridge = { + permissions = { + "@jalr:jalr.de" = "admin"; + "jalr.de" = "user"; + }; + default_bridge_presence = false; + send_presence_on_typing = false; + }; + }; + }; + + systemd.services.signal-cli-receive = { + description = "Run signal-cli to receive messages"; + serviceConfig = { + Type = "oneshot"; + User = signalUser; + CapabilityBoundingSet = null; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + SystemCallFilter = "@system-service"; + }; + script = "${pkgs.signal-cli}/bin/signal-cli -u ${signalPhoneNumber} receive"; + }; + systemd.timers.signal-cli-receive = { + description = "Run signal-cli to receive messages"; + after = [ "network.target" ]; + wantedBy = [ "timers.target" ]; + timerConfig = { + Persistent = true; + OnCalendar = "*-*-* *:00:00"; + Unit = config.systemd.services.signal-cli-receive.name; + }; + }; +} diff --git a/hosts/iron/services/navidrome.nix b/hosts/iron/services/navidrome.nix index 10f8fad..d001dfb 100644 --- a/hosts/iron/services/navidrome.nix +++ b/hosts/iron/services/navidrome.nix @@ -1,10 +1,13 @@ -{ config, lib, pkgs, utils, ... }: +{ config, lib, pkgs, ... }: + let - port = 4533; + inherit (config.networking) ports; settings = { # https://www.navidrome.org/docs/usage/configuration-options/#available-options Address = "127.0.0.1"; - Port = port; + Port = ports.navidrome.tcp; + DevActivityPanel = false; + MusicFolder = "/var/lib/navidrome/music"; }; passwordEncryptionKeyFile = config.sops.secrets.navidrome-password-encryption-key.path; configFile = (pkgs.formats.json { }).generate "navidrome.json" settings; @@ -15,21 +18,18 @@ let if [ -e "''$password_encryption_key_file" ]; then export ND_PASSWORDENCRYPTIONKEY="$(cat "''$password_encryption_key_file")" fi - exec ${pkgs.navidrome}/bin/navidrome --configfile ${configFile} + exec ${config.services.navidrome.package}/bin/navidrome --configfile ${configFile} ''; in { services.navidrome.enable = true; systemd.services.navidrome = { serviceConfig = { - ExecStart = lib.mkForce "${utils.systemdUtils.lib.makeJobScript "navidrome-start" script} %d"; + ExecStart = lib.mkForce "${pkgs.writeShellScript "navidrome-start" script} %d"; } // lib.attrsets.optionalAttrs (passwordEncryptionKeyFile != null) { LoadCredential = "PasswordEncryptionKey:${passwordEncryptionKeyFile}"; }; }; - sops.secrets.navidrome-password-encryption-key = { - sopsFile = ../secrets.yaml; - }; services.nginx.virtualHosts."navidrome.jalr.de" = { enableACME = true; forceSSL = true; @@ -37,7 +37,7 @@ in extraConfig = '' add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; location / { - proxy_pass http://127.0.0.1:${toString port}; + proxy_pass http://127.0.0.1:${toString ports.navidrome.tcp}; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; diff --git a/hosts/iron/services/nginx.nix b/hosts/iron/services/nginx.nix index bb9d306..6eb78ee 100644 --- a/hosts/iron/services/nginx.nix +++ b/hosts/iron/services/nginx.nix @@ -1,7 +1,13 @@ -{ pkgs, ... }: +{ config, ... }: + +let + inherit (config.networking) ports; +in { services.nginx = { enable = true; + defaultHTTPListenPort = ports.nginx-http.tcp; + defaultSSLListenPort = ports.nginx-https.tcp; recommendedGzipSettings = true; recommendedOptimisation = true; recommendedProxySettings = true; @@ -11,8 +17,4 @@ 80 443 ]; - security.acme = { - acceptTerms = true; - defaults.email = "mail@jalr.de"; - }; } diff --git a/hosts/iron/services/ntp.nix b/hosts/iron/services/ntp.nix new file mode 100644 index 0000000..b10a245 --- /dev/null +++ b/hosts/iron/services/ntp.nix @@ -0,0 +1,12 @@ +{ + services.chrony = { + enable = true; + extraConfig = '' + allow 192.168.42.0/24 + allow 10.20.0.0/22 + leapsectz right/UTC + ''; + enableRTCTrimming = true; + }; + networking.firewall.allowedUDPPorts = [ 123 ]; +} diff --git a/hosts/iron/services/photoprism.nix b/hosts/iron/services/photoprism.nix new file mode 100644 index 0000000..b4dfb05 --- /dev/null +++ b/hosts/iron/services/photoprism.nix @@ -0,0 +1,68 @@ +{ config, lib, pkgs, ... }: +let + domain = "media.weinturm-open-air.de"; + nextcloudDomain = "cloud.weinturm-open-air.de"; + inherit (config.networking) ports; + cfg = config.services.photoprism; + readSecretWrapper = pkgs.writeShellScriptBin "photoprism" '' + export PHOTOPRISM_OIDC_SECRET=$(cat "$CREDENTIALS_DIRECTORY/PHOTOPRISM_OIDC_SECRET_FILE") + + tagline[0]="Dein Blick. Unser Festival." + tagline[1]="Zeig uns das Festival durch deine Linse!" + tagline[2]="Gemeinsam festgehalten – Festivalmomente von euch für alle." + tagline[3]="Mach’s unvergesslich – lade deine Festivalfotos hoch!" + tagline[4]="Die besten Shots kommen von dir – teile sie hier." + tagline[5]="Jede Perspektive zählt – dein Foto, unser Highlight." + tagline[6]="Klick. Hochladen. Festivalgeschichte schreiben." + tagline[7]="Von der Crowd für die Crowd – Festivalfotos zum Verlieben." + tagline[8]="Dein Beitrag zum Festival-Archiv – jetzt Fotos teilen!" + tagline[9]="Weil kein Moment verloren gehen darf – deine Kamera zählt." + + size=''${#tagline[@]} + index=$(($RANDOM % $size)) + export PHOTOPRISM_SITE_CAPTION="''${tagline[$index]}" + + exec ${pkgs.photoprism}/bin/photoprism "$@" + ''; +in +{ + systemd.services.photoprism.serviceConfig.LoadCredential = lib.mkForce "PHOTOPRISM_OIDC_SECRET_FILE:${config.sops.secrets."photoprism/oidc-secret".path}"; + + services.photoprism = { + enable = true; + originalsPath = "/weinturm/photoprism"; + port = ports.photoprism.tcp; + package = readSecretWrapper; + settings = { + PHOTOPRISM_SITE_URL = "https://${domain}/"; + PHOTOPRISM_OIDC_URI = "https://${nextcloudDomain}"; + PHOTOPRISM_OIDC_CLIENT = "WnqjmaPJ5c0dY7KaWmvXVVgJYNjztqTKBZ6Wq6bjYXGOwM2Xuzx2WabFlnJVRCSE"; # Client ID from settings + PHOTOPRISM_OIDC_SCOPES = "openid profile email roles"; + PHOTOPRISM_OIDC_PROVIDER = "Nextcloud"; + PHOTOPRISM_OIDC_ICON = "https://${nextcloudDomain}/apps/theming/image/logo"; + PHOTOPRISM_OIDC_REDIRECT = "true"; + PHOTOPRISM_OIDC_REGISTER = "true"; + PHOTOPRISM_OIDC_USERNAME = "preferred_username"; + PHOTOPRISM_OIDC_WEBDAV = "true"; + PHOTOPRISM_ORIGINALS_LIMIT = toString (20 * 1024); # maximum size of media files in MB + PHOTOPRISM_INDEX_SCHEDULE = "@every 4h"; + PHOTOPRISM_DEFAULT_LOCALE = "de"; + PHOTOPRISM_DEFAULT_TIMEZONE = "Europe/Berlin"; + PHOTOPRISM_SITE_TITLE = "Weinturm Medien"; + PHOTOPRISM_SITE_CAPTION = ""; + PHOTOPRISM_SITE_AUTHOR = "Jugend- und Kultur Förderverein e.V."; + }; + }; + + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + + locations = { + "/" = { + proxyPass = "http://127.0.0.1:${toString cfg.port}"; + proxyWebsockets = true; + }; + }; + }; +} diff --git a/hosts/iron/services/prometheus.nix b/hosts/iron/services/prometheus.nix new file mode 100644 index 0000000..5d571bc --- /dev/null +++ b/hosts/iron/services/prometheus.nix @@ -0,0 +1,207 @@ +{ config +, lib +, pkgs +, ... +}: +let + #domain = ""; + cfg = config.services.prometheus; + mkStaticTargets = targets: lib.singleton { inherit targets; }; + inherit (config.networking) ports; + blackboxRelabelConfig = [ + { + source_labels = [ "__address__" ]; + target_label = "__param_target"; + } + { + source_labels = [ "__param_target" ]; + target_label = "instance"; + } + { + target_label = "__address__"; + replacement = with config.services.prometheus.exporters.blackbox; "${listenAddress}:${toString port}"; + } + ]; +in +{ + #sops.secrets.prometheus-htpasswd = { + # owner = "nginx"; + # sopsFile = ../secrets.yaml; + #}; + + services.prometheus = { + enable = true; + listenAddress = "127.0.0.1"; + #webExternalUrl = "https://${domain}"; + globalConfig = { + scrape_interval = "15s"; + evaluation_interval = "15s"; + }; + extraFlags = [ + "--storage.tsdb.retention.time=90d" + "--web.enable-admin-api" + ]; + scrapeConfigs = [ + { + job_name = "node"; + static_configs = [ + { + targets = with config.services.prometheus.exporters.node; [ + "${listenAddress}:${toString port}" + ]; + } + ]; + relabel_configs = [ + { + source_labels = [ "__address__" ]; + target_label = "instance"; + replacement = config.networking.hostName; + } + ]; + } + { + job_name = "vodafone_station"; + static_configs = mkStaticTargets [ + "127.0.0.1:${toString ports.prometheus-vodafone-station-exporter.tcp}" + ]; + } + { + job_name = "unifi"; + static_configs = mkStaticTargets [ + "${cfg.exporters.unpoller.listenAddress}:${toString cfg.exporters.unpoller.port}" + ]; + } + { + job_name = "blackbox"; + metrics_path = "/probe"; + params.module = [ "http_2xx" ]; + static_configs = [ + { + targets = [ + "https://c58r0l3wtmqltl4y.myfritz.net:44919/" + ]; + } + ]; + relabel_configs = blackboxRelabelConfig; + } + { + job_name = "internet_ip4"; + static_configs = mkStaticTargets [ "1.1.1.1" "8.8.8.8" ]; + metrics_path = "/probe"; + params.module = [ "icmp_ip4" ]; + relabel_configs = blackboxRelabelConfig; + } + { + job_name = "internet_ip6"; + static_configs = mkStaticTargets [ "2606:4700:4700::1111" "2001:4860:4860::8888" ]; + metrics_path = "/probe"; + params.module = [ "icmp_ip6" ]; + relabel_configs = blackboxRelabelConfig; + } + ]; + + exporters = { + node.enable = true; + + blackbox = { + enable = true; + listenAddress = "127.0.0.1"; + + # https://github.com/prometheus/blackbox_exporter/blob/master/CONFIGURATION.md + configFile = pkgs.writeText "prometheus-blackbox-config" (builtins.toJSON { + modules = { + icmp_ip4 = { + prober = "icmp"; + timeout = "5s"; + icmp = { + ip_protocol_fallback = false; + preferred_ip_protocol = "ip4"; + }; + }; + icmp_ip6 = { + prober = "icmp"; + timeout = "5s"; + icmp = { + ip_protocol_fallback = false; + preferred_ip_protocol = "ip6"; + }; + }; + http_2xx = { + prober = "http"; + timeout = "5s"; + http = { + valid_http_versions = [ "HTTP/1.1" "HTTP/2.0" ]; + valid_status_codes = [ ]; # Defaults to 2xx + method = "GET"; + follow_redirects = true; + fail_if_ssl = false; + fail_if_not_ssl = true; + tls_config = { + insecure_skip_verify = false; + }; + preferred_ip_protocol = "ip4"; # defaults to "ip6" + ip_protocol_fallback = false; # no fallback to "ip6" + }; + }; + }; + }); + }; + }; + }; + + /* + */ + # + + systemd.services.prometheus-vodafone-station-exporter = + let + unitName = "prometheus-vodafone-station-exporter"; + in + { + enable = true; + description = "Prometheus Vodafone Station exporter"; + wants = [ "network.target" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + BindReadOnlyPaths = [ + "/nix/store" + "/etc/resolv.conf" + ]; + DynamicUser = "yes"; + ExecStart = lib.strings.concatStringsSep " " [ + "${pkgs.vodafone-station-exporter}/bin/vodafone-station-exporter" + "-web.listen-address" + "127.0.0.1:${toString ports.prometheus-vodafone-station-exporter.tcp}" + "-vodafone.station-url" + "http://192.168.100.1" + "-vodafone.station-password-file" + "\${CREDENTIALS_DIRECTORY}/password" + ]; + LoadCredential = "password:${config.sops.secrets."prometheus/exporters/vodafone-station".path}"; + NoNewPrivileges = true; + PrivateTmp = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX"; + RestrictNamespaces = true; + RootDirectory = "%t/${unitName}"; + RuntimeDirectory = [ unitName ]; + }; + }; + + /* + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + + #basicAuthFile = config.sops.secrets.prometheus-htpasswd.path; + + locations = { + "/".proxyPass = "http://${cfg.listenAddress}:${toString cfg.port}"; + }; + }; + */ +} diff --git a/hosts/iron/services/public-ip-tunnel.nix b/hosts/iron/services/public-ip-tunnel.nix new file mode 100644 index 0000000..d6b4fcc --- /dev/null +++ b/hosts/iron/services/public-ip-tunnel.nix @@ -0,0 +1,50 @@ +{ config, lib, ... }: + +let + inherit (config.networking) ports; + listenPort = ports.wireguard-public-ip-tunnel.udp; + remoteHost = "magnesium.jalr.de"; + remotePort = 51000; + publicKey = "ABZCQfzlHJ1/iNbWFf6jVvdqSmqjxm3w5bpa0SYclBU="; + externalIp = "159.69.103.126"; + rtTable = { + id = 1000; + name = "hetzner-ha"; + }; +in +{ + networking = { + iproute2 = { + enable = true; + rttablesExtraConfig = '' + ${toString rtTable.id} ${rtTable.name} + ''; + }; + firewall.allowedUDPPorts = [ listenPort ]; + wireguard.interfaces.hetzner-ha = + let + addRule = rule: "ip rule add " + rule; + deleteRule = rule: "ip rule delete " + rule; + rules = [ + "from ${externalIp} to 192.168.0.0/16 table main priority 10" + "from ${externalIp} table ${rtTable.name} priority 20" + ]; + in + { + ips = [ "${externalIp}/32" ]; + privateKeyFile = config.sops.secrets.wireguard_key_hetzner-ha.path; + inherit listenPort; + table = rtTable.name; + postSetup = lib.concatLines (map addRule rules); + postShutdown = lib.concatLines (map deleteRule rules); + peers = [{ + inherit publicKey; + endpoint = "${remoteHost}:${toString remotePort}"; + persistentKeepalive = 25; + allowedIPs = [ + "0.0.0.0/0" + ]; + }]; + }; + }; +} diff --git a/hosts/iron/services/radicale.nix b/hosts/iron/services/radicale.nix new file mode 100644 index 0000000..d6cb95d --- /dev/null +++ b/hosts/iron/services/radicale.nix @@ -0,0 +1,57 @@ +{ config, ... }: + +let + inherit (config.networking) ports; +in +{ + sops.secrets.radicale-htpasswd.owner = "nginx"; + + services.nginx.virtualHosts = { + "cal.jalr.de" = { + enableACME = true; + forceSSL = true; + basicAuthFile = config.sops.secrets.radicale-htpasswd.path; + locations = { + "/radicale/" = { + proxyPass = "http://127.0.0.1:${toString ports.radicale.tcp}/"; + recommendedProxySettings = true; + #basicAuthFile = ""; + extraConfig = '' + proxy_set_header X-Script-Name /radicale; + proxy_set_header X-Remote-User $remote_user; + ''; + # proxy_pass_request_headers = on; + # underscores_in_headers = on; + }; + "/.well-known/caldav".return = "301 $scheme://$host:$server_port/radicale"; + "/.well-known/carddav".return = "301 $scheme://$host:$server_port/radicale"; + }; + }; + }; + + services.radicale = { + enable = true; + settings = { + server = { + hosts = "127.0.0.1:${toString ports.radicale.tcp},[::1]:${toString ports.radicale.tcp}"; + ssl = false; + }; + encoding = { + request = "utf-8"; + stock = "utf-8"; + }; + auth = { + type = "http_x_remote_user"; + }; + rights = { + type = "owner_only"; + }; + storage = { + filesystem_folder = "/var/lib/radicale/collections"; + }; + logging = { + level = "warning"; + }; + }; + }; +} diff --git a/hosts/iron/services/remarkable.nix b/hosts/iron/services/remarkable.nix new file mode 100644 index 0000000..dc38c83 --- /dev/null +++ b/hosts/iron/services/remarkable.nix @@ -0,0 +1,45 @@ +{ lib, config, pkgs, ... }: +let + inherit (config.networking) ports; + domain = "rmfakecloud.jalr.de"; + cfg = config.services.rmfakecloud; + mkEnvironment = settings: lib.strings.concatLines ( + lib.attrsets.mapAttrsToList (name: value: "export ${name}='${value}'") settings + ); + managementScript = pkgs.writeShellScriptBin "rmfakecloud" '' + [[ $(id -u) == "rmfakecloud" ]] || exec sudo -u rmfakecloud -- "$0" "$@" + set -a + source "${config.sops.secrets.rmfakecloud.path}" + set +a + ${mkEnvironment cfg.extraSettings} + ''; +in +{ + sops.secrets.rmfakecloud = { + owner = "root"; + group = "root"; + mode = "0400"; + }; + services.rmfakecloud = { + enable = true; + storageUrl = "https://${domain}"; + port = ports.rmfakecloud.tcp; + # see https://ddvk.github.io/rmfakecloud/install/configuration/ + environmentFile = config.sops.secrets.rmfakecloud.path; + extraSettings = { + RM_TRUST_PROXY = "true"; + DATADIR = "/var/lib/rmfakecloud"; + }; + }; + + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + locations."/" = { + proxyPass = "http://127.0.0.1:${toString cfg.port}/"; + recommendedProxySettings = true; + }; + }; + + environment.systemPackages = [ managementScript ]; +} diff --git a/hosts/iron/services/snapcast/bluetooth-sink.nix b/hosts/iron/services/snapcast/bluetooth-sink.nix new file mode 100644 index 0000000..43db142 --- /dev/null +++ b/hosts/iron/services/snapcast/bluetooth-sink.nix @@ -0,0 +1,79 @@ +{ pkgs, ... }: +{ + environment.systemPackages = with pkgs; [ + #bluez-alsa + bluez + bluez-tools + pipewire + wireplumber + ]; + + hardware.bluetooth = { + enable = true; + settings = { + General = { + Enable = "Source,Sink,Media,Socket"; + Class = "0x00041C"; + # Pairing always on + AlwaysPairable = "true"; + # Don't disable discoverability after timeout + DiscoverableTimeout = "0"; + # Faster but uses more power + FastConnectable = "true"; + # Allow repairing of existing devices + JustWorksRepairing = "always"; + # to show battery state + Experimental = true; + }; + }; + }; + + services = { + blueman.enable = true; + ofono.enable = true; + upower.enable = true; + }; + + systemd.services = { + bluetooth-auto-pair = { + wantedBy = [ "bluetooth.service" ]; + after = [ "bluetooth.service" ]; + bindsTo = [ "bluetooth.service" ]; + serviceConfig = { + Type = "simple"; + ExecStart = pkgs.writeShellScript "exec-start" '' + ${pkgs.bluez}/bin/bluetoothctl < /dev/null; do + sleep 1 + done + + echo "Sleeping..." + sleep 5m + + echo "Shutting down Android" + adb -s "$host:$port" shell -- reboot -p + ''; + }; + }; + systemd.timers."whatsapp-${vmName}" = { + description = "Start Android VM to run WhatsApp"; + after = [ "network.target" ]; + wantedBy = [ "timers.target" ]; + timerConfig = { + Persistent = true; + OnCalendar = "*-*-* 2:00:00"; + Unit = "whatsapp@${vmName}.service"; + }; + }; +} diff --git a/hosts/iron/services/wireguard-esphome.nix b/hosts/iron/services/wireguard-esphome.nix new file mode 100644 index 0000000..db8fca9 --- /dev/null +++ b/hosts/iron/services/wireguard-esphome.nix @@ -0,0 +1,21 @@ +{ config, ... }: + +let + inherit (config.networking) ports; + listenPort = ports.wireguard-esphome.udp; +in +{ + networking = { + firewall.allowedUDPPorts = [ listenPort ]; + wireguard.interfaces.esphome = { + ips = [ "10.20.16.1/30" ]; + privateKeyFile = config.sops.secrets."wireguard_key/esphome".path; + inherit listenPort; + peers = [{ + publicKey = "WM5qDvEF+tInUhzzF5msUwBy8WRyQOuxYX7roNcqNRc="; + persistentKeepalive = 120; + allowedIPs = [ "10.20.16.2" ]; + }]; + }; + }; +} diff --git a/hosts/jalr-t520/configuration.nix b/hosts/jalr-t520/configuration.nix index aeaf12c..e10957b 100644 --- a/hosts/jalr-t520/configuration.nix +++ b/hosts/jalr-t520/configuration.nix @@ -1,92 +1,42 @@ -{ config, pkgs, ... }: +{ lib, pkgs, ... }: { imports = [ - ./hardware-configuration.nix - ../../home-manager/users/jalr.nix + ./disko.nix + ../../users/jalr ]; networking = { hostName = "jalr-t520"; - networkmanager.enable = true; useDHCP = false; }; - # List packages installed in system profile. To search, run: - # $ nix search wget - environment.systemPackages = with pkgs; [ - gnome3.adwaita-icon-theme - vesc-tool - ]; - - environment.variables.EDITOR = "nvim"; - - programs.mtr.enable = true; - programs.wireshark.enable = true; - - hardware.bluetooth.enable = true; - - hardware.sane.enable = true; - - services.blueman.enable = true; - - services.udisks2.enable = true; - - services.avahi.enable = true; - services.avahi.nssmdns = true; - - services.udev.extraRules = '' - # 16C0:0442 - Axoloti Core - SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="0442", MODE:="0666" - # 0483:df11 - Axoloti Core (STM32 microcontroller) in DFU mode - SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE:="0666" - - # Samsung A5 - SUBSYSTEM=="usb", ATTRS{idVendor}=="04e8", ATTRS{idProduct}=="6860", GROUP="dialout", MODE="0660" - ''; - - myConfig = { - bootloader = "grub2"; - gui.enable = true; + jalr = { + bootloader = "systemd-boot"; + bluetooth.enable = true; + gui = { + enable = true; + sway.enable = true; + gnome.enable = true; + }; workstation.enable = true; - sdr.enable = true; - libvirt.enable = true; - autologin.enable = true; - autologin.username = "jalr"; }; - networking.wg-quick.interfaces.wgkalle = { - address = [ - "172.16.254.5/24" - "fd00::604:0:0:ac10:fe05/96" - ]; - privateKeyFile = "/root/wireguard-keys/wgkalle"; - listenPort = 51820; - mtu = 1296; - - peers = [ - { - publicKey = "52kAcBdnrFeSuVupHs0u4diUf6tpF8Esy4vzJAlT5Tc="; - endpoint = "78.47.224.233:1194"; - #endpoint = "[2a01:4f8:190:6068::2]:1194"; - persistentKeepalive = 60; - allowedIPs = [ - "0.0.0.0/0" - "::/0" - ]; - } - ]; + hardware = { + cpu.intel.updateMicrocode = true; + firmware = [ pkgs.linux-firmware ]; }; - networking.firewall.allowedUDPPorts = [ - 51820 # wireguard + + powerManagement.cpuFreqGovernor = "performance"; + + environment.systemPackages = with pkgs; [ + intel-media-driver + libva + libva-utils + libva1 ]; - # This value determines the NixOS release from which the default - # settings for stateful data, like file locations and database versions - # on your system were taken. It‘s perfectly fine and recommended to leave - # this value at the release version of the first install of this system. - # Before changing this value read the documentation for this option - # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "22.05"; # Did you read the comment? + hardware.graphics.extraPackages = lib.singleton pkgs.vaapiIntel; + system.stateVersion = "25.05"; } diff --git a/hosts/jalr-t520/disko.nix b/hosts/jalr-t520/disko.nix new file mode 100644 index 0000000..e75beca --- /dev/null +++ b/hosts/jalr-t520/disko.nix @@ -0,0 +1,65 @@ +{ config, ... }: +let + cfg = config.disko; +in +{ + disko.devices = { + disk = { + system = { + type = "disk"; + device = "/dev/disk/by-id/ata-Samsung_SSD_850_EVO_120GB_S21UNSAG200527E"; + content = { + type = "gpt"; + partitions = { + esp = { + type = "EF00"; + size = "1024M"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = [ "uid=0" "gid=0" "fmask=0077" "dmask=0077" "nodev" "nosuid" "noexec" ]; + }; + }; + nixos = { + size = "100%"; + content = { + type = "btrfs"; + extraArgs = [ "-f" ]; + subvolumes = { + "/root" = { + mountpoint = "/"; + mountOptions = [ "compress-force=zstd:1" "noatime" ]; + }; + "/home" = { + mountpoint = "/home"; + mountOptions = [ "compress-force=zstd:1" "noatime" "nodev" "nosuid" ]; + }; + "/home/.snapshots" = { + mountOptions = [ "compress-force=zstd:1" "noatime" "nodev" "nosuid" ]; + }; + "/nix" = { + mountpoint = "/nix"; + mountOptions = [ "compress-force=zstd:1" "noatime" "noatime" "nodev" ]; + }; + }; + postCreateHook = + let + inherit (cfg.devices.disk.system.content.partitions.nixos) device; + in + '' + mountpoint="$(mktemp -d)" + mount "${device}" "$mountpoint" -o subvol=/ + trap 'umount "$mountpoint"; rmdir "$mountpoint"' EXIT + btrfs subvolume snapshot -r $mountpoint/root $mountpoint/root-blank + ''; + }; + }; + }; + }; + }; + }; + }; +} + + diff --git a/hosts/jalr-t520/hardware-configuration.nix b/hosts/jalr-t520/hardware-configuration.nix deleted file mode 100644 index b744c59..0000000 --- a/hosts/jalr-t520/hardware-configuration.nix +++ /dev/null @@ -1,60 +0,0 @@ -{ lib, pkgs, modulesPath, ... }: - -{ - imports = [ - "${modulesPath}/installer/scan/not-detected.nix" - ]; - - boot = { - initrd = { - availableKernelModules = [ - "aes_generic" - "aesni_intel" - "ahci" - "cryptd" - "ehci_pci" - "firewire_ohci" - "i915" - "sd_mod" - "sdhci_pci" - "usb_storage" - "usbhid" - ]; - kernelModules = [ "dm-snapshot" ]; - luks.devices = { - pvcrypt = { - device = "/dev/disk/by-uuid/3a1a8fec-b028-45c0-8432-7fcbe4615f44"; - preLVM = true; - }; - }; - }; - kernelModules = [ "kvm-intel" ]; - }; - - fileSystems = { - "/" = { - device = "/dev/disk/by-uuid/62e31097-dc6e-4f91-a043-1a6b8a154201"; - fsType = "ext4"; - }; - "/boot" = { - device = "/dev/disk/by-uuid/c4df83d7-8985-47df-b5cd-bf18bd490a50"; - fsType = "ext2"; - }; - }; - - boot.loader.grub.devices = [ "/dev/disk/by-id/ata-INTEL_SSDSC2BB016T4_BTWD531101JM1P6HGN" ]; - - nix.settings.max-jobs = 4; - - hardware.cpu.intel.updateMicrocode = true; - - powerManagement.cpuFreqGovernor = "performance"; - - environment.systemPackages = with pkgs; [ - intel-media-driver - libva - libva-utils - libva1 - ]; - hardware.opengl.extraPackages = lib.singleton pkgs.vaapiIntel; -} diff --git a/hosts/magnesium/configuration.nix b/hosts/magnesium/configuration.nix new file mode 100644 index 0000000..626f0b8 --- /dev/null +++ b/hosts/magnesium/configuration.nix @@ -0,0 +1,32 @@ +{ lib, ... }: + +{ + imports = [ + ../../modules/providers/hetzner-cloud.nix + ./services + ../../users/jalr + ./persistence.nix + ./ports.nix + ]; + + networking.hostName = "magnesium"; + + disko.devices.disk.virt.device = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_60640534"; + + security.sudo.wheelNeedsPassword = false; + + systemd.network.networks."10-wan".address = [ + "2a01:4f8:c013:bab7::1/64" + ]; + + nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; + + zramSwap = { + enable = true; + algorithm = "zstd"; + memoryPercent = 60; + priority = 1; + }; + + system.stateVersion = "24.11"; +} diff --git a/hosts/magnesium/persistence.nix b/hosts/magnesium/persistence.nix new file mode 100644 index 0000000..9d8ef4a --- /dev/null +++ b/hosts/magnesium/persistence.nix @@ -0,0 +1,51 @@ +{ config, lib, ... }: +{ + boot.initrd.postDeviceCommands = + let + inherit (config.disko.devices.disk.virt.content.partitions.linux) device; + in + lib.mkAfter '' + mkdir /mnt + mount -t btrfs "${device}" /mnt + btrfs subvolume list -o /mnt/root | cut -f9 -d' ' | while read subvolume; do + btrfs subvolume delete "/mnt/$subvolume" + done + btrfs subvolume delete /mnt/root + btrfs subvolume snapshot /mnt/root-blank /mnt/root + ''; + + services = { + openssh = { + hostKeys = lib.mkForce [{ + path = "/persist/etc/ssh/ssh_host_ed25519_key"; + type = "ed25519"; + }]; + }; + forgejo.stateDir = "/persist/var/lib/forgejo"; + postgresql.dataDir = "/persist/var/lib/postgresql/${config.services.postgresql.package.psqlSchema}"; + }; + + fileSystems."/persist".neededForBoot = true; + environment.persistence."/persist" = { + hideMounts = true; + directories = [ + "/var/lib/acme" + "/var/lib/hedgedoc" + "/var/lib/nixos" + "/var/lib/private/mealie" + "/var/lib/private/ntfy-sh" + { + directory = "/var/lib/private/tandoor-recipes"; + user = "tandoor_recipes"; + group = "tandoor_recipes"; + mode = "u=rwx,g=rx,o="; + } + { + directory = "/var/lib/trilium"; + user = "trilium"; + group = "trilium"; + mode = "u=rwx,g=rx,o="; + } + ]; + }; +} diff --git a/hosts/magnesium/ports.nix b/hosts/magnesium/ports.nix new file mode 100644 index 0000000..fd74f41 --- /dev/null +++ b/hosts/magnesium/ports.nix @@ -0,0 +1,19 @@ +{ custom-utils, ... }: + +{ + config.networking.ports = custom-utils.validatePortAttrset { + coturn-cli.tcp = 5766; + coturn-plain = { tcp = [ 3478 3479 ]; udp = [ 3478 3479 ]; }; + coturn-relay.udp = { from = 49160; to = 49200; }; + coturn-tls = { tcp = [ 5349 5350 ]; udp = [ 5349 5350 ]; }; + forgejo-ssh.tcp = 2022; + hedgedoc.tcp = 3000; + mealie.tcp = 9000; + nginx-http.tcp = 80; + nginx-https.tcp = 443; + ntfy.tcp = 12474; + tandoor.tcp = 9001; + trilium.tcp = 12783; + wireguard-public-ip-tunnel.udp = 51000; + }; +} diff --git a/hosts/magnesium/secrets.yaml b/hosts/magnesium/secrets.yaml new file mode 100644 index 0000000..92851b1 --- /dev/null +++ b/hosts/magnesium/secrets.yaml @@ -0,0 +1,39 @@ +wireguard_key_hetzner-ha: ENC[AES256_GCM,data:HEW+EalHg6/mq7pRKZkasGz0nqbkSppkf0H/uV5QMJnWwKw9a9W21Y77OSw=,iv:OA6yml1T5kVafX0RYd0Es7DHcGjJazUxP2M6a5Pwkag=,tag:lX5UPIseIQ136HLrHbzZyw==,type:str] +turn-static-auth-secret: ENC[AES256_GCM,data:rzhixUemFPwKj1BcVPZd7KtUO9OA6A2R4qEQ1BZGVG0=,iv:uYHYe4Cywxovt3b/Ho1tQVHrpgVic+AKh9AjYMYSZcM=,tag:rr8RW/if06t38GpZCYQB4w==,type:str] +gitlab-runner_fablab-nea-hcloud-labsync: ENC[AES256_GCM,data:+znVO8cQxjDdhch7oUALZvt84iJmWnAx6lTM0+WGkGtaRWTCTPjgnst5waSJpw/Oysrd1PkXZKmLHyHuU7K/CHQij7sWH50G3ZcUum58klJc3dCPztlrLpDVHeSwyYiLpsqkQTfjqLPfrMkxuxBgTEVXlq2ZnFuyOGbFx9hubPxLeyQKakiW3qZWGjbFXYAps7Gl61AVdKJj3y1otX2JbCjG9x2i6FHZpl5ywwQCjKNM,iv:7v+I/oJtWDap6PNIJ4Qm3Si9dGs7a79SaMhnr/tbe1A=,tag:7jgoLtdWAEKMkWoXZ10owA==,type:str] +forgejo-mail: ENC[AES256_GCM,data:eZv9dM0a06wFJaDUZjo=,iv:L32ab5k/AX8HqSACJA5w+WbzLlBijA5++Gcr2SrnYIU=,tag:ddyTXikWTMnxq86IijgyYg==,type:str] +hedgedoc-session-secret: ENC[AES256_GCM,data:AYUiUF7R+5C3F5kNRL0R95e1l3Y59tIP388uY0IYCskBhR0H0XMVvyrX/gIM33Twwkc5it+fQtNPNXsbrAnoKQ==,iv:Q6pDEdFplp845/DCHutwni/g7Ch39pTCvfNs4Eh28CQ=,tag:aqVGs3iThmepT7iJusLOMA==,type:str] +mealie: ENC[AES256_GCM,data:4LlxJjDstTPZCD7Xyb+0CRkeDafP9a9oMuYDnXznINe+LrfkJGKwQIwP0B3VpeMmZ0Rwe7Tvje0ZWySFGADireb2r7TjDyASAoXJDyNNJ8byRc5Zt77zL2dp/W4xVt8WpQvwsXosjDv3NN6we831wWUrfNtp0g34YLqSU3F/9i7AaU7nVKnQ9QtJRVg5O57nhs/ZXopKOBUdiKAmxcl0hNNdQdaQX6xkDCWrV4432IOckqyqEQyd9KeCURuWeTUgPmTmnt9Cj8KkaQ39fd0LAGRjOBsKo4C4,iv:o5BPW4Wcg4KcFkJHc/mdrO4Rh+1nifxulYkF+iM3LEw=,tag:KXwDr3VHxjeHkyo23SPJgA==,type:str] +tandoor: + secret_key: ENC[AES256_GCM,data:8aVuOBljF+vnEXOzi0r2xUtUGlZM50MuBXK70XW78Q9jNq4ZuRciGabnYwFfknb2/tA=,iv:KN7DwcMH5NN5BgWFgO/V1dfSyiHfAM2wS86atYcBdlQ=,tag:wQA7QL8VqP5d4uBSNQLsnQ==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age19qkgfaq08kmyxghet48dq4gxwjuy9zpvuyxys9jkmcqa5634537qlxjcd8 + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHMW5lbkRNQS9Gc0VUWndk + VlJiczFVM1dHUGc2QWZnMHVIVGZzdWJKUUJzCngyNGxaR2JFNG9HbG81c1ZNSlQ3 + MlgvMlNYWVduY1diM3g2U3BiL0J3U2sKLS0tIHBscWxMTzVISkE5WW1CZTNYK1ZM + elNwdVlJS2NCWUlXcEZvZWsvZ29FRnMK/qa6Qj1yQc91PWk9tMKSyFkMfYcHIKpQ + jcPmGWbpi2NPL/F0Xz2X/zQQxWzs9uzlS1VH+y8JRe1EPMYJ78NXZw== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2025-04-17T23:26:44Z" + mac: ENC[AES256_GCM,data:Dl4/6wrIwOsCRK979O9lSKyi4LKAG0CfgTGS3RwNu23MvhhaBNru4P1gPWWu7/YC6ad63Ip/RuVB69A1kUmgrYimZcU6E3iPg7vsqskmTU0caMD54CHemj57EYS7r8tcloBEgkOvM6Vn/Bs1dV1/EKAv9Kr6r4x6xb3UOofDcwM=,iv:pzRSKp3EnUpgMdwLDKrExpEkm+uZbU6/pYkVLbcnjrY=,tag:Z6DIPVcNUa8QihV1lsmUMA==,type:str] + pgp: + - created_at: "2025-04-08T22:53:53Z" + enc: |- + -----BEGIN PGP MESSAGE----- + + hF4DY/xpNY5WhB0SAQdAbrDTh/Nvu8ky1ec34AAkKQcTH1G1nDlUCSfobMQsCmAw + XPI7V41rBAY2m6J1P/0oy9cHVfE/LUi4E/yCgNG7YIGdUbb9x29x7A3uoP1NAhE5 + 0l4BZQGZ+GGa69KZ2mOnWhbKfjtOVNDoaxcpgNWHxrtO35c/tNSCxJ2Uj2Q2u3Nj + +SRaHB3tsF8VL85Tn0FEXSWLzL7SfHj78wvaZ/3AxbqdF7WDJkl1hXEnrf2DjBCC + =Gi/Y + -----END PGP MESSAGE----- + fp: 3044E71E3DEFF49B586CF5809BF4FCCB90854DA9 + unencrypted_suffix: _unencrypted + version: 3.9.4 diff --git a/hosts/magnesium/services/coturn.nix b/hosts/magnesium/services/coturn.nix new file mode 100644 index 0000000..bfd6e4a --- /dev/null +++ b/hosts/magnesium/services/coturn.nix @@ -0,0 +1,110 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.coturn; + fqdn = "turn.jalr.de"; + inherit (config.networking) ports; +in +{ + sops.secrets.turn-static-auth-secret.owner = "turnserver"; + + services.coturn = ( + if ports.coturn-plain.tcp != ports.coturn-plain.udp then builtins.abort "coturn: plain TCP and UDP ports must match." + else if ports.coturn-tls.tcp != ports.coturn-tls.udp then builtins.abort "coturn: TLS TCP and UDP ports must match." + else if lib.lists.length ports.coturn-plain.tcp != 2 then builtins.abort "coturn: exactly two plain ports must be given." + else if lib.lists.length ports.coturn-tls.tcp != 2 then builtins.abort "coturn: exactly two TLS ports must be given." + else { + listening-port = builtins.elemAt ports.coturn-plain.tcp 0; + alt-listening-port = builtins.elemAt ports.coturn-plain.tcp 1; + tls-listening-port = builtins.elemAt ports.coturn-tls.tcp 0; + alt-tls-listening-port = builtins.elemAt ports.coturn-tls.tcp 1; + cli-port = ports.coturn-cli.tcp; + min-port = ports.coturn-relay.udp.from; + max-port = ports.coturn-relay.udp.to; + } + ) // { + enable = true; + + # config adapted from synapse’s turn howto: + # https://github.com/matrix-org/synapse/blob/develop/docs/turn-howto.md + use-auth-secret = true; + realm = fqdn; + # the NixOS module does not support loading the secret from a dedicated file + static-auth-secret-file = config.sops.secrets.turn-static-auth-secret.path; + + no-tcp-relay = true; + + no-cli = true; + + extraConfig = '' + denied-peer-ip=10.0.0.0-10.255.255.255 + denied-peer-ip=192.168.0.0-192.168.255.255 + denied-peer-ip=172.16.0.0-172.31.255.255 + + # https://www.rtcsec.com/article/cve-2020-26262-bypass-of-coturns-access-control-protection/ + no-multicast-peers + denied-peer-ip=0.0.0.0-0.255.255.255 + denied-peer-ip=10.0.0.0-10.255.255.255 + denied-peer-ip=100.64.0.0-100.127.255.255 + denied-peer-ip=127.0.0.0-127.255.255.255 + denied-peer-ip=169.254.0.0-169.254.255.255 + denied-peer-ip=172.16.0.0-172.31.255.255 + denied-peer-ip=192.0.0.0-192.0.0.255 + denied-peer-ip=192.0.2.0-192.0.2.255 + denied-peer-ip=192.88.99.0-192.88.99.255 + denied-peer-ip=192.168.0.0-192.168.255.255 + denied-peer-ip=198.18.0.0-198.19.255.255 + denied-peer-ip=198.51.100.0-198.51.100.255 + denied-peer-ip=203.0.113.0-203.0.113.255 + denied-peer-ip=240.0.0.0-255.255.255.255 + denied-peer-ip=::1 + denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff + denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255 + denied-peer-ip=100::-100::ffff:ffff:ffff:ffff + denied-peer-ip=2001::-2001:1ff:ffff:ffff:ffff:ffff:ffff:ffff + denied-peer-ip=2002::-2002:ffff:ffff:ffff:ffff:ffff:ffff:ffff + denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff + denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff + + user-quota=12 + total-quota=1200 + ''; + }; + + systemd.services.coturn = { + after = [ "acme-finished-${fqdn}.target" ]; + serviceConfig = { + Environment = [ + "CERT_FILE=%d/fullchain.pem" + "KEY_FILE=%d/key.pem" + ]; + LoadCredential = [ + "fullchain.pem:${config.security.acme.certs."${fqdn}".directory}/fullchain.pem" + "key.pem:${config.security.acme.certs."${fqdn}".directory}/key.pem" + ]; + ExecStartPre = lib.singleton "${pkgs.writeShellScript "coturn-setup-tls" '' + cat >> /run/coturn/turnserver.cfg << EOF + cert="$CERT_FILE"; + pkey="$KEY_FILE"; + EOF + ''}"; + }; + }; + + security.acme.certs."${fqdn}".postRun = '' + if systemctl is-active coturn; then + systemctl --no-block restart coturn + fi + ''; + + services.nginx.virtualHosts."${fqdn}" = { + enableACME = true; + forceSSL = true; + }; + + networking.firewall = { + allowedTCPPorts = with cfg; [ listening-port alt-listening-port tls-listening-port alt-tls-listening-port ]; + allowedUDPPorts = with cfg; [ listening-port alt-listening-port tls-listening-port alt-tls-listening-port ]; + allowedUDPPortRanges = lib.singleton ports.coturn-relay.udp; + }; +} diff --git a/hosts/magnesium/services/default.nix b/hosts/magnesium/services/default.nix new file mode 100644 index 0000000..c257730 --- /dev/null +++ b/hosts/magnesium/services/default.nix @@ -0,0 +1,15 @@ +{ + imports = [ + ./coturn.nix + ./forgejo.nix + ./gitlab-runner.nix + ./hedgedoc.nix + ./it-tools.nix + ./mealie.nix + ./ntfy.nix + ./public-ip-tunnel.nix + ./tandoor.nix + ./trilium.nix + ./webserver.nix + ]; +} diff --git a/hosts/magnesium/services/forgejo.nix b/hosts/magnesium/services/forgejo.nix new file mode 100644 index 0000000..4484e16 --- /dev/null +++ b/hosts/magnesium/services/forgejo.nix @@ -0,0 +1,69 @@ +{ config, ... }: +let + domain = "git.jalr.de"; + cfg = config.services.forgejo; + inherit (config.networking) ports; +in +{ + sops.secrets.forgejo-mail.owner = cfg.user; + services.forgejo = { + enable = true; + lfs.enable = true; + secrets.mailer.PASSWD = config.sops.secrets.forgejo-mail.path; + settings = { + DEFAULT.APP_NAME = "jalr's git"; + avatar.DISABLE_GRAVATAR = true; + mailer = { + ENABLED = true; + PROTOCOL = "smtps"; + SMTP_ADDR = "hha.jalr.de"; + FROM = "git@jalr.de"; + USER = "git@jalr.de"; + }; + server = { + DOMAIN = domain; + PROTOCOL = "http+unix"; + ROOT_URL = "https://${domain}/"; + + DISABLE_ROUTER_LOG = true; + OFFLINE_MODE = true; + + BUILTIN_SSH_SERVER_USER = "git"; + START_SSH_SERVER = true; + SSH_PORT = ports.forgejo-ssh.tcp; + SSH_SERVER_HOST_KEYS = "ssh/forgejo.ed25519"; + }; + service = { + DEFAULT_ALLOW_CREATE_ORGANIZATION = false; + DEFAULT_KEEP_EMAIL_PRIVATE = true; + ENABLE_NOTIFY_MAIL = false; + REGISTER_MANUAL_CONFIRM = true; + DISABLE_REGISTRATION = true; + }; + session = { + PROVIDER = "file"; + COOKIE_SECURE = true; + }; + log.level = "Warn"; + }; + dump = { + enable = true; + type = "tar.zst"; + }; + }; + + networking.firewall.allowedTCPPorts = [ cfg.settings.server.SSH_PORT ]; + + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + + locations."/" = { + proxyPass = "http://unix:/run/forgejo/forgejo.sock"; + }; + + extraConfig = '' + client_max_body_size 1G; + ''; + }; +} diff --git a/hosts/magnesium/services/gitlab-runner.nix b/hosts/magnesium/services/gitlab-runner.nix new file mode 100644 index 0000000..ad17690 --- /dev/null +++ b/hosts/magnesium/services/gitlab-runner.nix @@ -0,0 +1,36 @@ +{ config, pkgs, ... }: + +{ + services.gitlab-runner = { + enable = true; + extraPackages = [ + #(pkgs.writeShellScriptBin "docker-machine" '' + # exec ${pkgs.docker-machine-gitlab}/bin/docker-machine --debug "$@" + #'') + pkgs.docker-machine-gitlab + ]; + #settings.log_level = "debug"; + services."fablab-nea-hcloud-labsync" = { + description = "FabLab NEA Hetzner Cloud - labsync image builder"; + limit = 5; + executor = "docker+machine"; + authenticationTokenConfigFile = config.sops.secrets.gitlab-runner_fablab-nea-hcloud-labsync.path; + dockerImage = "quay.io/official-images/alpine:latest"; + dockerPrivileged = true; + registrationFlags = [ + "--docker-tlsverify" + "--machine-idle-nodes 0" + "--machine-idle-scale-factor 0.0" + "--machine-idle-count-min 0" + "--machine-idle-time 900" + "--machine-max-builds 100" + "--machine-machine-driver hetzner" + "--machine-machine-name gitlabrunner-%s" + ] ++ (map (o: "--machine-machine-options=" + o) [ + "hetzner-image=debian-12" + "hetzner-server-type=cx22" + "hetzner-server-location=nbg1" + ]); + }; + }; +} diff --git a/hosts/magnesium/services/hedgedoc.nix b/hosts/magnesium/services/hedgedoc.nix new file mode 100644 index 0000000..d9ee1b8 --- /dev/null +++ b/hosts/magnesium/services/hedgedoc.nix @@ -0,0 +1,50 @@ +{ config, ... }: + +let + domain = "pad.jalr.de"; + cfg = config.services.hedgedoc; + inherit (config.networking) ports; +in +{ + sops.secrets.hedgedoc-session-secret.owner = config.systemd.services.hedgedoc.serviceConfig.User; + services = { + hedgedoc = { + enable = true; + settings = + let + day = 24 * 60 * 60 * 1000; + in + { + inherit domain; + protocolUseSSL = true; + csp.enable = true; + port = ports.hedgedoc.tcp; + db = { + dialect = "postgres"; + host = "/run/postgresql"; + user = "hedgedoc"; + database = "hedgedoc"; + }; + allowEmailRegister = false; + sessionSecret = config.sops.secrets.hedgedoc-session-secret.path; + sessionLife = 90 * day; + }; + }; + postgresql = { + enable = true; + ensureDatabases = [ "hedgedoc" ]; + ensureUsers = [{ + name = "hedgedoc"; + ensureDBOwnership = true; + }]; + }; + nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + + locations."/" = { + proxyPass = "http://${cfg.settings.host}:${toString cfg.settings.port}"; + }; + }; + }; +} diff --git a/hosts/magnesium/services/it-tools.nix b/hosts/magnesium/services/it-tools.nix new file mode 100644 index 0000000..4999bf0 --- /dev/null +++ b/hosts/magnesium/services/it-tools.nix @@ -0,0 +1,12 @@ +{ pkgs, ... }: + +let + domain = "tools.jalr.de"; +in +{ + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + root = "${pkgs.it-tools}/lib"; + }; +} diff --git a/hosts/magnesium/services/mealie.nix b/hosts/magnesium/services/mealie.nix new file mode 100644 index 0000000..a60f80e --- /dev/null +++ b/hosts/magnesium/services/mealie.nix @@ -0,0 +1,33 @@ +{ config, ... }: + +let + domain = "mealie.jalr.de"; + cfg = config.services.mealie; +in +{ + services.mealie = { + enable = true; + credentialsFile = config.sops.secrets.mealie.path; + port = config.networking.ports.mealie.tcp; + listenAddress = "127.0.0.1"; + settings = { + BASE_URL = "https://${domain}"; + ALLOW_SIGNUP = "false"; + SMTP_HOST = "hha.jalr.de"; + SMTP_PORT = 465; + SMTP_FROM_NAME = "mealie"; + SMTP_AUTH_STRATEGY = "TLS"; + SMTP_FROM_EMAIL = "no-reply@jalr.de"; + SMTP_USER = "mealie@jalr.de"; + }; + }; + + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + + locations."/" = { + proxyPass = "http://127.0.0.1:${toString cfg.port}"; + }; + }; +} diff --git a/hosts/magnesium/services/ntfy.nix b/hosts/magnesium/services/ntfy.nix new file mode 100644 index 0000000..16f9129 --- /dev/null +++ b/hosts/magnesium/services/ntfy.nix @@ -0,0 +1,34 @@ +{ config, ... }: +let + cfg = config.services.ntfy-sh; + domain = "ntfy.jalr.de"; + datadir = "/var/lib/ntfy-sh"; + inherit (config.networking) ports; +in +{ + # ntfy access --auth-file /var/lib/private/ntfy-sh/user.db '*' 'up*' write-only + + services.ntfy-sh = { + enable = true; + settings = { + listen-http = "127.0.0.1:${toString ports.ntfy.tcp}"; + base-url = "https://${domain}"; + behind-proxy = true; + #web-root = "disable"; + #auth-default-access = "read-only"; + attachment-cache-dir = "${datadir}/attachments"; + auth-file = "${datadir}/user.db"; + cache-file = "${datadir}/cache-file.db"; + }; + }; + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + kTLS = true; + locations."/" = { + proxyPass = "http://${cfg.settings.listen-http}/"; + recommendedProxySettings = true; + proxyWebsockets = true; + }; + }; +} diff --git a/hosts/magnesium/services/public-ip-tunnel.nix b/hosts/magnesium/services/public-ip-tunnel.nix new file mode 100644 index 0000000..1dee818 --- /dev/null +++ b/hosts/magnesium/services/public-ip-tunnel.nix @@ -0,0 +1,37 @@ +{ config, ... }: + +let + listenPort = ports.wireguard-public-ip-tunnel.udp; + publicKey = "GCmQs7upvDYFueEfqD2yJkkOZg3K7YaGluWWzdjsyTo="; + inherit (config.networking) ports; +in +{ + #boot.kernel.sysctl = { + # "net.ipv4.conf.all.forwarding" = 1; + # "net.ipv4.conf.hetzner-ha.proxy_arp" = 1; + # "net.ipv4.conf.enp1s0.proxy_arp" = 1; + #}; + + networking = { + interfaces = { + hetzner-ha.proxyARP = true; + enp1s0.proxyARP = true; + }; + firewall.allowedUDPPorts = [ listenPort ]; + wireguard.interfaces = { + hetzner-ha = { + ips = [ ]; + privateKeyFile = config.sops.secrets.wireguard_key_hetzner-ha.path; + inherit listenPort; + + peers = [{ + inherit publicKey; + persistentKeepalive = 25; + allowedIPs = [ + "159.69.103.126/32" + ]; + }]; + }; + }; + }; +} diff --git a/hosts/magnesium/services/tandoor.nix b/hosts/magnesium/services/tandoor.nix new file mode 100644 index 0000000..1127cd2 --- /dev/null +++ b/hosts/magnesium/services/tandoor.nix @@ -0,0 +1,47 @@ +{ config, ... }: + +let + domain = "tandoor.jalr.de"; + cfg = config.services.tandoor-recipes; + #recipesDirectory = "/var/lib/private/tandoor-recipes/recipes"; + inherit (config.networking) ports; +in +{ + services.tandoor-recipes = { + enable = true; + port = ports.tandoor.tcp; + extraConfig = { + GUNICORN_MEDIA = "1"; + }; + }; + + systemd.services.tandoor-recipes = { + serviceConfig = { + LoadCredential = [ + "secret_key:${config.sops.secrets."tandoor/secret_key".path}" + ]; + Environment = [ + "SECRET_KEY_FILE=%d/secret_key" + ]; + }; + }; + + #users.groups.tandoor-recipes.members = [ "nginx" ]; + # https://tandoor.jalr.de/media/recipes/c071286f-60b3-45e9-9ac5-f4bb99703c17_11.jpg + + #systemd.services.nginx.serviceConfig.BindReadOnlyPaths = [ recipesDirectory ]; + #users.groups.tandoor-recipes.members = [ "nginx" ]; + + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + + locations = { + "/" = { + proxyPass = "http://127.0.0.1:${toString cfg.port}"; + proxyWebsockets = true; + }; + #"/media/recipes/".alias = recipesDirectory; + }; + }; +} diff --git a/hosts/magnesium/services/trilium.nix b/hosts/magnesium/services/trilium.nix new file mode 100644 index 0000000..9b7adbf --- /dev/null +++ b/hosts/magnesium/services/trilium.nix @@ -0,0 +1,23 @@ +{ config, pkgs, ... }: + +let + domain = "notes.jalr.de"; + inherit (config.networking) ports; +in +{ + services.trilium-server = { + enable = true; + package = pkgs.trilium-next-server; + host = "127.0.0.1"; + port = ports.trilium.tcp; + nginx = { + enable = true; + hostName = domain; + }; + }; + + services.nginx.virtualHosts."${domain}" = { + enableACME = true; + forceSSL = true; + }; +} diff --git a/hosts/magnesium/services/webserver.nix b/hosts/magnesium/services/webserver.nix new file mode 100644 index 0000000..a30a098 --- /dev/null +++ b/hosts/magnesium/services/webserver.nix @@ -0,0 +1,62 @@ +{ config, lib, pkgs, ... }: + +let + domain = "jalr.de"; + matrixDomain = "matrix.jalr.de"; + inherit (config.networking) ports; +in +{ + networking.firewall.allowedTCPPorts = [ ports.nginx-http.tcp ports.nginx-https.tcp ]; + services.nginx = { + enable = true; + defaultHTTPListenPort = ports.nginx-http.tcp; + defaultSSLListenPort = ports.nginx-https.tcp; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + commonHttpConfig = '' + map $scheme $hsts_header { + https "max-age=31536000"; + } + add_header Strict-Transport-Security $hsts_header; + + add_header Referrer-Policy strict-origin; + add_header X-Content-Type-Options nosniff; + add_header X-Frame-Options SAMEORIGIN; + ''; + virtualHosts = { + "${domain}" = { + enableACME = true; + forceSSL = true; + root = pkgs.jalr.contact; + locations = + let + # workaround for nginx dropping parent headers + # see https://github.com/yandex/gixy/blob/master/docs/en/plugins/addheaderredefinition.md + parentHeaders = lib.concatStringsSep "\n" (lib.filter + (lib.hasPrefix "add_header ") + (lib.splitString "\n" config.services.nginx.commonHttpConfig)); + in + { + "=/.well-known/matrix/server".extraConfig = '' + ${parentHeaders} + add_header Content-Type application/json; + return 200 '${builtins.toJSON { + "m.server" = "${matrixDomain}:443"; + }}'; + ''; + "=/.well-known/matrix/client".extraConfig = '' + ${parentHeaders} + add_header Content-Type application/json; + add_header Access-Control-Allow-Origin *; + return 200 '${builtins.toJSON { + "m.homeserver"."base_url" = "https://${matrixDomain}"; + "org.matrix.msc3575.proxy"."url" = "https://${matrixDomain}"; + }}'; + ''; + }; + }; + }; + }; +} diff --git a/hosts/weinturm-pretix-prod/configuration.nix b/hosts/weinturm-pretix-prod/configuration.nix deleted file mode 100644 index eee18ec..0000000 --- a/hosts/weinturm-pretix-prod/configuration.nix +++ /dev/null @@ -1,33 +0,0 @@ -{ ... }: { - imports = [ - ./hardware-configuration.nix - ../../home-manager/users/jalr.nix - ./services - ]; - - networking = { - hostName = "weinturm-pretix-prod"; - interfaces.ens3.ipv6.addresses = [{ - address = "2a01:4f8:1c1e:ed47::"; - prefixLength = 64; - }]; - defaultGateway6 = { - address = "fe80::1"; - interface = "ens3"; - }; - }; - - zramSwap = { - enable = true; - algorithm = "zstd"; - memoryPercent = 60; - numDevices = 1; - priority = 1; - }; - - security.sudo.wheelNeedsPassword = false; - - services.netdata.enable = true; - - system.stateVersion = "22.11"; -} diff --git a/hosts/weinturm-pretix-prod/hardware-configuration.nix b/hosts/weinturm-pretix-prod/hardware-configuration.nix deleted file mode 100644 index 6679bdf..0000000 --- a/hosts/weinturm-pretix-prod/hardware-configuration.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ modulesPath, ... }: -{ - imports = [ (modulesPath + "/profiles/qemu-guest.nix") ]; - boot.loader.grub.device = "/dev/sda"; - boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "xen_blkfront" "vmw_pvscsi" ]; - boot.initrd.kernelModules = [ "nvme" ]; - fileSystems."/" = { device = "/dev/sda1"; fsType = "ext4"; }; -} diff --git a/hosts/weinturm-pretix-prod/secrets.yaml b/hosts/weinturm-pretix-prod/secrets.yaml deleted file mode 100644 index 9e263b4..0000000 --- a/hosts/weinturm-pretix-prod/secrets.yaml +++ /dev/null @@ -1,33 +0,0 @@ -pretix-cfg: ENC[AES256_GCM,data:AgT82cee/BHR5V2JkNdDkbS82zXntOD6dLuEm4XUgal0Jpz+3ACqrEF72U0nNTd/JUYVD1HJtphKKKLZ0zHBix+xMJ/JP5hnz7O94+xqyjODhl6XpNtKgc2bDsPWpoejeUDAQK4lWDwu7eCl3+L/HCK5oEE7d01HcfudI1XD8Qr6E4PAyrKd71d65h66OtcwgQVojtWbMSditWHEubVQEssrmZGjOzmd/JzlUBEKQ5piJquQ0RTTGynQdKpLw29CakjpxVzT1uLvuvM89I21BDeXW2A6Gn54ay7Ov9aFGbp+wexlZqGpOZ0Pkw==,iv:3CWknBvAAt0Ls45kzAaeXSsiebkWWT2UdJhoyImVoHU=,tag:yy26Txkzf/Yof+Y8R5LcJA==,type:str] -pretix-banktool-cfg: ENC[AES256_GCM,data:6tcaQwnXsA2jZYQD2BdGu7gVzlCE+cF03icOF7VIVY92+xWMw+aivJRocDQAMBslD04EoEAu74tNIcky23swtgJwbtcwSmouDqofzo/HoXrudNyvjECYd6xUzmq/OvSBSQVY3s3OxCcc0TlpPVUipDxcGaALwJdhtCB11ZrVfOoXrycZ3YLAEESK0rC4tc5E550lhxmkyOan1m9q9rI4KVhv4mIlU6nVQbDA7LcljRPAJESFmwuwx75t6uPYKH9CjaYL3XE/MmZIZ6pN3qiNyqEx8dsxWZh1KQI233TfMr6jcIXwamTFK6/yieE2nQJfyM8r8b3E3D4nJ5+nn5Cb+wxwewEPveEmFUsNXYD84fpk2fK0nvYyOi32++gfefObFVE2ejAE7iu3rIrboA/wJoSfPCDGHVtdLVXC//MjLI3JUAU+QMTwRFz53KhE+lxFsbqaX4IdLuUwMoFSW6qRWGe7/SGUChH1Wrj705MMc4dfn2qaGhDbZNkDJA==,iv:Pl8XpuGcFzLlzEVe+JQ02S2rthKz88QMZXkGEywpCTU=,tag:beSgKDPcErhJhoV59YDIsw==,type:str] -sops: - kms: [] - gcp_kms: [] - azure_kv: [] - hc_vault: [] - age: - - recipient: age1w42q9qg7l6gea36erhw0u7jvlpenvtrjm38q4ux0aasa929hes6s2ecj6m - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWeTl6WjVObjAxMTU2QWUz - VzNFYkg0VEd0WkZhL21zYjJCaHZ3emU5UmdrCnZaTmpleC9BNEpFYkl0RnRrNDdP - d2FpMWo4amxsa1RTVEJJSXh6RzJxbkUKLS0tIHl1YjlQaUtEbzNVcll1eHEzK2dL - N2VMRTNjR1RQVm00YnlpbVBzSmZPRkUKv7LCrjyKb4z0e4yBdzwRR5+ErQYHzZCv - +j8j4EuhA6NwsTydgIjueuORbrX/c6VxcgQwRd9En+vQVYhWhlu5Xw== - -----END AGE ENCRYPTED FILE----- - lastmodified: "2023-03-01T13:25:37Z" - mac: ENC[AES256_GCM,data:WcF4i8b+YpJuZj/hP8SEEvXJNlrf77ymNF6Avg4vt2JUkIoLh5EAMOjqPWWhJXS65rRSOCQOW/uRLoAMs3b1lX8r93u1wlzxnF5L/1RnAyTcCI2Aiadq6QjOKevgRwfc4vvTVN7LHKwZ9f8kCqgYiuOYtVDx3N4UPQ4SPJ3MZRw=,iv:iliNHU5y+YL2hpvWIltkhP6bkUonMakL7Ssdyf/be38=,tag:4YO93pGujwpHWjX5IAOQfw==,type:str] - pgp: - - created_at: "2023-02-23T00:30:25Z" - enc: | - -----BEGIN PGP MESSAGE----- - - hF4D3ylLYNOsO+0SAQdA2SmHfeFrNINSLf2aLONZeidpLaCScS7zmWq0YaeM/SUw - 66MK2BqgIxX81M9lIexCXdQ9EVS1p0KGQ2dw0CpAN07qdDqqOnJeedgv9zZ3trwU - 0l4BwoXSnuKxaLDs7vq6y9xrzyKZS5Mx8H7BxVRg0o1mAvSwFez23DmDQWnJyUgO - otTg9fp217ldr3VNwKIYtoO+1floZtbfmoH2EhZhpml36mz1oRCUUJvjQO++EpJW - =N9AT - -----END PGP MESSAGE----- - fp: 66FB54F6081375106EEBF651A222365EB448F934 - unencrypted_suffix: _unencrypted - version: 3.7.3 diff --git a/hosts/weinturm-pretix-prod/services/default.nix b/hosts/weinturm-pretix-prod/services/default.nix deleted file mode 100644 index 731194c..0000000 --- a/hosts/weinturm-pretix-prod/services/default.nix +++ /dev/null @@ -1,5 +0,0 @@ -{ - imports = [ - ./pretix.nix - ]; -} diff --git a/hosts/weinturm-pretix-prod/services/pretix.nix b/hosts/weinturm-pretix-prod/services/pretix.nix deleted file mode 100644 index 35cfbd3..0000000 --- a/hosts/weinturm-pretix-prod/services/pretix.nix +++ /dev/null @@ -1,23 +0,0 @@ -{ config, pkgs, ... }: -{ - services.pretix = { - enable = true; - instanceName = "Weinturm Open Air"; - domain = "tickets.weinturm-open-air.de"; - enableTls = true; - enableRegistration = false; - passwordReset = true; - locale = "de"; - timezone = "Europe/Berlin"; - secretsFile = ../secrets.yaml; - banktool = { - enable = true; - days = 14; - }; - }; - - security.acme = { - acceptTerms = true; - defaults.email = "helfer@weinturm-open-air.de"; - }; -} diff --git a/justfile b/justfile index b44ce6d..d1d2a9a 100644 --- a/justfile +++ b/justfile @@ -1,14 +1,34 @@ +usb_ram_disk := "/dev/disk/by-label/RAM_USB" +usb_ram_mountpoint := shell("findmnt -n -o TARGET $1 || true", usb_ram_disk) + boot: - nixos-rebuild boot --flake . --use-remote-sudo - which fwupdmgr >/dev/null 2>&1 && fwupdmgr update || true + nixos-rebuild boot --flake . --use-remote-sudo + which fwupdmgr >/dev/null 2>&1 && fwupdmgr update || true switch: - nixos-rebuild switch --flake . --use-remote-sudo - which fwupdmgr >/dev/null 2>&1 && fwupdmgr update || true + nixos-rebuild switch --flake . --use-remote-sudo + which fwupdmgr >/dev/null 2>&1 && fwupdmgr update || true build: - nixos-rebuild build --flake . + nixos-rebuild build --flake . update: - nix flake update --commit-lock-file - which fwupdmgr >/dev/null 2>&1 && fwupdmgr refresh || true + nix flake update --commit-lock-file + which fwupdmgr >/dev/null 2>&1 && fwupdmgr refresh || true + +repl: + nix repl --expr "\ + let \ + flake = builtins.getFlake \"$(git rev-parse --show-toplevel)\"; in \ + flake // (with flake; { \ + lib = inputs.nixpkgs.lib; \ + pkgs = inputs.nixpkgs.legacyPackages."\${builtins.currentSystem}".extend(import ./pkgs inputs); \ + }) \ + " + +luks-pass host: + @if [ -d "{{usb_ram_mountpoint}}" ]; then \ + gpg -d hosts/{{host}}/luks-passfile.gpg > "{{usb_ram_mountpoint}}/{{host}}.key"; \ + else \ + echo "Mount point not found. Is the usb device plugged and mounted?" >&2; \ + fi diff --git a/modules/adb.nix b/modules/adb.nix new file mode 100644 index 0000000..47198c1 --- /dev/null +++ b/modules/adb.nix @@ -0,0 +1,5 @@ +{ config, lib, ... }: + +lib.mkIf config.jalr.gui.enable { + programs.adb.enable = true; +} diff --git a/modules/autologin.nix b/modules/autologin.nix index a2b4440..b0b4576 100644 --- a/modules/autologin.nix +++ b/modules/autologin.nix @@ -1,10 +1,10 @@ { config, lib, pkgs, ... }: let - cfg = config.myConfig; + cfg = config.jalr; in { - options.myConfig = { + options.jalr = { autologin = { enable = pkgs.lib.mkEnableOption "Enable tty1 autologin"; username = pkgs.lib.mkOption { diff --git a/modules/avahi.nix b/modules/avahi.nix new file mode 100644 index 0000000..c12bd75 --- /dev/null +++ b/modules/avahi.nix @@ -0,0 +1,42 @@ +{ config, lib, pkgs, ... }: + +lib.mkIf config.jalr.gui.enable { + services.avahi = { + enable = true; + package = + let + xmltoman = pkgs.xmltoman.overrideAttrs (_: { + nativeBuildInputs = [ + pkgs.installShellFiles + ]; + buildInputs = [ + (pkgs.perl.withPackages (pl: [ + pl.XMLParser + ])) + ]; + }); + in + pkgs.avahi.overrideAttrs (o: rec { + version = "0.9-rc2"; + src = pkgs.fetchurl { + url = "https://github.com/avahi/avahi/archive/refs/tags/v${version}.tar.gz"; + sha256 = "sha256-9k7+1qlyz5LLLfs1q/aqkXPWK4Q7FYUML0CvdqQjj4o="; + }; + patches = [ ]; + buildInputs = o.buildInputs ++ [ pkgs.systemdLibs ]; + nativeBuildInputs = o.nativeBuildInputs ++ [ xmltoman ]; + installFlags = [ + "runstatedir=${placeholder "out"}/run" + "sysconfdir=${placeholder "out"}/etc" + ]; + }); + nssmdns4 = true; + extraConfig = '' + [server] + ratelimit-interval-usec=500000 + ratelimit-burst=500 + [wide-area] + enable-wide-area=no + ''; + }; +} diff --git a/modules/aws.nix b/modules/aws.nix index e2cc715..2b6eddb 100644 --- a/modules/aws.nix +++ b/modules/aws.nix @@ -1,10 +1,10 @@ { lib, ... }: { - options.myConfig.aws = { + options.jalr.aws = { enable = lib.mkEnableOption "Enable AWS CLI"; accounts = with lib; mkOption { - type = with types; attrsOf (submodule ({ config, name, ... }: { + type = with types; attrsOf (submodule (_: { options = { sso_account_id = mkOption { type = int; diff --git a/modules/bluetooth.nix b/modules/bluetooth.nix new file mode 100644 index 0000000..6c478b7 --- /dev/null +++ b/modules/bluetooth.nix @@ -0,0 +1,20 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.jalr; +in +{ + options.jalr = { + bluetooth.enable = pkgs.lib.mkEnableOption "Enable bluetooth" // { default = false; }; + }; + config = lib.mkIf cfg.bluetooth.enable { + hardware.bluetooth.enable = true; + services = { + blueman.enable = true; + ofono.enable = true; + upower.enable = true; + }; + hardware.bluetooth.settings.General.Experimental = true; # to show battery state + }; +} + diff --git a/modules/bootloader/default.nix b/modules/bootloader/default.nix index ade474c..bcb2908 100644 --- a/modules/bootloader/default.nix +++ b/modules/bootloader/default.nix @@ -1,15 +1,16 @@ -{ config, lib, pkgs, ... }: +{ lib, ... }: { - options.myConfig = { + options.jalr = { bootloader = lib.mkOption { - type = lib.types.nullOr (lib.types.enum [ "systemd-boot" "grub2" ]); + type = lib.types.nullOr (lib.types.enum [ "systemd-boot" "grub2" "lanzaboote" ]); default = null; description = "Bootloader to install"; }; }; imports = [ - ./systemd-boot.nix ./grub2.nix + ./lanzaboote.nix + ./systemd-boot.nix ]; } diff --git a/modules/bootloader/grub2.nix b/modules/bootloader/grub2.nix index dbceb03..2037be7 100644 --- a/modules/bootloader/grub2.nix +++ b/modules/bootloader/grub2.nix @@ -1,8 +1,7 @@ -{ config, lib, pkgs, ... }: +{ config, lib, ... }: -lib.mkIf (config.myConfig.bootloader == "grub2") { +lib.mkIf (config.jalr.bootloader == "grub2") { boot.loader.grub = { enable = true; - version = 2; }; } diff --git a/modules/bootloader/lanzaboote.nix b/modules/bootloader/lanzaboote.nix new file mode 100644 index 0000000..92f0820 --- /dev/null +++ b/modules/bootloader/lanzaboote.nix @@ -0,0 +1,8 @@ +{ config, lib, ... }: + +lib.mkIf (config.jalr.bootloader == "lanzaboote") { + boot.lanzaboote = { + enable = true; + pkiBundle = "/etc/secureboot"; + }; +} diff --git a/modules/bootloader/systemd-boot.nix b/modules/bootloader/systemd-boot.nix index 26ade2b..995bb0b 100644 --- a/modules/bootloader/systemd-boot.nix +++ b/modules/bootloader/systemd-boot.nix @@ -1,6 +1,6 @@ -{ config, lib, pkgs, ... }: +{ config, lib, ... }: -lib.mkIf (config.myConfig.bootloader == "systemd-boot") { +lib.mkIf (config.jalr.bootloader == "systemd-boot") { boot = { loader = { systemd-boot = { diff --git a/modules/debug.nix b/modules/debug.nix new file mode 100644 index 0000000..9cc70b5 --- /dev/null +++ b/modules/debug.nix @@ -0,0 +1,16 @@ +{ config, lib, ... }: +let + cfg = config.jalr.debug; +in +{ + options.jalr.debug = { + enable = lib.mkEnableOption "debugging helpers, DO NOT USE IN PRODUCTION!"; + }; + + config = lib.mkIf cfg.enable { + services.getty.autologinUser = "root"; + boot.initrd.systemd.emergencyAccess = true; + systemd.enableEmergencyMode = true; + boot.kernelParams = [ "systemd.setenv=SYSTEMD_SULOGIN_FORCE=1" ]; + }; +} diff --git a/modules/default.nix b/modules/default.nix index e654b5e..cd6a1cf 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -1,48 +1,79 @@ { lib, ... }: { - options.myConfig = { - gui.enable = lib.mkEnableOption "GUI"; + options.jalr = { + gui = { + enable = lib.mkEnableOption "GUI"; + sway.enable = lib.mkEnableOption "sway"; + gnome.enable = lib.mkEnableOption "gnome"; + }; workstation.enable = lib.mkEnableOption "Workstation"; }; imports = [ ../pkgs/modules.nix + ./adb.nix ./autologin.nix + ./avahi.nix ./aws.nix + ./bluetooth.nix ./bootloader - ./dji-goggles.nix - ./dnsmasq.nix + ./debug.nix + ./dns.nix + ./esphome ./fish.nix ./fonts.nix + ./gnome.nix ./journald.nix ./kdeconnect.nix ./kvm-switch-enable-screen.nix ./libvirt.nix ./localization.nix + ./luksusb.nix + ./mailserver + ./matrix + ./mobile-network.nix ./mute-indicator.nix + ./neo.nix + ./networking ./nix.nix ./obs.nix ./pipewire.nix ./podman.nix ./printers - ./qbittorrent - ./sdr.nix + ./remarkable.nix ./sshd.nix + ./steelseries-nova-pro.nix ./sudo.nix ./sway.nix - ./tor.nix - ./tradebyte - ./udmx.nix + ./udev.nix ./uefi.nix ./unfree.nix - ./wireshark.nix + ./upgrade-diff.nix + ./wireshark ./yubikey-gpg.nix ]; config = { - boot.cleanTmpDir = true; + boot = { + tmp.cleanOnBoot = true; + kernel.sysctl = { + "kernel.kptr_restrict" = 1; + "kernel.yama.ptrace_scope" = 1; + "kernel.kexec_load_disabled" = 1; + }; + kernelParams = [ + "lockdown=integrity" + ]; + }; - security.polkit.enable = true; + programs.nano.enable = false; + + security.acme = { + acceptTerms = true; + defaults = { + email = "security@jalr.de"; + }; + }; }; } diff --git a/modules/dji-goggles.nix b/modules/dji-goggles.nix deleted file mode 100644 index 00a735b..0000000 --- a/modules/dji-goggles.nix +++ /dev/null @@ -1,6 +0,0 @@ -{ - services.udev.extraRules = '' - # DJI Goggles - SUBSYSTEM=="usb", ATTRS{idVendor}=="2ca3", ATTRS{idProduct}=="001f", GROUP="video", MODE="0660" - ''; -} diff --git a/modules/dns.nix b/modules/dns.nix new file mode 100644 index 0000000..a15d066 --- /dev/null +++ b/modules/dns.nix @@ -0,0 +1,50 @@ +{ lib, config, ... }: + +let + dnscryptListenAddress = "127.0.0.1"; + dnscryptListenPort = 9053; +in +{ + config = lib.mkIf config.jalr.workstation.enable { + services.dnscrypt-proxy2 = { + enable = true; + settings = { + ipv6_servers = true; + require_dnssec = true; + require_nolog = true; + require_nofilter = true; + dnscrypt_ephemeral_keys = true; + tls_disable_session_tickets = true; + listen_addresses = [ "${dnscryptListenAddress}:${toString dnscryptListenPort}" ]; + anonymized_dns.skip_incompatible = true; + }; + }; + services.dnsmasq = { + enable = true; + resolveLocalQueries = true; + settings = { + server = [ + "/iceportal.de/172.18.0.1" + "/lab.fablab-nea.de/192.168.94.1" + "/iot.bw.jalr.de/192.168.42.1" + "/lan.bw.jalr.de/192.168.42.1" + "/lechner.zz/192.168.0.1" + "/login.wifionice.de/172.18.0.1" + "${dnscryptListenAddress}#${toString dnscryptListenPort}" + ]; + address = [ + "/localhost/127.0.0.1" + ]; + no-resolv = true; + interface = "lo"; + listen-address = [ + "::1" + "127.0.0.1" + ]; + bind-interfaces = true; + dns-loop-detect = true; + neg-ttl = 5; + }; + }; + }; +} diff --git a/modules/dnsmasq.nix b/modules/dnsmasq.nix deleted file mode 100644 index 665ae54..0000000 --- a/modules/dnsmasq.nix +++ /dev/null @@ -1,24 +0,0 @@ -{ lib, config, ... }: - -{ - config = lib.mkIf config.myConfig.workstation.enable { - services.dnsmasq = { - enable = true; - resolveLocalQueries = true; - servers = [ - "127.0.0.1#9053" - "/lechner.zz/192.168.0.1" - "/lab.fablab-nea.de/192.168.94.1" - ]; - extraConfig = '' - no-resolv - interface=lo - listen-address=::1 - listen-address=127.0.0.1 - bind-interfaces - dns-loop-detect - neg-ttl=5 - ''; - }; - }; -} diff --git a/modules/esphome/default.nix b/modules/esphome/default.nix new file mode 100644 index 0000000..4edf71e --- /dev/null +++ b/modules/esphome/default.nix @@ -0,0 +1,100 @@ +{ lib +, pkgs +, config +, ... +}: +let + cfg = config.jalr.esphome; + esphomeParams = + if config.services.esphome.enableUnixSocket + then "--socket /run/esphome/esphome.sock" + else "--address ${config.services.esphome.address} --port ${toString config.services.esphome.port}"; +in +{ + options.jalr.esphome = with lib; with lib.types; { + enable = mkEnableOption "ESPHome"; + port = mkOption { + description = "TCP port for esphome dashboard."; + type = port; + }; + configDir = mkOption { + type = path; + description = "Location of the device configuration"; + }; + secretsFile = mkOption { + type = path; + description = "Location of the secrets file"; + }; + }; + + config = lib.mkIf cfg.enable { + services.esphome = { + enable = true; + address = "127.0.0.1"; + inherit (cfg) port; + package = pkgs.master.esphome; + }; + + systemd.services.esphome = { + environment = { + "PLATFORMIO_CORE_DIR" = lib.mkForce "/var/cache/esphome/.platformio"; + }; + serviceConfig = { + BindReadOnlyPaths = [ + "/nix/store" + "%d/secrets.yaml:/run/esphome/config/secrets.yaml" + "/etc/resolv.conf" + "/etc/ssl" + "/etc/static/ssl" + ]; + TemporaryFileSystem = [ + "/var/lib" + ]; + ExecPaths = [ + "-+/var/cache/esphome/.platformio/packages/" + ]; + DeviceAllow = [ + "char-ttyACM rw" + "char-ttyAMA rw" + "char-ttyUSB rw" + ]; + ExecStartPre = [ + (pkgs.writeShellScript "esphome-exec-start-pre" '' + if ! [ -d "$CACHE_DIRECTORY/.platformio/packages" ]; then + mkdir -p "$CACHE_DIRECTORY/.platformio/packages" + exit 1 + fi + mkdir -p "$CACHE_DIRECTORY/.esphome" + for linked in \ + .esphome \ + .gitignore + do + ln -s "$CACHE_DIRECTORY/$linked" "/run/esphome/config/$linked" + done + ${pkgs.rsync}/bin/rsync \ + -a \ + --delete \ + --checksum \ + --exclude secrets.yaml \ + --exclude=.esphome \ + --exclude=.platformio \ + --exclude=.gitignore \ + '${cfg.configDir}/' /run/esphome/config/ + '') + ]; + ExecStart = lib.mkForce "${config.services.esphome.package}/bin/esphome dashboard ${esphomeParams} /run/esphome/config"; + LoadCredential = "secrets.yaml:${cfg.secretsFile}"; + PrivateTmp = true; + RootDirectory = "%t/esphome/chroot"; + RuntimeDirectory = [ + "esphome/chroot" + "esphome/config" + ]; + StateDirectory = lib.mkForce [ ]; + CacheDirectory = "esphome"; + SupplementaryGroups = [ "dialout" ]; + WorkingDirectory = lib.mkForce "/run/esphome/config"; + }; + }; + }; +} diff --git a/modules/esphome/devices/justfile b/modules/esphome/devices/justfile new file mode 100644 index 0000000..2c86b69 --- /dev/null +++ b/modules/esphome/devices/justfile @@ -0,0 +1,17 @@ +import '../../../../../justfile' +set dotenv-load + +download: + rsync \ + -r \ + --rsync-path='sudo rsync' \ + --exclude '/build' \ + --exclude '/.esphome' \ + --exclude '.gitignore' \ + --exclude 'secrets.yaml' \ + ${ESPHOME_HOST}:/var/lib/esphome/ ./ + +download-secrets: + umask 0077 && ssh ${ESPHOME_HOST} sudo cat /run/secrets/esphome > "/dev/shm/${ESPHOME_SECRETS_FILE}" + ln -sf "/dev/shm/${ESPHOME_SECRETS_FILE}" "{{justfile_directory()}}/secrets.yaml" + diff --git a/modules/fonts.nix b/modules/fonts.nix index 2cf6898..2225251 100644 --- a/modules/fonts.nix +++ b/modules/fonts.nix @@ -2,8 +2,11 @@ { console.font = "Lat2-Terminus16"; - fonts.fonts = with pkgs; lib.mkIf config.myConfig.gui.enable [ - (nerdfonts.override { fonts = [ "Iosevka" ]; }) + fonts.packages = with pkgs; lib.mkIf config.jalr.gui.enable [ + nerd-fonts.fira-code + nerd-fonts.iosevka + nerd-fonts.iosevka-term + nerd-fonts.iosevka-term-slab font-awesome powerline-fonts roboto diff --git a/modules/gnome.nix b/modules/gnome.nix new file mode 100644 index 0000000..1a6b784 --- /dev/null +++ b/modules/gnome.nix @@ -0,0 +1,32 @@ +{ config, lib, ... }: + +lib.mkIf (config.jalr.gui.enable && config.jalr.gui.gnome.enable) { + services.xserver = { + enable = true; + desktopManager.gnome.enable = true; + displayManager.gdm = { + enable = true; + autoSuspend = false; + }; + exportConfiguration = true; + }; + + /* + programs.dconf = { + enable = true; + profiles = { + user.databases = [{ + settings = with lib.gvariant; { + "org/gnome/desktop/input-sources" = { + sources = [ + (mkTuple [ "xkb" "de" ]) + (mkTuple [ "xkb" "de+neo" ]) + (mkTuple [ "xkb" "us" ]) + ]; + }; + }; + }]; + }; + }; + */ +} diff --git a/modules/journald.nix b/modules/journald.nix index c6f1271..e527dc6 100644 --- a/modules/journald.nix +++ b/modules/journald.nix @@ -1,5 +1,3 @@ -{ lib, ... }: - { services.journald.extraConfig = '' MaxRetentionSec=90day diff --git a/modules/kdeconnect.nix b/modules/kdeconnect.nix index a8d1d82..96b3016 100644 --- a/modules/kdeconnect.nix +++ b/modules/kdeconnect.nix @@ -1,11 +1,12 @@ -{ config, lib, pkgs, ... }: +{ config, lib, ... }: -let portRange = { - from = 1714; - to = 1764; -}; +let + portRange = { + from = 1714; + to = 1764; + }; in -lib.mkIf config.myConfig.gui.enable { +lib.mkIf config.jalr.gui.enable { programs.kdeconnect.enable = true; networking.firewall.allowedTCPPortRanges = [ portRange ]; networking.firewall.allowedUDPPortRanges = [ portRange ]; diff --git a/modules/libvirt.nix b/modules/libvirt.nix index 93f8443..b93f8c8 100644 --- a/modules/libvirt.nix +++ b/modules/libvirt.nix @@ -1,16 +1,16 @@ { config, lib, pkgs, ... }: let - cfg = config.myConfig; + cfg = config.jalr; in { - options.myConfig = { + options.jalr = { libvirt = { enable = pkgs.lib.mkEnableOption "Enable libvirt"; }; }; config = lib.mkIf cfg.libvirt.enable { - environment.systemPackages = with pkgs; lib.mkIf config.myConfig.gui.enable [ + environment.systemPackages = with pkgs; lib.mkIf config.jalr.gui.enable [ spice-gtk virt-manager ]; diff --git a/modules/localization.nix b/modules/localization.nix index 3c3d813..0034c23 100644 --- a/modules/localization.nix +++ b/modules/localization.nix @@ -1,3 +1,4 @@ +{ config, ... }: { i18n = { defaultLocale = "en_GB.UTF-8"; @@ -8,7 +9,7 @@ console.keyMap = "neo"; - time.timeZone = "UTC"; + time.timeZone = if config.jalr.workstation.enable then "Europe/Berlin" else "UTC"; location = { latitude = 49.5; diff --git a/modules/luksusb.nix b/modules/luksusb.nix new file mode 100644 index 0000000..315c6a0 --- /dev/null +++ b/modules/luksusb.nix @@ -0,0 +1,121 @@ +{ config, lib, ... }: +let + cfg = config.jalr.luksUsbUnlock; +in +{ + options.jalr.luksUsbUnlock = with lib; with lib.types; { + enable = mkEnableOption "unlock LUKS volumes with a USB device on boot"; + devices = mkOption { + default = { }; + example = { + cryptroot = { + keyPath = "/path/to/the/key"; + usbDevice = "by-label/MY_USB"; + }; + }; + type = types.attrsOf (types.submodule { + options = { + keyPath = mkOption { + example = "/mykey.key"; + description = mdDoc '' + Path to the key file inside the USB device's filesystem. + `/` is relative to the device's filesystem root. + ''; + type = types.str; + }; + + usbDevice = mkOption { + example = "by-label/BOOTKEY"; + description = mdDoc '' + Path to the USB device that contains the keys. (Path relative to `/dev/disk/`) + ''; + type = types.str; + }; + + waitForDevice = mkOption { + default = 5; + example = 10; + description = mdDoc '' + How many seconds to wait for the USB device to be detected by the + kernel. + ''; + type = types.ints.unsigned; + }; + }; + }); + }; + }; + config = lib.mkIf cfg.enable ( + let + makeUsbDevPath = usbDevice: "/dev/disk/" + usbDevice; + makeMountPath = usbDevice: "/key/" + (builtins.hashString "md5" usbDevice); + usbFsType = "vfat"; + + mapAttrsNameValue = f: set: + lib.listToAttrs (map f (lib.attrsToList set)); + in + { + boot.initrd = { + kernelModules = [ "uas" "usbcore" "usb_storage" "vfat" "nls_cp437" "nls_iso8859_1" ]; + systemd.services = + let + makeService = name: { usbDevice, waitForDevice, ... }: + let + usbDevPath = makeUsbDevPath usbDevice; + usbMountPath = makeMountPath usbDevice; + in + { + description = "Mount ${name} key"; + wantedBy = [ "cryptsetup.target" ]; + before = [ "systemd-cryptsetup@${name}.service" ]; + after = [ "systemd-modules-load.service" ]; + unitConfig.DefaultDependencies = "no"; + serviceConfig.Type = "oneshot"; + + script = '' + if awk -v mountpoint="${usbMountPath}" '$2==mountpoint {f=1} END {exit !f}' /proc/mounts; then + exit 0 + fi + + attempts=0 + while [ ! -e ${lib.escapeShellArg usbDevPath} ]; do + sleep 1 + if [ $attempts -ge ${toString waitForDevice} ]; then + break; + fi + attempts=$((attempts+1)) + done + + if [ -e ${lib.escapeShellArg usbDevPath} ]; then + mkdir -m0500 -p ${lib.escapeShellArg usbMountPath} + mount \ + -n \ + -t ${lib.escapeShellArg usbFsType} \ + -o ro,fmask=0137,dmask=0027 \ + ${lib.escapeShellArg usbDevPath} \ + ${lib.escapeShellArg usbMountPath} + fi + ''; + }; + in + mapAttrsNameValue + ({ name, value }: { + name = "luksusb-${name}"; + value = makeService name value; + }) + cfg.devices; + + luks.devices = builtins.mapAttrs + (_: { keyPath, usbDevice, ... }: + let + usbMountPath = makeMountPath usbDevice; + in + { + keyFile = "${usbMountPath}/${keyPath}"; + keyFileTimeout = 1; + }) + cfg.devices; + }; + } + ); +} diff --git a/modules/mailserver/default.nix b/modules/mailserver/default.nix new file mode 100644 index 0000000..59a8e2c --- /dev/null +++ b/modules/mailserver/default.nix @@ -0,0 +1,112 @@ +{ config, lib, ... }: +let + cfg = config.jalr.mailserver; +in +{ + options.jalr.mailserver = with lib; with lib.types; { + enable = mkEnableOption "simple mail server"; + relayPort = mkOption { + description = "SMTP port for relay mail relay."; + type = port; + default = 25; + }; + fqdn = mkOption { + type = str; + description = '' + FQDN of the mail server + + It needs to have a matching reverse DNS record. + By default, an acme certificate with this name has to be present. + See `certDir` for more details. + ''; + example = "mail.example.com"; + }; + storageDir = mkOption { + type = path; + description = "Location of the storage directory for mails"; + default = "/var/vmail"; + }; + domains = mkOption { + type = listOf (submodule { + options = { + domain = mkOption { + type = str; + description = "Domain to serve"; + example = [ "example.com" "example.org" ]; + }; + enableDKIM = (lib.mkEnableOption "Enable DKIM signing") // { default = false; }; + DKIMSelector = mkOption { + type = str; + description = "DKIM selector to use when signing"; + default = "default"; + }; + }; + }); + description = "Domains of the mail server"; + }; + certDir = mkOption { + type = path; + description = "Directory with `fullchain.pem` and `key.pem` for the FQDN. Defaults to the ACME directory of the FQDN."; + default = config.security.acme.certs."${cfg.fqdn}".directory; + }; + users = mkOption { + type = listOf (submodule { + options = { + address = mkOption { + type = str; + description = "Primary e-mail address of the user"; + example = "jdoe@example.com"; + }; + passwordHash = mkOption { + type = str; + description = '' + Argon2id hash of the user’s password. Please note that it will be + world-readable in the nix store. + ''; + example = "$argon2id$v=19$m=2097152,t=9,p=4$ycAnTa3lq5EAPTNJVpZ3+A$dIJ0CHVNn3vRUUso3IveHlrzTURoudrkxU92P5Q9/P4"; + }; + aliases = mkOption { + type = listOf str; + description = '' + A list of aliases for the user. + + If multiple users have the same alias defined, mail will be + delivered to both of them. + ''; + default = [ ]; + example = [ + "j.doe@example.com" + "jane.doe@example.com" + "postmaster@example.com" + ]; + }; + }; + }); + description = "Users of the mail server"; + }; + cleanHeaders = mkOption { + type = listOf str; + description = "A list of regular expressions that define what headers are filtered"; + default = [ + "/^\\s*Received:/" + "/^\\s*User-Agent:/" + "/^\\s*X-Mailer:/" + "/^\\s*X-Originating-IP:/" + ]; + }; + messageSizeLimit = mkOption { + type = int; + description = '' + Message size limit, in bytes. + ''; + default = 10485760; + }; + }; + + imports = [ + ./dovecot.nix + ./postfix.nix + ./rspamd.nix + ./users.nix + ]; +} diff --git a/modules/mailserver/dovecot.nix b/modules/mailserver/dovecot.nix new file mode 100644 index 0000000..a2485a5 --- /dev/null +++ b/modules/mailserver/dovecot.nix @@ -0,0 +1,169 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.jalr.mailserver; + postfixCfg = config.services.postfix; + + passdb = pkgs.writeText "dovecot-users" + (lib.concatMapStringsSep + "\n" + ({ address, passwordHash, ... }: "${address}:${passwordHash}") + cfg.users); +in +lib.mkIf cfg.enable { + services.dovecot2 = { + enable = true; + + enableLmtp = true; + enablePAM = false; + + mailUser = "vmail"; + mailGroup = "vmail"; + mailLocation = "maildir:${cfg.storageDir}/%d/%n"; + + sslServerCert = "${cfg.certDir}/fullchain.pem"; + sslServerKey = "${cfg.certDir}/key.pem"; + + mailboxes = { + Archive = { specialUse = "Archive"; auto = "subscribe"; }; + Sent = { specialUse = "Sent"; auto = "subscribe"; }; + Drafts = { specialUse = "Drafts"; auto = "subscribe"; }; + Trash = { specialUse = "Trash"; auto = "subscribe"; }; + Spam = { specialUse = "Junk"; auto = "subscribe"; }; + }; + + sieve = { + globalExtensions = [ + "fileinto" + "vnd.dovecot.pipe" + ]; + plugins = [ + "sieve_imapsieve" + "sieve_extprograms" + ]; + scripts = { + before = pkgs.writeText "spam.sieve" '' + require "fileinto"; + + if header :is "X-Spam" "Yes" { + fileinto "Spam"; + } + ''; + }; + }; + + extraConfig = '' + # generated 2021-02-04, Mozilla Guideline v5.6, Dovecot 2.3.13, OpenSSL 1.1.1i, intermediate configuration + # https://ssl-config.mozilla.org/#server=dovecot&version=2.3.13&config=intermediate&openssl=1.1.1i&guideline=5.6 + ssl = required + ssl_min_protocol = TLSv1.2 + ssl_cipher_list = ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES256-GCM-SHA384 + ssl_prefer_server_ciphers = no + + protocol imap { + mail_plugins = $mail_plugins imap_sieve + } + + protocol lmtp { + mail_plugins = $mail_plugins sieve + } + + service imap-login { + inet_listener imap { + } + } + + service lmtp { + unix_listener dovecot-lmtp { + mode = 0600 + user = ${postfixCfg.user} + group = ${postfixCfg.group} + } + } + + passdb { + driver = passwd-file + args = scheme=argon2id username_format=%u ${passdb} + auth_verbose = yes + } + + userdb { + driver = static + args = uid=vmail gid=vmail home=${cfg.storageDir}/%d/%n + } + + service auth { + vsz_limit = 4G # needed for argon2. + unix_listener auth { + mode = 0660 + user = ${postfixCfg.user} + group = ${postfixCfg.group} + } + } + + service auth-worker { + vsz_limit = 4G # needed for argon2. + } + + lda_mailbox_autosubscribe = yes + lda_mailbox_autocreate = yes + + plugin { + ${lib.optionalString cfg.spam.enable '' + imapsieve_mailbox1_name = Spam + imapsieve_mailbox1_causes = COPY + imapsieve_mailbox1_before = file:/var/lib/dovecot/sieve/learn-spam.sieve + + imapsieve_mailbox2_name = * + imapsieve_mailbox2_from = Spam + imapsieve_mailbox2_causes = COPY + imapsieve_mailbox2_before = file:/var/lib/dovecot/sieve/learn-ham.sieve + sieve_pipe_bin_dir = ${pkgs.symlinkJoin { name = "sieve-pipe-bin-dir"; paths = with pkgs; [ rspamd ]; } }/bin + ''} + } + ''; + }; + + environment.systemPackages = [ pkgs.dovecot_pigeonhole ]; + + systemd.services.dovecot2 = { + wants = [ "acme-finished-${cfg.fqdn}.target" ]; + after = [ "acme-finished-${cfg.fqdn}.target" ]; + + preStart = lib.mkIf cfg.spam.enable + (lib.mkAfter + (lib.concatStrings + (lib.mapAttrsToList + (name: content: '' + cp ${pkgs.writeText name content} /var/lib/dovecot/sieve/${name} + '') + { + "learn-spam.sieve" = '' + require ["vnd.dovecot.pipe", "copy", "imapsieve"]; + pipe :copy "rspamc" ["learn_spam"]; + ''; + "learn-ham.sieve" = '' + require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"]; + + if environment :matches "imap.mailbox" "*" { + set "mailbox" "''${1}"; + } + + if string "''${mailbox}" "Trash" { + stop; + } + + pipe :copy "rspamc" ["learn_ham"]; + ''; + }))); + }; + + networking.firewall.allowedTCPPorts = [ + 143 # IMAP + ]; + + security.acme.certs."${cfg.fqdn}".postRun = '' + if systemctl is-active dovecot2; then + systemctl --no-block reload dovecot2 + fi + ''; +} diff --git a/modules/mailserver/postfix.nix b/modules/mailserver/postfix.nix new file mode 100644 index 0000000..e813dcb --- /dev/null +++ b/modules/mailserver/postfix.nix @@ -0,0 +1,159 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.jalr.mailserver; + + listToString = lib.concatStringsSep ","; + + # List of attribute sets with single key-value pair + plainAliases = lib.flatten + (map + ({ address, aliases, ... }: + map + (alias: { "${alias}" = address; }) + (aliases ++ lib.singleton address)) + cfg.users); + + # Attribute set with every alias mapped to a list of receivers + mergedAliases = lib.attrsets.foldAttrs + (val: col: lib.singleton val ++ col) + [ ] + plainAliases; + + # Contents of the aliases file + aliasesString = lib.concatStringsSep + "\n" + (lib.mapAttrsToList + (alias: addresses: "${alias} ${listToString addresses}") + mergedAliases); + + valiases = pkgs.writeText "valiases" aliasesString; + + submissionHeaderCleanupRules = pkgs.writeText "submission_header_cleanup_rules" + (lib.concatMapStringsSep + "\n" + (regex: "${regex} IGNORE") + cfg.cleanHeaders); +in +lib.mkIf cfg.enable { + services.postfix = { + enable = true; + + inherit (cfg) relayPort; + + enableSubmission = false; # plain/STARTTLS (latter is forced in submissionOptions) + enableSubmissions = true; # submission with implicit TLS (TCP/465) + + hostname = cfg.fqdn; + networksStyle = "host"; + sslCert = "${cfg.certDir}/fullchain.pem"; + sslKey = "${cfg.certDir}/key.pem"; + + recipientDelimiter = "+"; + + mapFiles = { + inherit valiases; + }; + + config = { + # General + smtpd_banner = "${cfg.fqdn} ESMTP"; + disable_vrfy_command = true; # disable check if mailbox exists + enable_long_queue_ids = true; # better for debugging + strict_rfc821_envelopes = true; # only accept properly formatted envelope + message_size_limit = toString cfg.messageSizeLimit; + + virtual_mailbox_domains = listToString (map (x: x.domain) cfg.domains); + virtual_mailbox_maps = "hash:/var/lib/postfix/conf/valiases"; + virtual_alias_maps = "hash:/var/lib/postfix/conf/valiases"; + virtual_transport = "lmtp:unix:/run/dovecot2/dovecot-lmtp"; + + smtpd_recipient_restrictions = listToString [ + "reject_non_fqdn_recipient" + "reject_unknown_recipient_domain" + "reject_unverified_recipient" + ]; + + smtpd_client_restrictions = listToString [ + "reject_unknown_client_hostname" + ]; + + smtpd_sender_restrictions = listToString [ + "reject_non_fqdn_sender" + "reject_unknown_sender_domain" + ]; + + # generated 2021-02-04, Mozilla Guideline v5.6, Postfix 3.5.6, OpenSSL 1.1.1i, intermediate configuration + # https://ssl-config.mozilla.org/#server=postfix&version=3.5.6&config=intermediate&openssl=1.1.1i&guideline=5.6 + smtpd_tls_security_level = "may"; + smtpd_tls_auth_only = "yes"; + smtpd_tls_mandatory_protocols = "!SSLv2, !SSLv3, !TLSv1, !TLSv1.1"; + smtpd_tls_protocols = "!SSLv2, !SSLv3, !TLSv1, !TLSv1.1"; + smtpd_tls_mandatory_ciphers = "medium"; + smtpd_tls_loglevel = "1"; + + tls_medium_cipherlist = listToString [ + "ECDHE-ECDSA-AES128-GCM-SHA256" + "ECDHE-RSA-AES128-GCM-SHA256" + "ECDHE-ECDSA-AES256-GCM-SHA384" + "ECDHE-RSA-AES256-GCM-SHA384" + "ECDHE-ECDSA-CHACHA20-POLY1305" + "ECDHE-RSA-CHACHA20-POLY1305" + "DHE-RSA-AES128-GCM-SHA256" + "DHE-RSA-AES256-GCM-SHA384" + ]; + tls_preempt_cipherlist = "no"; + }; + + # plain/STARTTLS (forced with smtpd_tls_security_level) + submissionOptions = { + smtpd_tls_security_level = "encrypt"; + smtpd_sasl_auth_enable = "yes"; + smtpd_sasl_type = "dovecot"; + smtpd_sasl_path = "/run/dovecot2/auth"; + #smtpd_sasl_security_options = "noanonymous, forward_secrecy" + + smtpd_sender_login_maps = "hash:/etc/postfix/valiases"; + + smtpd_recipient_restrictions = listToString [ ]; + + smtpd_client_restrictions = listToString [ + "permit_sasl_authenticated" + "reject" + ]; + + smtpd_sender_restrictions = listToString [ + "reject_sender_login_mismatch" + ]; + + cleanup_service_name = "submission-header-cleanup"; + }; + # implicit TLS + submissionsOptions = config.services.postfix.submissionOptions; + + masterConfig = { + submission-header-cleanup = { + private = false; + maxproc = 0; + command = "cleanup"; + args = [ "-o" "header_checks=pcre:${submissionHeaderCleanupRules}" ]; + }; + }; + }; + + networking.firewall.allowedTCPPorts = [ + 25 # SMTP + 465 # SMTPS (implicit TLS) + ]; + + systemd.services.postfix = { + wants = [ "acme-finished-${cfg.fqdn}.target" ]; + requires = [ "dovecot2.service" ]; + after = [ "acme-finished-${cfg.fqdn}.target" "dovecot2.service" ]; + }; + + security.acme.certs."${cfg.fqdn}".postRun = '' + if systemctl is-active postfix; then + systemctl --no-block reload postfix + fi + ''; +} diff --git a/modules/mailserver/rspamd.nix b/modules/mailserver/rspamd.nix new file mode 100644 index 0000000..ee205de --- /dev/null +++ b/modules/mailserver/rspamd.nix @@ -0,0 +1,129 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.jalr.mailserver; + + # Generate DKIM keys: + # nix shell nixpkgs#rspamd -c \ + # rspamadm dkim_keygen -s default -d example.com -b 4096 -k /dev/shm/dkim.key > dkim.txt + + dkimEnabledDomains = lib.filter (d: d.enableDKIM) cfg.domains; + dkimSignatureDir = pkgs.stdenvNoCC.mkDerivation { + name = "dkim-signatures"; + dontUnpack = true; + installPhase = "mkdir $out" + "\n" + lib.concatStringsSep "\n" ( + map + ( + x: "ln -s " + config.sops.secrets."dkim-keys/${x.domain}.${x.DKIMSelector}".path + " $out/${x.domain}.${x.DKIMSelector}.key" + ) + dkimEnabledDomains + ); + }; +in +{ + options.jalr.mailserver.spam = { + enable = (lib.mkEnableOption "spam filtering") // { default = true; }; + }; + + config = lib.mkIf (cfg.enable && cfg.spam.enable) { + sops.secrets = lib.attrsets.listToAttrs + ( + map + (x: + { + name = "dkim-keys/${x.domain}.${x.DKIMSelector}"; + value = { + owner = config.users.users.rspamd.name; + sopsFile = ../../hosts + "/${config.networking.hostName}/secrets.yaml"; + }; + } + ) + dkimEnabledDomains + ) // { + rspamd-worker-controller = { + owner = config.users.users.rspamd.name; + sopsFile = ../../hosts + "/${config.networking.hostName}/secrets.yaml"; + }; + }; + + services.rspamd = { + enable = true; + postfix.enable = true; + workers = { + normal = { + includes = [ "$CONFDIR/worker-normal.inc" ]; + bindSockets = lib.singleton { + socket = "/run/rspamd/rspamd.sock"; + mode = "0660"; + owner = "${config.services.rspamd.user}"; + group = "${config.services.rspamd.group}"; + }; + }; + controller = { + includes = [ "$CONFDIR/worker-controller.inc" ]; + bindSockets = [ "127.0.0.1:11334" ]; + }; + }; + locals = { + "dkim_signing.conf".text = '' + enabled = true; + path = "${dkimSignatureDir}/$domain.$selector.key" + selector = "default"; + allow_envfrom_empty = true; + allow_hdrfrom_mismatch = false; + allow_hdrfrom_multiple = false; + allow_username_mismatch = false; + sign_authenticated = true; + sign_local = true; + symbol = "DKIM_SIGNED"; + try_fallback = true; + use_domain = "header"; + use_esld = true; + use_redis = false; + key_prefix = "DKIM_KEYS"; + check_pubkey = true; + allow_pubkey_mismatch = false; + ''; + "logging.inc".text = '' + # starts at info, drops to notice once started up + level = "silent"; + #debug_modules = ["dkim_signing"]; + ''; + "milter_headers.conf".text = '' + extended_spam_headers = true; + ''; + "multimap.conf".text = '' + SENDER_BLOCKED { + type = "from"; + filter = "email:addr"; + map = "/var/lib/rspamd/blocked_senders.map"; + symbol = "SENDER_BLOCKED"; + description = "Sender’s address is manually blocked"; + prefilter = true; + action = "reject"; + score = 30.0; + } + SENDER_DOMAIN_BLOCKED { + type = "from"; + filter = "email:domain:tld"; + map = "/var/lib/rspamd/blocked_sender_domains.map"; + symbol = "SENDER_DOMAIN_BLOCKED"; + description = "Sender’s effective second level domain is manually blocked"; + score = 8.0; + } + ''; + "redis.conf".text = '' + servers = "127.0.0.1:${toString config.services.redis.servers.rspamd.port}" + ''; + "worker-controller.inc".source = config.sops.secrets.rspamd-worker-controller.path; # includes password + }; + }; + + services.redis = { + vmOverCommit = true; + servers.rspamd = { + enable = true; + port = 6379; + }; + }; + }; +} diff --git a/modules/mailserver/users.nix b/modules/mailserver/users.nix new file mode 100644 index 0000000..33e2beb --- /dev/null +++ b/modules/mailserver/users.nix @@ -0,0 +1,12 @@ +{ config, lib, ... }: + +lib.mkIf config.jalr.mailserver.enable { + users.users.vmail = { + uid = 10000; + group = "vmail"; + home = config.jalr.mailserver.storageDir; + createHome = true; + }; + + users.groups.vmail.gid = 10000; +} diff --git a/modules/matrix/default.nix b/modules/matrix/default.nix new file mode 100644 index 0000000..4f3c745 --- /dev/null +++ b/modules/matrix/default.nix @@ -0,0 +1,85 @@ +{ config, lib, pkgs, ... }: + +{ + options.jalr.matrix = with lib; with lib.types; { + enable = mkEnableOption "simple matrix server"; + synapse = { + port = mkOption { + description = "TCP port for synapse service."; + type = port; + }; + app_service_config = mkOption { + type = attrsOf path; + description = '' + An attribute set of app_service_config_files + ''; + default = { }; + example = { + "my-service-alias" = "/path/to/appservice.yaml"; + }; + }; + }; + fqdn = mkOption { + type = str; + description = '' + FQDN of the matrix server + ''; + example = "matrix.example.com"; + }; + domain = mkOption { + type = str; + description = '' + Domain of the matrix server + ''; + example = "example.com"; + }; + turn = { + host = mkOption { + type = str; + description = '' + Hostname of TURN service + ''; + example = "turn.example.com"; + }; + sharedSecretFile = mkOption { + type = path; + description = "Location of the shared secret file for the TURN service"; + }; + }; + mautrix-signal = { + enable = mkEnableOption "signal bridge"; + serviceDependencies = mkOption { + type = with types; listOf str; + default = optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit; + defaultText = literalExpression '' + optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit + ''; + description = '' + List of Systemd services to require and wait for when starting the application service. + ''; + }; + port = mkOption { + description = "TCP port for mautrix-signal."; + type = port; + }; + settings = mkOption { + inherit ((pkgs.formats.json { })) type; + }; + }; + mautrix-whatsapp = { + enable = mkEnableOption "whatsapp bridge"; + port = mkOption { + description = "TCP port for mautrix-whatsapp."; + type = port; + }; + settings = mkOption { + inherit ((pkgs.formats.json { })) type; + }; + }; + }; + imports = [ + ./mautrix-signal.nix + ./mautrix-whatsapp.nix + ./synapse.nix + ]; +} diff --git a/modules/matrix/mautrix-signal.nix b/modules/matrix/mautrix-signal.nix new file mode 100644 index 0000000..e4b50d1 --- /dev/null +++ b/modules/matrix/mautrix-signal.nix @@ -0,0 +1,49 @@ +{ config, lib, ... }: + +let + cfg = config.jalr.matrix; + synapseCfg = config.services.matrix-synapse.settings; + dataDir = "/var/lib/mautrix-signal"; +in +lib.mkIf cfg.mautrix-signal.enable { + services.mautrix-signal = { + enable = true; + registerToSynapse = true; + settings = { + homeserver = { + address = synapseCfg.public_baseurl; + domain = synapseCfg.server_name; + }; + database = { + type = "sqlite3-fk-wal"; + uri = "file:${dataDir}/mautrix-signal.db?_txlock=immediate"; + }; + appservice = rec { + hostname = "127.0.0.1"; + inherit (cfg.mautrix-signal) port; + address = "http://${hostname}:${toString port}"; + provisioning.shared_secret = "disable"; + }; + bridge = { + encryption = { + allow = true; + default = true; + }; + verification_levels = { + receive = "cross-signed-tofu"; + send = "cross-signed-tofu"; + share = "cross-signed-tofu"; + }; + }; + logging = { + version = 1; + min_level = "info"; + writers = lib.singleton { + type = "stdout"; + format = "pretty-colored"; + time_format = " "; + }; + }; + } // cfg.mautrix-signal.settings; + }; +} diff --git a/modules/matrix/mautrix-whatsapp.nix b/modules/matrix/mautrix-whatsapp.nix new file mode 100644 index 0000000..7225227 --- /dev/null +++ b/modules/matrix/mautrix-whatsapp.nix @@ -0,0 +1,62 @@ +{ config, lib, ... }: + +let + cfg = config.jalr.matrix; + synapseCfg = config.services.matrix-synapse.settings; +in +lib.mkIf cfg.mautrix-whatsapp.enable { + services.mautrix-whatsapp = { + enable = true; + registerToSynapse = true; + settings = lib.mkForce ({ + homeserver = { + address = synapseCfg.public_baseurl; + domain = synapseCfg.server_name; + }; + database = { + type = "sqlite3-fk-wal"; + uri = "file:/var/lib/mautrix-whatsapp/mautrix-whatsapp.db?_txlock=immediate"; + }; + appservice = rec { + hostname = "127.0.0.1"; + inherit (cfg.mautrix-whatsapp) port; + address = "http://${hostname}:${toString port}"; + provisioning.shared_secret = "disable"; + id = "whatsapp"; + bot = { + username = "whatsappbot"; + displayname = "WhatsApp bridge bot"; + avatar = "mxc://maunium.net/NeXNQarUbrlYBiPCpprYsRqr"; + }; + }; + whatsapp = { + browser_name = "mx-wa"; + os_name = "Mautrix-WhatsApp bridge"; + }; + bridge = { + command_prefix = "!wa"; + delivery_receipts = true; + displayname_template = "{{if .FullName}}{{.FullName}}{{else if .Notify}}{{.Notify}}{{else}}{{.Jid}}{{end}} (WA)"; + history_sync = { + backfill = true; + }; + identity_change_notices = true; + private_chat_portal_meta = true; + reaction_notices = true; + relay.enable = false; + }; + logging = { + file_name_format = null; + min_level = "info"; + print_level = "info"; + writers = [ + { + format = "pretty-colored"; + time_format = " "; + type = "stdout"; + } + ]; + }; + } // cfg.mautrix-whatsapp.settings); + }; +} diff --git a/modules/matrix/synapse.nix b/modules/matrix/synapse.nix new file mode 100644 index 0000000..ea3ecc0 --- /dev/null +++ b/modules/matrix/synapse.nix @@ -0,0 +1,149 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.jalr.matrix; +in +lib.mkIf cfg.enable { + services = { + matrix-synapse = { + enable = true; + + settings = { + server_name = cfg.domain; + public_baseurl = "https://${cfg.fqdn}"; + + database = { + name = "psycopg2"; + args.user = "matrix-synapse"; + args.database = "matrix-synapse"; + }; + + listeners = lib.singleton { + inherit (cfg.synapse) port; + bind_addresses = [ "127.0.0.1" "::1" ]; + type = "http"; + tls = false; + x_forwarded = true; + resources = lib.singleton { + names = [ "client" "federation" "metrics" ]; + compress = false; + }; + }; + + turn_uris = [ + "turns:${cfg.turn.host}:5349?transport=udp" + "turns:${cfg.turn.host}:5349?transport=tcp" + "turn:${cfg.turn.host}:3478?transport=udp" + "turn:${cfg.turn.host}:3478?transport=tcp" + ]; + turn_user_lifetime = "1h"; + + enable_metrics = true; + + # adapted from https://github.com/NixOS/nixpkgs/blob/7e10bf4327491a6ebccbe1aaa8e6c6c0aca4663a/nixos/modules/services/misc/matrix-synapse-log_config.yaml + # - set root.level to WARNING instead of INFO + log_config = pkgs.writeText "log_config.yaml" (builtins.toJSON { + version = 1; + + formatters.journal_fmt.format = "%(name)s: [%(request)s] %(message)s"; + + filters.context = { + "()" = "synapse.util.logcontext.LoggingContextFilter"; + request = ""; + }; + + handlers.journal = { + class = "systemd.journal.JournalHandler"; + formatter = "journal_fmt"; + filters = [ "context" ]; + SYSLOG_IDENTIFIER = "synapse"; + }; + + root = { + level = "WARNING"; + handlers = [ "journal" ]; + }; + + disable_existing_loggers = false; + }); + + max_upload_size = "50M"; + + # I’m okay with using matrix.org as trusted key server + suppress_key_server_warning = true; + + # For mautrix-whatsapp backfilling + experimental_features.msc2716_enabled = true; + }; + + extraConfigFiles = [ + cfg.turn.sharedSecretFile + ]; + }; + + matrix-synapse.settings.app_service_config_files = lib.attrsets.mapAttrsToList + ( + name: _: + "/run/matrix-synapse/app_service_config/${name}.yaml" + ) + cfg.synapse.app_service_config; + + nginx.virtualHosts = { + "${cfg.fqdn}" = { + enableACME = true; + forceSSL = true; + + locations."/_matrix" = + let + listenerCfg = lib.elemAt config.services.matrix-synapse.settings.listeners 0; + in + { + proxyPass = "http://${lib.elemAt listenerCfg.bind_addresses 0}:${toString listenerCfg.port}"; + + extraConfig = '' + client_max_body_size ${config.services.matrix-synapse.settings.max_upload_size}; + ''; + }; + }; + + }; + + postgresql = { + enable = true; + ensureDatabases = [ + config.services.matrix-synapse.settings.database.args.database + ]; + ensureUsers = [{ + name = config.services.matrix-synapse.settings.database.args.user; + ensureDBOwnership = true; + }]; + }; + }; + + systemd.services.matrix-synapse = { + restartTriggers = lib.attrsets.mapAttrsToList + ( + _: value: "${value}" + ) + cfg.synapse.app_service_config; + serviceConfig = { + RuntimeDirectory = lib.mkForce [ + "matrix-synapse" + "matrix-synapse/app_service_config" + ]; + RuntimeDirectoryPreserve = lib.mkForce false; + ExecStartPre = lib.attrsets.mapAttrsToList + (name: value: + let + script = pkgs.writeShellScript "app_service_config-${name}" + '' + cp "${value}" "/run/matrix-synapse/app_service_config/${name}.yaml" + chown matrix-synapse: "/run/matrix-synapse/app_service_config/${name}.yaml" + ''; + in + "+${script}" + ) + cfg.synapse.app_service_config; + }; + }; +} diff --git a/modules/mobile-network.nix b/modules/mobile-network.nix new file mode 100644 index 0000000..fb7efdb --- /dev/null +++ b/modules/mobile-network.nix @@ -0,0 +1,7 @@ +{ config, lib, pkgs, ... }: + +lib.mkIf config.jalr.gui.enable { + environment.systemPackages = with pkgs; [ + usb-modeswitch + ]; +} diff --git a/home-manager/modules/neo.nix b/modules/neo.nix similarity index 71% rename from home-manager/modules/neo.nix rename to modules/neo.nix index 48bbb3b..b9c299c 100644 --- a/home-manager/modules/neo.nix +++ b/modules/neo.nix @@ -1,6 +1,5 @@ -{ config, pkgs, ... }: { - home.sessionVariables = { + environment.variables = { XKB_DEFAULT_LAYOUT = "de,de"; XKB_DEFAULT_VARIANT = "neo,"; XKB_DEFAULT_OPTIONS = "grp:win_space_toggle"; diff --git a/modules/networking/default.nix b/modules/networking/default.nix new file mode 100644 index 0000000..80cdff2 --- /dev/null +++ b/modules/networking/default.nix @@ -0,0 +1,14 @@ +{ lib +, ... +}: + +{ + imports = [ + ./network-manager.nix + ./ports.nix + ]; + + networking.firewall.logRefusedConnections = lib.mkDefault false; + + networking.nftables.enable = true; +} diff --git a/modules/networking/network-manager.nix b/modules/networking/network-manager.nix new file mode 100644 index 0000000..d70a218 --- /dev/null +++ b/modules/networking/network-manager.nix @@ -0,0 +1,47 @@ +{ config, lib, ... }: + +lib.mkIf config.jalr.gui.enable { + programs.nm-applet = { + enable = true; + indicator = true; + }; + + networking.networkmanager = { + enable = true; + ensureProfiles.profiles = { + "38C3" = { + connection = { + id = "38C3"; + type = "wifi"; + }; + wifi = { + mode = "infrastructure"; + ssid = "38C3"; + }; + wifi-security = { + auth-alg = "open"; + key-mgmt = "wpa-eap"; + }; + "802-1x" = { + anonymous-identity = "38C3"; + eap = "ttls;"; + identity = "38C3"; + password = "38C3"; + phase2-auth = "pap"; + altsubject-matches = "DNS:radius.c3noc.net"; + ca-cert = "${builtins.fetchurl { + url = "https://letsencrypt.org/certs/isrgrootx1.pem"; + sha256 = "sha256:1la36n2f31j9s03v847ig6ny9lr875q3g7smnq33dcsmf2i5gd92"; + }}"; + }; + ipv4 = { + method = "auto"; + }; + ipv6 = { + addr-gen-mode = "default"; + method = "auto"; + }; + }; + }; + }; +} diff --git a/modules/networking/ports.nix b/modules/networking/ports.nix new file mode 100644 index 0000000..3e656aa --- /dev/null +++ b/modules/networking/ports.nix @@ -0,0 +1,20 @@ +{ lib, ... }: + +{ + options.networking.ports = with lib; with lib.types; mkOption { + type = attrsOf (types.submodule { + options = { + tcp = mkOption { + type = oneOf [ port (listOf port) (attrsOf port) ]; + description = "TCP ports"; + default = [ ]; + }; + udp = mkOption { + type = oneOf [ port (listOf port) (attrsOf port) ]; + description = "UDP ports"; + default = [ ]; + }; + }; + }); + }; +} diff --git a/modules/nix.nix b/modules/nix.nix index c3cccdb..eb8f9b4 100644 --- a/modules/nix.nix +++ b/modules/nix.nix @@ -1,12 +1,7 @@ -{ config, lib, pkgs, inputs, system, ... }: +{ lib, pkgs, inputs, system, ... }: { nix = { - package = pkgs.nixFlakes; - extraOptions = '' - experimental-features = nix-command flakes - ''; - daemonCPUSchedPolicy = "idle"; daemonIOSchedClass = "idle"; daemonIOSchedPriority = 7; @@ -16,37 +11,47 @@ ]; settings = { + experimental-features = [ + "nix-command" + "flakes" + ]; trusted-users = [ "@wheel" ]; auto-optimise-store = true; allowed-users = [ "@wheel" ]; + + log-lines = lib.mkDefault 25; + + # Avoid disk full issues + max-free = lib.mkDefault (3000 * 1024 * 1024); + min-free = lib.mkDefault (512 * 1024 * 10); + + download-buffer-size = lib.mkDefault (512 * 1024 * 1024); + }; + gc = { + automatic = true; + options = "--delete-older-than 30d"; + randomizedDelaySec = "60 min"; }; }; - nixpkgs.overlays = with inputs; [ - self.overlay - (final: prev: { + systemd.services.nix-daemon.serviceConfig.OOMScoreAdjust = 250; + + nixpkgs.config.permittedInsecurePackages = [ + "olm-3.2.16" + ]; + + nixpkgs.overlays = [ + inputs.self.overlays.default + (_: prev: { master = import inputs.nixpkgsMaster { inherit system; - config = prev.config; + inherit (prev) config; }; }) - ] - # Tradebyte access points use legacy crypto - ++ lib.optional config.myConfig.tradebyte.enable ( - final: prev: - let - inherit (prev) callPackage; - in - { - wpa_supplicant = prev.wpa_supplicant.overrideAttrs (attrs: { - patches = attrs.patches ++ [ - ./wpa_supplicant/SSL_CTX_set_options-SSL_OP_LEGACY_SERVER_CONNECT.patch - ]; - }); - } - ); + ]; environment.systemPackages = with pkgs; [ cached-nix-shell + git ]; } diff --git a/modules/obs.nix b/modules/obs.nix index 24fea82..e8bcf9f 100644 --- a/modules/obs.nix +++ b/modules/obs.nix @@ -1,28 +1,14 @@ { config, lib, pkgs, ... }: -lib.mkIf config.myConfig.gui.enable { +lib.mkIf config.jalr.gui.enable { boot = { + extraModulePackages = [ pkgs.linuxPackages.v4l2loopback ]; kernelModules = [ "v4l2loopback" ]; extraModprobeConfig = '' options v4l2loopback exclusive_caps=1 card_label=OBS video_nr=10 ''; - extraModulePackages = [ - (pkgs.linuxPackages.v4l2loopback.overrideAttrs ({ ... }: { - src = pkgs.fetchFromGitHub { - owner = "umlaeute"; - repo = "v4l2loopback"; - rev = "edf0f10bc079e5e3922bddbb8185dc626ab14a1b"; - sha256 = "nHwC6/miECn8RuAeWoOxYv+9NWcBeeGHlcr0ai827Uo="; - fetchSubmodules = false; - }; - })) - ]; }; environment.systemPackages = with pkgs; [ v4l-utils ]; - - services.udev.extraRules = '' - SUBSYSTEM=="video4linux", ATTR{idVendor}=="2109", ATTR{idProduct}=="2813", SYMLINK+="video_c922" - ''; } diff --git a/modules/pipewire.nix b/modules/pipewire.nix index 6e213bd..ea97fb6 100644 --- a/modules/pipewire.nix +++ b/modules/pipewire.nix @@ -1,19 +1,14 @@ { config, lib, pkgs, ... }: -let - defaults = { - media-session = (builtins.fromJSON (builtins.readFile "${pkgs.pipewire-media-session}/nix-support/media-session.conf.json")); - }; -in -lib.mkIf config.myConfig.gui.enable { - sound.enable = true; - hardware.pulseaudio.enable = false; +lib.mkIf config.jalr.gui.enable { + services.pulseaudio.enable = false; # FIXME #hardware.pulseaudio.extraModules = [ pkgs.pulseaudio-modules-bt ]; services.pipewire = { enable = true; + package = pkgs.master.pipewire; pulse = { enable = true; }; @@ -24,6 +19,33 @@ lib.mkIf config.myConfig.gui.enable { enable = true; support32Bit = true; }; + extraConfig = { + pipewire-pulse."10-snapcast-discover" = { + "context.modules" = [ + { + name = "libpipewire-module-snapcast-discover"; + args = { + stream.rules = [{ + matches = [{ + snapcast.ip = "~.*"; + }]; + actions = { + create-stream = { }; + }; + }]; + }; + } + ]; + }; + /* + pipewire."raop-sink" = { + "context.modules" = [ + { name = "libpipewire-module-raop-discover"; args = { }; } + ]; + }; + */ + }; + raopOpenFirewall = true; }; environment.systemPackages = with pkgs; [ @@ -41,42 +63,6 @@ lib.mkIf config.myConfig.gui.enable { } ]; - # Split U-PHORIA inputs into mono channels - # https://gitlab.freedesktop.org/pipewire/pipewire/-/wikis/Virtual-Devices#behringer-umc404hd-micguitar-virtual-sources - services.pipewire.media-session.config.media-session."context.modules" = defaults.media-session."context.modules" ++ [ - { - name = "libpipewire-module-loopback"; - args = { - "node.name" = "UMC202HD_input1"; - "node.description" = "UMC202HD Input 1"; - "capture.props" = { - "audio.position" = [ "AUX0" ]; - "stream.dont-remix" = true; - "node.target" = "alsa_input.usb-BEHRINGER_UMC202HD_192k-00.pro-input-0"; - "node.passive" = true; - }; - "playback.props" = { - "media.class" = "Audio/Source"; - "audio.position" = [ "MONO" ]; - }; - }; - } - { - name = "libpipewire-module-loopback"; - args = { - "node.name" = "UMC202HD_input2"; - "node.description" = "UMC202HD Input 2"; - "capture.props" = { - "audio.position" = [ "AUX1" ]; - "stream.dont-remix" = true; - "node.target" = "alsa_input.usb-BEHRINGER_UMC202HD_192k-00.pro-input-0"; - "node.passive" = true; - }; - "playback.props" = { - "media.class" = "Audio/Source"; - "audio.position" = [ "MONO" ]; - }; - }; - } - ]; + security.rtkit.enable = true; + } diff --git a/modules/podman.nix b/modules/podman.nix index d7453a9..79486f1 100644 --- a/modules/podman.nix +++ b/modules/podman.nix @@ -1,7 +1,11 @@ +{ pkgs, ... }: { virtualisation.podman = { enable = true; dockerCompat = true; dockerSocket.enable = true; }; + environment.systemPackages = with pkgs; [ + podman-compose + ]; } diff --git a/modules/printers/default.nix b/modules/printers/default.nix index a29723f..b207377 100644 --- a/modules/printers/default.nix +++ b/modules/printers/default.nix @@ -1,7 +1,11 @@ +{ config, lib, ... }: + { imports = [ ./hl3172cdw.nix - ./p-touch_p700.nix ]; + config = lib.mkIf config.jalr.gui.enable { + # install virtual pdf printer + services.printing.cups-pdf.enable = true; + }; } - diff --git a/modules/printers/hl3172cdw.nix b/modules/printers/hl3172cdw.nix index 49ae01e..b89b981 100644 --- a/modules/printers/hl3172cdw.nix +++ b/modules/printers/hl3172cdw.nix @@ -1,6 +1,6 @@ { config, lib, pkgs, ... }: -lib.mkIf config.myConfig.gui.enable { +lib.mkIf config.jalr.gui.enable { services.printing = { enable = true; drivers = [ pkgs.brlaser ]; diff --git a/modules/printers/p-touch_p700.nix b/modules/printers/p-touch_p700.nix deleted file mode 100644 index 627ad56..0000000 --- a/modules/printers/p-touch_p700.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ config, lib, ... }: - -{ - services.udev.extraRules = '' - SUBSYSTEM=="usb", ACTION=="add", ATTR{idVendor}=="04f9", ATTR{idProduct}=="2061", OWNER="root", GROUP="users", MODE="660" - ''; -} diff --git a/modules/providers/hetzner-cloud.nix b/modules/providers/hetzner-cloud.nix new file mode 100644 index 0000000..c1e0b16 --- /dev/null +++ b/modules/providers/hetzner-cloud.nix @@ -0,0 +1,108 @@ +{ config +, pkgs +, modulesPath +, ... +}: +let + cfg = config.disko; +in +{ + imports = [ + (modulesPath + "/profiles/qemu-guest.nix") + ]; + + config = { + networking.useDHCP = false; + + systemd.network = { + enable = true; + networks."10-wan" = { + matchConfig.Name = "enp1s0"; + networkConfig.DHCP = "ipv4"; + routes = [ + { Gateway = "fe80::1"; } + ]; + }; + }; + + boot = { + loader = { + grub.enable = pkgs.hostPlatform.system == "x86_64-linux"; + systemd-boot = { + enable = pkgs.hostPlatform.system == "aarch64-linux"; + configurationLimit = 10; + }; + efi = { + efiSysMountPoint = "/boot"; + canTouchEfiVariables = true; + }; + }; + }; + disko.devices = { + disk = { + virt = { + type = "disk"; + content = { + type = "gpt"; + partitions = { + boot = { + size = "1M"; + type = "EF02"; # for grub MBR + priority = 1; # Needs to be first partition + }; + esp = { + type = "EF00"; + size = "1024M"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = [ "uid=0" "gid=0" "fmask=0077" "dmask=0077" "nodev" "nosuid" "noexec" ]; + }; + }; + linux = { + size = "100%"; + content = { + type = "btrfs"; + extraArgs = [ "-f" ]; + postCreateHook = + let + inherit (cfg.devices.disk.virt.content.partitions.linux) device; + in + '' + mountpoint="$(mktemp -d)" + mount "${device}" "$mountpoint" -o subvol=/ + trap 'umount "$mountpoint"; rmdir "$mountpoint"' EXIT + btrfs subvolume snapshot -r $mountpoint/root $mountpoint/root-blank + ''; + subvolumes = { + "/root" = { + mountpoint = "/"; + mountOptions = [ "compress-force=zstd:1" "noatime" ]; + }; + "/home" = { + mountpoint = "/home"; + mountOptions = [ "compress-force=zstd:1" "noatime" "nodev" "nosuid" ]; + }; + "/nix" = { + mountpoint = "/nix"; + mountOptions = [ "compress-force=zstd:1" "noatime" "nodev" ]; + }; + "/log" = { + mountpoint = "/var/log"; + mountOptions = [ "compress-force=zstd:1" "noatime" "nodev" "nosuid" ]; + }; + "/persist" = { + mountpoint = "/persist"; + mountOptions = [ "compress-force=zstd:1" "noatime" "nodev" "nosuid" ]; + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; +} diff --git a/modules/qbittorrent/default.nix b/modules/qbittorrent/default.nix deleted file mode 100644 index 40eeb33..0000000 --- a/modules/qbittorrent/default.nix +++ /dev/null @@ -1,94 +0,0 @@ -{ config, inputs, lib, pkgs, ... }: -let - cfg = config.myConfig.qbittorrent; -in -{ - options.myConfig.qbittorrent = { - enable = lib.mkEnableOption "the qbittorrent service"; - homeDir = lib.mkOption { - type = lib.types.path; - default = "/var/lib/qbittorrent"; - }; - configDir = lib.mkOption { - type = lib.types.path; - default = "${cfg.homeDir}/config"; - }; - downloadDir = lib.mkOption { - type = lib.types.path; - default = "${cfg.homeDir}/download"; - }; - webuiPort = lib.mkOption { - type = lib.types.int; - default = 8099; - }; - sopsFile = lib.mkOption { - type = lib.types.path; - default = ../../hosts/${config.networking.hostName}/secrets.yaml; - description = '' - The sops secret file that includes the htpasswd file. - ''; - }; - fqdn = lib.mkOption { - type = lib.types.str; - description = "The fqdn nginx should listen on. It must not be used for anything else."; - }; - }; - - config = lib.mkIf cfg.enable - { - users.users.qbittorrent = { - group = "qbittorrent"; - home = cfg.homeDir; - isSystemUser = true; - }; - users.groups.qbittorrent = { }; - - systemd.tmpfiles.rules = [ - "d '${cfg.downloadDir}' 0775 qbittorrent users - -" - "d '${cfg.homeDir}' 0771 qbittorrent qbittorrent - -" - ]; - - sops.secrets.sturzbach-htpasswd = { - sopsFile = cfg.sopsFile; - owner = "nginx"; - }; - - systemd.services.qbittorrent = { - description = "qBittorrent Service"; - wantedBy = [ "multi-user.target" ]; - - serviceConfig = { - Restart = "always"; - ExecStart = "${pkgs.qbittorrent-nox}/bin/qbittorrent-nox --profile=${cfg.configDir} --webui-port=${toString cfg.webuiPort}"; - User = "qbittorrent"; - Group = "qbittorrent"; - - # Increase number of open file descriptors (default: 1024) - LimitNOFILE = 65536; - - # systemd-analyze --no-pager security qbittorrent.service - CapabilityBoundingSet = null; - PrivateDevices = true; - PrivateTmp = true; - PrivateUsers = true; - ProtectHome = true; - RestrictNamespaces = true; - SystemCallFilter = "@system-service"; - }; - }; - - services.nginx.virtualHosts."${cfg.fqdn}" = { - enableACME = lib.mkDefault true; - forceSSL = lib.mkDefault true; - - basicAuthFile = config.sops.secrets.sturzbach-htpasswd.path; - - locations = { - "/" = { - proxyPass = "http://127.0.0.1:${toString cfg.webuiPort}"; - proxyWebsockets = true; - }; - }; - }; - }; -} diff --git a/modules/remarkable.nix b/modules/remarkable.nix new file mode 100644 index 0000000..bce495c --- /dev/null +++ b/modules/remarkable.nix @@ -0,0 +1,22 @@ +{ config, lib, ... }: +lib.mkIf config.jalr.gui.enable { + # allow port for reMarkable screen sharing + networking.firewall.allowedUDPPorts = [ 5901 ]; + + #services.printing.cups-pdf.instances.remarkable = { + # settings.Out = "socket://remarkable" + #}; + + # https://github.com/Evidlo/remarkable_printer needs to be + # installed on the reMarkable + hardware.printers.ensurePrinters = [ + { + name = "remarkable"; + description = "reMarkable virtual printer"; + deviceUri = "socket://remarkable"; + #location = "/var/spool/cups-pdf-pdf/users/\${USER}"; + model = "CUPS-PDF_opt.ppd"; + ppdOptions = { }; + } + ]; +} diff --git a/modules/sdr.nix b/modules/sdr.nix deleted file mode 100644 index 55ff119..0000000 --- a/modules/sdr.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - cfg = config.myConfig; -in -{ - options.myConfig = { - sdr = { - enable = pkgs.lib.mkEnableOption "Enable software defined radio"; - }; - }; - config = lib.mkIf cfg.sdr.enable { - services.udev.extraRules = '' - # rad10 - SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="cc15", GROUP="users", MODE="0660" - SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="6089", GROUP="users", MODE="0660" - ''; - }; -} diff --git a/modules/sshd.nix b/modules/sshd.nix index 4aa1ad6..040ed09 100644 --- a/modules/sshd.nix +++ b/modules/sshd.nix @@ -1,6 +1,61 @@ +{ lib +, config +, ... +}: + { services.openssh = { enable = true; - passwordAuthentication = false; + settings = { + KbdInteractiveAuthentication = false; + Ciphers = [ + "aes256-gcm@openssh.com" + ]; + # Use key exchange algorithms recommended by `nixpkgs#ssh-audit` + KexAlgorithms = [ + "curve25519-sha256" + "curve25519-sha256@libssh.org" + "diffie-hellman-group16-sha512" + "diffie-hellman-group18-sha512" + "sntrup761x25519-sha512@openssh.com" + ]; + PasswordAuthentication = false; + StreamLocalBindUnlink = true; # unbind gnupg sockets if they exists + UseDns = false; + X11Forwarding = false; + }; + hostKeys = [ + { + path = "/etc/ssh/ssh_host_ed25519_key"; + type = "ed25519"; + } + ]; + authorizedKeysFiles = lib.mkForce [ "/etc/ssh/authorized_keys.d/%u" ]; }; + + networking.nftables.tables."nixos-fw".content = lib.mkOrder 20 '' + set ssh-ratelimit-v4 { + type ipv4_addr + timeout 60s + flags dynamic + } + + set ssh-ratelimit-v6 { + type ipv6_addr + timeout 60s + flags dynamic + } + ''; + + # Implement connection rate limit + services.openssh.openFirewall = false; + networking.firewall.extraInputRules = lib.mkOrder 5 ( + let + ports = builtins.concatStringsSep ", " (map builtins.toString config.services.openssh.ports); + in + '' + tcp dport { ${ports} } update @ssh-ratelimit-v4 { ip saddr limit rate 1/second burst 10 packets } accept + tcp dport { ${ports} } update @ssh-ratelimit-v6 { ip6 saddr limit rate 1/second burst 10 packets } accept + '' + ); } diff --git a/modules/steelseries-nova-pro.nix b/modules/steelseries-nova-pro.nix new file mode 100644 index 0000000..d432dca --- /dev/null +++ b/modules/steelseries-nova-pro.nix @@ -0,0 +1,7 @@ +{ lib, config, ... }: + +lib.mkIf config.jalr.gui.enable { + services.gg-chatmix = { + enable = true; + }; +} diff --git a/modules/sudo.nix b/modules/sudo.nix index ebe10d1..da6577a 100644 --- a/modules/sudo.nix +++ b/modules/sudo.nix @@ -1,5 +1,21 @@ -{ pkgs, inputs, ... }: - +let + commandsWithoutPassword = [ + "/run/current-system/sw/bin/systemctl restart tor.service" + ]; +in { - security.sudo.execWheelOnly = true; + security.sudo = { + execWheelOnly = true; + extraRules = [ + { + groups = [ "wheel" ]; + commands = map + (cmd: { + command = cmd; + options = [ "NOPASSWD" ]; + }) + commandsWithoutPassword; + } + ]; + }; } diff --git a/modules/sway.nix b/modules/sway.nix index dff050e..dbd37fe 100644 --- a/modules/sway.nix +++ b/modules/sway.nix @@ -1,33 +1,65 @@ { config, lib, pkgs, ... }: -lib.mkIf config.myConfig.gui.enable { - programs.sway = { - enable = true; - # FIXME: move to home manager - extraPackages = with pkgs; [ - grim - mako - gammastep - slurp - wl-clipboard - xwayland - ]; - extraSessionCommands = '' - export XKB_DEFAULT_LAYOUT=de # TODO: test if we need it - export XKB_DEFAULT_VARIANT=neo # TODO: test if we need it - export QT_QPA_PLATFORM=wayland - export QT_WAYLAND_DISABLE_WINDOWDECORATION=1 - export ELM_ENGINE=wayland_shm - export GDK_BACKEND=wayland - export _JAVA_AWT_WM_NONREPARENTING=1 - ''; +lib.mkIf (config.jalr.gui.enable && config.jalr.gui.sway.enable) { + services.displayManager.sessionPackages = [ pkgs.sway ]; + + programs = { + wshowkeys.enable = true; + dconf.enable = true; + sway = { + enable = true; + xwayland.enable = true; + wrapperFeatures.gtk = true; + }; }; + services.greetd = { + enable = !config.jalr.gui.gnome.enable; + settings = + let + command = pkgs.writeShellScript "sway-init" '' + systemctl --user import-environment PATH + #systemctl --user restart xdg-desktop-portal.service + exec ${pkgs.sway}/bin/sway + ''; + user = "jalr"; + in + { + default_session = { + inherit command; + inherit user; + }; + initial_session = { + inherit command; + inherit user; + }; + }; + }; + + hardware.graphics.enable = true; + + security.polkit.enable = true; + + security.pam.loginLimits = [{ + domain = "@users"; + item = "rtprio"; + type = "-"; + value = 1; + }]; + xdg = { + icons.enable = true; portal = { enable = true; - extraPortals = [ pkgs.xdg-desktop-portal-wlr ]; + wlr.enable = true; + extraPortals = [ + pkgs.xdg-desktop-portal-gtk + ]; + xdgOpenUsePortal = true; }; - icons.enable = true; }; + + environment.systemPackages = with pkgs; [ + adwaita-icon-theme + ]; } diff --git a/modules/tor.nix b/modules/tor.nix deleted file mode 100644 index 26b357b..0000000 --- a/modules/tor.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ - services.tor = { - enable = true; - settings = { - DNSPort = 9053; - AutomapHostsOnResolve = true; - AutomapHostsSuffixes = [ - ".exit" - ".onion" - ]; - }; - }; -} diff --git a/modules/tradebyte/default.nix b/modules/tradebyte/default.nix deleted file mode 100644 index e0c1b89..0000000 --- a/modules/tradebyte/default.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ lib, ... }: - -{ - options.myConfig = { - tradebyte.enable = lib.mkEnableOption "TB.config"; - }; -} - diff --git a/modules/udev.nix b/modules/udev.nix new file mode 100644 index 0000000..908a711 --- /dev/null +++ b/modules/udev.nix @@ -0,0 +1,31 @@ +let + usbDeviceRules = [ + # rad10 + { vendor = "1d50"; product = "cc15"; group = "users"; mode = "0660"; } + { vendor = "1d50"; product = "6089"; group = "users"; mode = "0660"; } + + # DJI Goggles + { vendor = "2ca3"; product = "001f"; group = "plugdev"; mode = "0660"; } + + # uDMX + { vendor = "16c0"; product = "05dc"; group = "users"; mode = "0660"; } + + # Brother P-touch P700 + { vendor = "04f9"; product = "2061"; group = "users"; mode = "0660"; } + + # RP2040 in BOOTSEL mode + { vendor = "2e8a"; product = "0003"; group = "users"; mode = "0660"; } + + # RP2350 in BOOTSEL mode + { vendor = "2e8a"; product = "000f"; group = "users"; mode = "0660"; } + + # WCH Link (CMSIS-DAP compatible adapter) + { vendor = "1a86"; product = "8010"; group = "plugdev"; mode = "0660"; } + ]; + + mkUsbRule = rule: + ''SUBSYSTEM=="usb", ATTR{idVendor}=="${rule.vendor}", ATTR{idProduct}=="${rule.product}", GROUP="${rule.group}", MODE="${rule.mode}"''; +in +{ + services.udev.extraRules = builtins.concatStringsSep "\n" (map mkUsbRule usbDeviceRules); +} diff --git a/modules/uefi.nix b/modules/uefi.nix index a6701c3..8f4f855 100644 --- a/modules/uefi.nix +++ b/modules/uefi.nix @@ -1,10 +1,10 @@ { config, lib, pkgs, ... }: let - cfg = config.myConfig; + cfg = config.jalr; in { - options.myConfig = { + options.jalr = { uefi.enable = pkgs.lib.mkEnableOption "System uses UEFI" // { default = false; }; }; config = lib.mkIf cfg.uefi.enable { diff --git a/modules/unfree.nix b/modules/unfree.nix index 5024029..a24df1c 100644 --- a/modules/unfree.nix +++ b/modules/unfree.nix @@ -1,7 +1,11 @@ { lib, ... }: { - nixpkgs.config.allowUnfreePredicate = (pkg: lib.elem (lib.getName pkg) [ + nixpkgs.config.allowUnfreePredicate = pkg: lib.elem (lib.getName pkg) [ + "mongodb" + "rar2fs" + "roomeqwizard" "unifi-controller" - ]); + "unrar" + ]; } diff --git a/modules/upgrade-diff.nix b/modules/upgrade-diff.nix new file mode 100644 index 0000000..ccdbe4c --- /dev/null +++ b/modules/upgrade-diff.nix @@ -0,0 +1,14 @@ +# MIT Jörg Thalheim - https://github.com/Mic92/dotfiles/blob/c6cad4e57016945c4816c8ec6f0a94daaa0c3203/nixos/modules/upgrade-diff.nix +{ config, pkgs, ... }: +{ + system.activationScripts.diff = { + supportsDryActivation = true; + text = '' + if [[ -e /run/current-system ]]; then + echo "--- diff to current-system" + ${pkgs.nvd}/bin/nvd --nix-bin-dir=${config.nix.package}/bin diff /run/current-system "$systemConfig" + echo "---" + fi + ''; + }; +} diff --git a/modules/wireshark.nix b/modules/wireshark.nix deleted file mode 100644 index 6a407b5..0000000 --- a/modules/wireshark.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ config, lib, pkgs, ... }: -lib.mkIf config.myConfig.gui.enable { - programs.wireshark = { - enable = true; - package = pkgs.wireshark; - }; -} diff --git a/modules/wireshark/default.nix b/modules/wireshark/default.nix new file mode 100644 index 0000000..6c1b0c0 --- /dev/null +++ b/modules/wireshark/default.nix @@ -0,0 +1,23 @@ +{ config, lib, pkgs, ... }: +let + extcap = ./extcap; + pythonWithPackages = pkgs.python3.withPackages (pp: with pp; [ + pyserial + psutil + ]); + nrf_sniffer_ble = pkgs.writeShellScript "nrf_sniffer_ble" '' + script_path=$(dirname `which $0`) + + exec ${pythonWithPackages}/bin/python3 $script_path/nrf_sniffer_ble.py "$@" + ''; +in +lib.mkIf config.jalr.gui.enable { + programs.wireshark = { + enable = true; + package = pkgs.wireshark.overrideAttrs (o: { + postInstall = '' + cp -r ${extcap}/* ${nrf_sniffer_ble} $out/lib/wireshark/extcap + '' + o.postInstall; + }); + }; +} diff --git a/modules/wireshark/extcap/SnifferAPI/CaptureFiles.py b/modules/wireshark/extcap/SnifferAPI/CaptureFiles.py new file mode 100644 index 0000000..8c218e5 --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/CaptureFiles.py @@ -0,0 +1,91 @@ +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +import time, os, logging +from . import Logger +from . import Pcap + + +DEFAULT_CAPTURE_FILE_DIR = Logger.DEFAULT_LOG_FILE_DIR +DEFAULT_CAPTURE_FILE_NAME = "capture.pcap" + + +def get_capture_file_path(capture_file_path=None): + default_path = os.path.join(DEFAULT_CAPTURE_FILE_DIR, DEFAULT_CAPTURE_FILE_NAME) + if capture_file_path is None: + return default_path + if os.path.splitext(capture_file_path)[1] != ".pcap": + return default_path + return os.path.abspath(capture_file_path) + + +class CaptureFileHandler: + def __init__(self, capture_file_path=None, clear=False): + filename = get_capture_file_path(capture_file_path) + if not os.path.isdir(os.path.dirname(filename)): + os.makedirs(os.path.dirname(filename)) + self.filename = filename + self.backupFilename = self.filename + ".1" + if not os.path.isfile(self.filename): + self.startNewFile() + elif os.path.getsize(self.filename) > 20000000: + self.doRollover() + if clear: + # clear file + self.startNewFile() + + def startNewFile(self): + with open(self.filename, "wb") as f: + f.write(Pcap.get_global_header()) + + def doRollover(self): + try: + os.remove(self.backupFilename) + except: + logging.exception("capture file rollover remove backup failed") + try: + os.rename(self.filename, self.backupFilename) + self.startNewFile() + except: + logging.exception("capture file rollover failed") + + def writePacket(self, packet): + with open(self.filename, "ab") as f: + packet = Pcap.create_packet( + bytes([packet.boardId] + packet.getList()), packet.time + ) + f.write(packet) diff --git a/modules/wireshark/extcap/SnifferAPI/Devices.py b/modules/wireshark/extcap/SnifferAPI/Devices.py new file mode 100644 index 0000000..61ac961 --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/Devices.py @@ -0,0 +1,150 @@ +# Copyright (c) 2017, Nordic Semiconductor ASA +# +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from . import Notifications +import logging, threading + + +class DeviceList(Notifications.Notifier): + def __init__(self, *args, **kwargs): + Notifications.Notifier.__init__(self, *args, **kwargs) + logging.info("args: " + str(args)) + logging.info("kwargs: " + str(kwargs)) + self._deviceListLock = threading.RLock() + with self._deviceListLock: + self.devices = [] + + def __len__(self): + return len(self.devices) + + def __repr__(self): + return "Sniffer Device List: " + str(self.asList()) + + def clear(self): + logging.info("Clearing") + with self._deviceListLock: + self.devices = [] + self.notify("DEVICES_CLEARED") + + def appendOrUpdate(self, newDevice): + with self._deviceListLock: + existingDevice = self.find(newDevice) + + # Add device to the list of devices being displayed, but only if CRC is OK + if existingDevice == None: + self.append(newDevice) + else: + updated = False + if (newDevice.name != '""') and (existingDevice.name == '""'): + existingDevice.name = newDevice.name + updated = True + + if ( + newDevice.RSSI != 0 + and (existingDevice.RSSI < (newDevice.RSSI - 5)) + or (existingDevice.RSSI > (newDevice.RSSI + 2)) + ): + existingDevice.RSSI = newDevice.RSSI + updated = True + + if updated: + self.notify("DEVICE_UPDATED", existingDevice) + + def append(self, device): + self.devices.append(device) + self.notify("DEVICE_ADDED", device) + + def find(self, id): + if type(id) == list: + for dev in self.devices: + if dev.address == id: + return dev + elif type(id) == int: + return self.devices[id] + elif type(id) == str: + for dev in self.devices: + if dev.name in [id, '"' + id + '"']: + return dev + elif id.__class__.__name__ == "Device": + return self.find(id.address) + return None + + def remove(self, id): + if type(id) == list: # address + device = self.devices.pop(self.devices.index(self.find(id))) + elif type(id) == int: + device = self.devices.pop(id) + elif type(id) == Device: + device = self.devices.pop(self.devices.index(self.find(id.address))) + self.notify("DEVICE_REMOVED", device) + + def index(self, device): + index = 0 + for dev in self.devices: + if dev.address == device.address: + return index + index += 1 + return None + + def setFollowed(self, device): + if device in self.devices: + for dev in self.devices: + dev.followed = False + device.followed = True + self.notify("DEVICE_FOLLOWED", device) + + def asList(self): + return self.devices[:] + + +class Device: + def __init__(self, address, name, RSSI): + self.address = address + self.name = name + self.RSSI = RSSI + self.followed = False + + def __repr__(self): + return 'Bluetooth LE device "' + self.name + '" (' + str(self.address) + ")" + + +def listToString(list): + str = "" + for i in list: + str += chr(i) + return str diff --git a/modules/wireshark/extcap/SnifferAPI/Exceptions.py b/modules/wireshark/extcap/SnifferAPI/Exceptions.py new file mode 100644 index 0000000..86f356a --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/Exceptions.py @@ -0,0 +1,66 @@ +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +class SnifferTimeout(Exception): + pass + + +class UARTPacketError(Exception): + pass + + +class LockedException(Exception): + def __init__(self, message): + self.message = message + + +class InvalidPacketException(Exception): + pass + + +class InvalidAdvChannel(Exception): + pass + + +# Internal Use +class SnifferWatchDogTimeout(SnifferTimeout): + pass + + +# Internal Use +class ExitCodeException(Exception): + pass diff --git a/modules/wireshark/extcap/SnifferAPI/Filelock.py b/modules/wireshark/extcap/SnifferAPI/Filelock.py new file mode 100644 index 0000000..7bf21b5 --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/Filelock.py @@ -0,0 +1,67 @@ +import os +import logging +from sys import platform + +if platform == "linux": + import psutil + +from . import Exceptions + +# Lock file management. +# ref: https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch05s09.html +# +# Stored in /var/lock: +# The naming convention which must be used is "LCK.." followed by the base name of the device. +# For example, to lock /dev/ttyS0 the file "LCK..ttyS0" would be created. +# HDB UUCP lock file format: +# process identifier (PID) as a ten byte ASCII decimal number, with a trailing newline + + +def lockpid(lockfile): + if os.path.isfile(lockfile): + with open(lockfile) as fd: + lockpid = fd.read() + + try: + return int(lockpid) + except: + logging.info("Lockfile is invalid. Overriding it..") + os.remove(lockfile) + return 0 + + return 0 + + +def lock(port): + if platform != "linux": + return + + tty = os.path.basename(port) + lockfile = os.path.join("/run", "user", f"{os.getuid()}", f"{tty}.lock") + + lockedpid = lockpid(lockfile) + if lockedpid: + if lockedpid == os.getpid(): + return + + if psutil.pid_exists(lockedpid): + raise Exceptions.LockedException(f"Device {port} is locked") + else: + logging.info("Lockfile is stale. Overriding it..") + os.remove(lockfile) + + fd = open(lockfile, "w") + with open(lockfile, "w") as fd: + fd.write(f"{os.getpid():10}") + + +def unlock(port): + if platform != "linux": + return + + tty = os.path.basename(port) + lockfile = f"/var/lock/LCK..{tty}" + + lockedpid = lockpid(lockfile) + if lockedpid == os.getpid(): + os.remove(lockfile) diff --git a/modules/wireshark/extcap/SnifferAPI/Logger.py b/modules/wireshark/extcap/SnifferAPI/Logger.py new file mode 100644 index 0000000..228a0f1 --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/Logger.py @@ -0,0 +1,214 @@ +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +import time, os, logging, traceback, threading +import logging.handlers as logHandlers + +################################################################# +# This file contains the logger. To log a line, simply write # +# 'logging.[level]("whatever you want to log")' # +# [level] is one of {info, debug, warning, error, critical, # +# exception} # +# See python logging documentation # +# As long as Logger.initLogger has been called beforehand, this # +# will result in the line being appended to the log file # +################################################################# + +appdata = os.getenv("appdata") +if appdata: + DEFAULT_LOG_FILE_DIR = os.path.join( + appdata, "Nordic Semiconductor", "Sniffer", "logs" + ) +else: + DEFAULT_LOG_FILE_DIR = "/tmp/logs" + +DEFAULT_LOG_FILE_NAME = "log.txt" + +logFileName = None +logHandler = None +logHandlerArray = [] +logFlusher = None + +myMaxBytes = 1000000 + + +def setLogFileName(log_file_path): + global logFileName + logFileName = os.path.abspath(log_file_path) + + +# Ensure that the directory we are writing the log file to exists. +# Create our logfile, and write the timestamp in the first line. +def initLogger(): + try: + global logFileName + if logFileName is None: + logFileName = os.path.join(DEFAULT_LOG_FILE_DIR, DEFAULT_LOG_FILE_NAME) + + # First, make sure that the directory exists + if not os.path.isdir(os.path.dirname(logFileName)): + os.makedirs(os.path.dirname(logFileName)) + + # If the file does not exist, create it, and save the timestamp + if not os.path.isfile(logFileName): + with open(logFileName, "w") as f: + f.write(str(time.time()) + str(os.linesep)) + + global logFlusher + global logHandlerArray + + logHandler = MyRotatingFileHandler( + logFileName, mode="a", maxBytes=myMaxBytes, backupCount=3 + ) + logFormatter = logging.Formatter( + "%(asctime)s %(levelname)s: %(message)s", datefmt="%d-%b-%Y %H:%M:%S (%z)" + ) + logHandler.setFormatter(logFormatter) + logger = logging.getLogger() + logger.addHandler(logHandler) + logger.setLevel(logging.INFO) + logFlusher = LogFlusher(logHandler) + logHandlerArray.append(logHandler) + except: + print("LOGGING FAILED") + print(traceback.format_exc()) + raise + + +def shutdownLogger(): + if logFlusher is not None: + logFlusher.stop() + logging.shutdown() + + +# Clear the log (typically after it has been sent on email) +def clearLog(): + try: + logHandler.doRollover() + except: + print("LOGGING FAILED") + raise + + +# Returns the timestamp residing on the first line of the logfile. Used for checking the time of creation +def getTimestamp(): + try: + with open(logFileName, "r") as f: + f.seek(0) + return f.readline() + except: + print("LOGGING FAILED") + + +def addTimestamp(): + try: + with open(logFileName, "a") as f: + f.write(str(time.time()) + os.linesep) + except: + print("LOGGING FAILED") + + +# Returns the entire content of the logfile. Used when sending emails +def readAll(): + try: + text = "" + with open(logFileName, "r") as f: + text = f.read() + return text + except: + print("LOGGING FAILED") + + +def addLogHandler(logHandler): + global logHandlerArray + logger = logging.getLogger() + logger.addHandler(logHandler) + logger.setLevel(logging.INFO) + logHandlerArray.append(logHandler) + + +def removeLogHandler(logHandler): + global logHandlerArray + logger = logging.getLogger() + logger.removeHandler(logHandler) + logHandlerArray.remove(logHandler) + + +class MyRotatingFileHandler(logHandlers.RotatingFileHandler): + def doRollover(self): + try: + logHandlers.RotatingFileHandler.doRollover(self) + addTimestamp() + self.maxBytes = myMaxBytes + except: + # There have been permissions issues with the log files. + self.maxBytes += int(myMaxBytes / 2) + + +class LogFlusher(threading.Thread): + def __init__(self, logHandler): + threading.Thread.__init__(self) + + self.daemon = True + self.handler = logHandler + self.exit = threading.Event() + + self.start() + + def run(self): + while True: + if self.exit.wait(10): + try: + self.doFlush() + except AttributeError as e: + print(e) + break + self.doFlush() + + def doFlush(self): + self.handler.flush() + os.fsync(self.handler.stream.fileno()) + + def stop(self): + self.exit.set() + + +if __name__ == "__main__": + initLogger() + for i in range(50): + logging.info("test log no. " + str(i)) + print("test log no. ", i) diff --git a/modules/wireshark/extcap/SnifferAPI/Notifications.py b/modules/wireshark/extcap/SnifferAPI/Notifications.py new file mode 100644 index 0000000..b7cba37 --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/Notifications.py @@ -0,0 +1,92 @@ +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +import threading, logging + + +class Notification: + def __init__(self, key, msg=None): + if type(key) is not str: + raise TypeError("Invalid notification key: " + str(key)) + self.key = key + self.msg = msg + + def __repr__(self): + return "Notification (key: %s, msg: %s)" % (str(self.key), str(self.msg)) + + +class Notifier: + def __init__(self, callbacks=[], **kwargs): + self.callbacks = {} + self.callbackLock = threading.RLock() + + for callback in callbacks: + self.subscribe(*callback) + + def clearCallbacks(self): + with self.callbackLock: + self.callbacks.clear() + + def subscribe(self, key, callback): + with self.callbackLock: + if callback not in self.getCallbacks(key): + self.getCallbacks(key).append(callback) + + def unSubscribe(self, key, callback): + with self.callbackLock: + if callback in self.getCallbacks(key): + self.getCallbacks(key).remove(callback) + + def getCallbacks(self, key): + with self.callbackLock: + if key not in self.callbacks: + self.callbacks[key] = [] + return self.callbacks[key] + + def notify(self, key=None, msg=None, notification=None): + with self.callbackLock: + if notification == None: + notification = Notification(key, msg) + + for callback in self.getCallbacks(notification.key): + callback(notification) + + for callback in self.getCallbacks("*"): + callback(notification) + + def passOnNotification(self, notification): + self.notify(notification=notification) diff --git a/modules/wireshark/extcap/SnifferAPI/Packet.py b/modules/wireshark/extcap/SnifferAPI/Packet.py new file mode 100644 index 0000000..bc4abd9 --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/Packet.py @@ -0,0 +1,651 @@ +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from . import UART, Exceptions, Notifications +import time, logging, os, sys, serial +from .Types import * + +ADV_ACCESS_ADDRESS = [0xD6, 0xBE, 0x89, 0x8E] + +SYNCWORD_POS = 0 +PAYLOAD_LEN_POS_V1 = 1 +PAYLOAD_LEN_POS = 0 +PROTOVER_POS = PAYLOAD_LEN_POS + 2 +PACKETCOUNTER_POS = PROTOVER_POS + 1 +ID_POS = PACKETCOUNTER_POS + 2 + +BLE_HEADER_LEN_POS = ID_POS + 1 +FLAGS_POS = BLE_HEADER_LEN_POS + 1 +CHANNEL_POS = FLAGS_POS + 1 +RSSI_POS = CHANNEL_POS + 1 +EVENTCOUNTER_POS = RSSI_POS + 1 +TIMESTAMP_POS = EVENTCOUNTER_POS + 2 +BLEPACKET_POS = TIMESTAMP_POS + 4 +TXADD_POS = BLEPACKET_POS + 4 +TXADD_MSK = 0x40 +PAYLOAD_POS = BLE_HEADER_LEN_POS + +HEADER_LENGTH = 6 +BLE_HEADER_LENGTH = 10 + +VALID_ADV_CHANS = [37, 38, 39] + +PACKET_COUNTER_CAP = 2**16 + + +class PacketReader(Notifications.Notifier): + def __init__(self, portnum=None, callbacks=[], baudrate=None): + Notifications.Notifier.__init__(self, callbacks) + self.portnum = portnum + try: + self.uart = UART.Uart(portnum, baudrate) + except serial.SerialException as e: + logging.exception("Error opening UART %s" % str(e)) + self.uart = UART.Uart() + self.packetCounter = 0 + self.lastReceivedPacketCounter = 0 + self.lastReceivedPacket = None + self.lastReceivedTimestampPacket = None + self.supportedProtocolVersion = PROTOVER_V3 + + def setup(self): + pass + + def doExit(self): + # This method will always join the Uart worker thread + self.uart.close() + # Clear method references to avoid uncollectable cyclic references + self.clearCallbacks() + + # This function takes a byte list, encode it in SLIP protocol and return the encoded byte list + def encodeToSLIP(self, byteList): + tempSLIPBuffer = [] + tempSLIPBuffer.append(SLIP_START) + for i in byteList: + if i == SLIP_START: + tempSLIPBuffer.append(SLIP_ESC) + tempSLIPBuffer.append(SLIP_ESC_START) + elif i == SLIP_END: + tempSLIPBuffer.append(SLIP_ESC) + tempSLIPBuffer.append(SLIP_ESC_END) + elif i == SLIP_ESC: + tempSLIPBuffer.append(SLIP_ESC) + tempSLIPBuffer.append(SLIP_ESC_ESC) + else: + tempSLIPBuffer.append(i) + tempSLIPBuffer.append(SLIP_END) + return tempSLIPBuffer + + # This function uses getSerialByte() function to get SLIP encoded bytes from the serial port and return a decoded byte list + # Based on https://github.com/mehdix/pyslip/ + def decodeFromSLIP(self, timeout=None, complete_timeout=None): + dataBuffer = [] + startOfPacket = False + endOfPacket = False + + if complete_timeout is not None: + time_start = time.time() + + while not startOfPacket and ( + complete_timeout is None or (time.time() - time_start < complete_timeout) + ): + res = self.getSerialByte(timeout) + startOfPacket = res == SLIP_START + + while not endOfPacket and ( + complete_timeout is None or (time.time() - time_start < complete_timeout) + ): + serialByte = self.getSerialByte(timeout) + if serialByte == SLIP_END: + endOfPacket = True + elif serialByte == SLIP_ESC: + serialByte = self.getSerialByte(timeout) + if serialByte == SLIP_ESC_START: + dataBuffer.append(SLIP_START) + elif serialByte == SLIP_ESC_END: + dataBuffer.append(SLIP_END) + elif serialByte == SLIP_ESC_ESC: + dataBuffer.append(SLIP_ESC) + else: + dataBuffer.append(SLIP_END) + else: + dataBuffer.append(serialByte) + if not endOfPacket: + raise Exceptions.UARTPacketError( + "Exceeded max timeout of %f seconds." % complete_timeout + ) + return dataBuffer + + # This function read byte chuncks from the serial port and return one byte at a time + # Based on https://github.com/mehdix/pyslip/ + def getSerialByte(self, timeout=None): + serialByte = self.uart.readByte(timeout) + if serialByte is None: + raise Exceptions.SnifferTimeout("Packet read timed out.") + return serialByte + + def handlePacketHistory(self, packet): + # Reads and validates packet counter + if ( + self.lastReceivedPacket is not None + and packet.packetCounter + != (self.lastReceivedPacket.packetCounter + 1) % PACKET_COUNTER_CAP + and self.lastReceivedPacket.packetCounter != 0 + ): + + logging.info( + "gap in packets, between " + + str(self.lastReceivedPacket.packetCounter) + + " and " + + str(packet.packetCounter) + + " packet before: " + + str(self.lastReceivedPacket.packetList) + + " packet after: " + + str(packet.packetList) + ) + + self.lastReceivedPacket = packet + if packet.id in [EVENT_PACKET_DATA_PDU, EVENT_PACKET_ADV_PDU]: + self.lastReceivedTimestampPacket = packet + + def getPacketTime(self, packet): + ble_payload_length = self.lastReceivedPacket.payloadLength - BLE_HEADER_LENGTH + + if packet.phy == PHY_1M: + return 8 * (1 + ble_payload_length) + elif packet.phy == PHY_2M: + return 4 * (2 + ble_payload_length) + elif packet.phy == PHY_CODED: + # blePacket is not assigned if not packet is "OK" (CRC error) + ci = packet.packetList[BLEPACKET_POS + 4] + fec2_block_len = ble_payload_length - 4 - 1 + fec1_block_us = 80 + 256 + 16 + 24 + if ci == PHY_CODED_CI_S8: + return fec1_block_us + 64 * fec2_block_len + 24 + elif ci == PHY_CODED_CI_S2: + return fec1_block_us + 16 * fec2_block_len + 6 + # Unknown PHY or Coding Indicator + return 0 + + def convertPacketListProtoVer2(self, packet): + # Convert to version 2 + packet.packetList[PROTOVER_POS] = 2 + + # Convert to common packet ID + if packet.packetList[ID_POS] == EVENT_PACKET_ADV_PDU: + packet.packetList[ID_POS] = EVENT_PACKET_DATA_PDU + + if packet.packetList[ID_POS] != EVENT_PACKET_DATA_PDU: + # These types do not have a timestamp + return + + # Convert time-stamp to End to Start delta + time_delta = 0 + if ( + self.lastReceivedTimestampPacket is not None + and self.lastReceivedTimestampPacket.valid + ): + time_delta = packet.timestamp - ( + self.lastReceivedTimestampPacket.timestamp + + self.getPacketTime(self.lastReceivedTimestampPacket) + ) + + time_delta = toLittleEndian(time_delta, 4) + packet.packetList[TIMESTAMP_POS] = time_delta[0] + packet.packetList[TIMESTAMP_POS + 1] = time_delta[1] + packet.packetList[TIMESTAMP_POS + 2] = time_delta[2] + packet.packetList[TIMESTAMP_POS + 3] = time_delta[3] + + def handlePacketCompatibility(self, packet): + if ( + self.supportedProtocolVersion == PROTOVER_V2 + and packet.packetList[PROTOVER_POS] > PROTOVER_V2 + ): + self.convertPacketListProtoVer2(packet) + + def setSupportedProtocolVersion(self, supportedProtocolVersion): + if supportedProtocolVersion != PROTOVER_V3: + logging.info( + "Using packet compatibility, converting packets to protocol version %d", + supportedProtocolVersion, + ) + self.supportedProtocolVersion = supportedProtocolVersion + + def getPacket(self, timeout=None): + packetList = [] + try: + packetList = self.decodeFromSLIP(timeout) + except Exceptions.UARTPacketError: # FIXME: This is never thrown... + logging.exception("") + return None + else: + packet = Packet(packetList) + if packet.valid: + self.handlePacketCompatibility(packet) + self.handlePacketHistory(packet) + return packet + + def sendPacket(self, id, payload): + packetList = ( + [HEADER_LENGTH] + + [len(payload)] + + [PROTOVER_V1] + + toLittleEndian(self.packetCounter, 2) + + [id] + + payload + ) + packetList = self.encodeToSLIP(packetList) + self.packetCounter += 1 + self.uart.writeList(packetList) + + def sendScan(self, findScanRsp=False, findAux=False, scanCoded=False): + flags0 = findScanRsp | (findAux << 1) | (scanCoded << 2) + self.sendPacket(REQ_SCAN_CONT, [flags0]) + logging.info("Scan flags: %s" % bin(flags0)) + + def sendFollow( + self, + addr, + followOnlyAdvertisements=False, + followOnlyLegacy=False, + followCoded=False, + ): + flags0 = followOnlyAdvertisements | (followOnlyLegacy << 1) | (followCoded << 2) + logging.info("Follow flags: %s" % bin(flags0)) + self.sendPacket(REQ_FOLLOW, addr + [flags0]) + + def sendPingReq(self): + self.sendPacket(PING_REQ, []) + + def getBytes(self, value, size): + if len(value) < size: + value = [0] * (size - len(value)) + value + else: + value = value[:size] + + return value + + def sendTK(self, TK): + TK = self.getBytes(TK, 16) + self.sendPacket(SET_TEMPORARY_KEY, TK) + logging.info("Sent TK to sniffer: " + str(TK)) + + def sendPrivateKey(self, pk): + pk = self.getBytes(pk, 32) + self.sendPacket(SET_PRIVATE_KEY, pk) + logging.info("Sent private key to sniffer: " + str(pk)) + + def sendLegacyLTK(self, ltk): + ltk = self.getBytes(ltk, 16) + self.sendPacket(SET_LEGACY_LONG_TERM_KEY, ltk) + logging.info("Sent Legacy LTK to sniffer: " + str(ltk)) + + def sendSCLTK(self, ltk): + ltk = self.getBytes(ltk, 16) + self.sendPacket(SET_SC_LONG_TERM_KEY, ltk) + logging.info("Sent SC LTK to sniffer: " + str(ltk)) + + def sendIRK(self, irk): + irk = self.getBytes(irk, 16) + self.sendPacket(SET_IDENTITY_RESOLVING_KEY, irk) + logging.info("Sent IRK to sniffer: " + str(irk)) + + def sendSwitchBaudRate(self, newBaudRate): + self.sendPacket(SWITCH_BAUD_RATE_REQ, toLittleEndian(newBaudRate, 4)) + + def switchBaudRate(self, newBaudRate): + self.uart.switchBaudRate(newBaudRate) + + def sendHopSequence(self, hopSequence): + for chan in hopSequence: + if chan not in VALID_ADV_CHANS: + raise Exceptions.InvalidAdvChannel( + "%s is not an adv channel" % str(chan) + ) + payload = [len(hopSequence)] + hopSequence + [37] * (3 - len(hopSequence)) + self.sendPacket(SET_ADV_CHANNEL_HOP_SEQ, payload) + self.notify("NEW_ADV_HOP_SEQ", {"hopSequence": hopSequence}) + + def sendVersionReq(self): + self.sendPacket(REQ_VERSION, []) + + def sendTimestampReq(self): + self.sendPacket(REQ_TIMESTAMP, []) + + def sendGoIdle(self): + self.sendPacket(GO_IDLE, []) + + +class Packet: + def __init__(self, packetList): + try: + if not packetList: + raise Exceptions.InvalidPacketException( + "packet list not valid: %s" % str(packetList) + ) + + self.protover = packetList[PROTOVER_POS] + + if self.protover > PROTOVER_V3: + logging.exception( + "Unsupported protocol version %s" % str(self.protover) + ) + raise RuntimeError( + "Unsupported protocol version %s" % str(self.protover) + ) + + self.packetCounter = parseLittleEndian( + packetList[PACKETCOUNTER_POS : PACKETCOUNTER_POS + 2] + ) + self.id = packetList[ID_POS] + + if int(self.protover) == PROTOVER_V1: + self.payloadLength = packetList[PAYLOAD_LEN_POS_V1] + else: + self.payloadLength = parseLittleEndian( + packetList[PAYLOAD_LEN_POS : PAYLOAD_LEN_POS + 2] + ) + + self.packetList = packetList + self.readPayload(packetList) + + except Exceptions.InvalidPacketException as e: + logging.error("Invalid packet: %s" % str(e)) + self.OK = False + self.valid = False + except Exception as e: + logging.exception("packet creation error %s" % str(e)) + logging.info("packetList: " + str(packetList)) + self.OK = False + self.valid = False + + def __repr__(self): + return "UART packet, type: " + str(self.id) + ", PC: " + str(self.packetCounter) + + def readPayload(self, packetList): + self.blePacket = None + self.OK = False + + if not self.validatePacketList(packetList): + raise Exceptions.InvalidPacketException( + "packet list not valid: %s" % str(packetList) + ) + else: + self.valid = True + + self.payload = packetList[PAYLOAD_POS : PAYLOAD_POS + self.payloadLength] + + if self.id == EVENT_PACKET_ADV_PDU or self.id == EVENT_PACKET_DATA_PDU: + try: + self.bleHeaderLength = packetList[BLE_HEADER_LEN_POS] + if self.bleHeaderLength == BLE_HEADER_LENGTH: + self.flags = packetList[FLAGS_POS] + self.readFlags() + self.channel = packetList[CHANNEL_POS] + self.rawRSSI = packetList[RSSI_POS] + self.RSSI = -self.rawRSSI + self.eventCounter = parseLittleEndian( + packetList[EVENTCOUNTER_POS : EVENTCOUNTER_POS + 2] + ) + + self.timestamp = parseLittleEndian( + packetList[TIMESTAMP_POS : TIMESTAMP_POS + 4] + ) + + # The hardware adds a padding byte which isn't sent on air. + # We remove it, and update the payload length in the packet list. + if self.phy == PHY_CODED: + self.packetList.pop(BLEPACKET_POS + 6 + 1) + else: + self.packetList.pop(BLEPACKET_POS + 6) + self.payloadLength -= 1 + if self.protover >= PROTOVER_V2: + # Write updated payload length back to the packet list. + payloadLength = toLittleEndian(self.payloadLength, 2) + packetList[PAYLOAD_LEN_POS] = payloadLength[0] + packetList[PAYLOAD_LEN_POS + 1] = payloadLength[1] + else: # PROTOVER_V1 + packetList[PAYLOAD_LEN_POS_V1] = self.payloadLength + else: + logging.info("Invalid BLE Header Length " + str(packetList)) + self.valid = False + + if self.OK: + try: + if self.protover >= PROTOVER_V3: + packet_type = ( + PACKET_TYPE_ADVERTISING + if self.id == EVENT_PACKET_ADV_PDU + else PACKET_TYPE_DATA + ) + else: + packet_type = ( + PACKET_TYPE_ADVERTISING + if packetList[BLEPACKET_POS : BLEPACKET_POS + 4] + == ADV_ACCESS_ADDRESS + else PACKET_TYPE_DATA + ) + + self.blePacket = BlePacket( + packet_type, packetList[BLEPACKET_POS:], self.phy + ) + except Exception as e: + logging.exception("blePacket error %s" % str(e)) + except Exception as e: + # malformed packet + logging.exception("packet error %s" % str(e)) + self.OK = False + elif self.id == PING_RESP: + if self.protover < PROTOVER_V3: + self.version = parseLittleEndian( + packetList[PAYLOAD_POS : PAYLOAD_POS + 2] + ) + elif self.id == RESP_VERSION: + self.version = "".join([chr(i) for i in packetList[PAYLOAD_POS:]]) + elif self.id == RESP_TIMESTAMP: + self.timestamp = parseLittleEndian( + packetList[PAYLOAD_POS : PAYLOAD_POS + 4] + ) + elif self.id == SWITCH_BAUD_RATE_RESP or self.id == SWITCH_BAUD_RATE_REQ: + self.baudRate = parseLittleEndian(packetList[PAYLOAD_POS : PAYLOAD_POS + 4]) + else: + logging.info("Unknown packet ID") + + def readFlags(self): + self.crcOK = not not (self.flags & 1) + self.direction = not not (self.flags & 2) + self.encrypted = not not (self.flags & 4) + self.micOK = not not (self.flags & 8) + self.phy = (self.flags >> 4) & 7 + self.OK = self.crcOK and (self.micOK or not self.encrypted) + + def getList(self): + return self.packetList + + def validatePacketList(self, packetList): + try: + if (self.payloadLength + HEADER_LENGTH) == len(packetList): + return True + else: + return False + except: + logging.exception("Invalid packet: %s" % str(packetList)) + return False + + +class BlePacket: + def __init__(self, type, packetList, phy): + self.type = type + + offset = 0 + offset = self.extractAccessAddress(packetList, offset) + offset = self.extractFormat(packetList, phy, offset) + + if self.type == PACKET_TYPE_ADVERTISING: + offset = self.extractAdvHeader(packetList, offset) + else: + offset = self.extractConnHeader(packetList, offset) + + offset = self.extractLength(packetList, offset) + self.payload = packetList[offset:] + + if self.type == PACKET_TYPE_ADVERTISING: + offset = self.extractAddresses(packetList, offset) + self.extractName(packetList, offset) + + def __repr__(self): + return "BLE packet, AAddr: " + str(self.accessAddress) + + def extractAccessAddress(self, packetList, offset): + self.accessAddress = packetList[offset : offset + 4] + return offset + 4 + + def extractFormat(self, packetList, phy, offset): + self.coded = phy == PHY_CODED + if self.coded: + self.codingIndicator = packetList[offset] & 3 + return offset + 1 + + return offset + + def extractAdvHeader(self, packetList, offset): + self.advType = packetList[offset] & 15 + self.txAddrType = (packetList[offset] >> 6) & 1 + if self.advType in [1, 3, 5]: + self.rxAddrType = (packetList[offset] << 7) & 1 + elif self.advType == 7: + flags = packetList[offset + 2] + if flags & 0x02: + self.rxAddrType = (packetList[offset] << 7) & 1 + return offset + 1 + + def extractConnHeader(self, packetList, offset): + self.llid = packetList[offset] & 3 + self.sn = (packetList[offset] >> 2) & 1 + self.nesn = (packetList[offset] >> 3) & 1 + self.md = (packetList[offset] >> 4) & 1 + return offset + 1 + + def extractAddresses(self, packetList, offset): + addr = None + scanAddr = None + + if self.advType in [0, 1, 2, 4, 6]: + addr = packetList[offset : offset + 6] + addr.reverse() + addr += [self.txAddrType] + offset += 6 + + if self.advType in [3, 5]: + scanAddr = packetList[offset : offset + 6] + scanAddr.reverse() + scanAddr += [self.txAddrType] + offset += 6 + addr = packetList[offset : offset + 6] + addr.reverse() + addr += [self.rxAddrType] + offset += 6 + + if self.advType == 1: + scanAddr = packetList[offset : offset + 6] + scanAddr.reverse() + scanAddr += [self.rxAddrType] + offset += 6 + + if self.advType == 7: + ext_header_len = packetList[offset] & 0x3F + offset += 1 + + ext_header_offset = offset + flags = packetList[offset] + ext_header_offset += 1 + + if flags & 0x01: + addr = packetList[ext_header_offset : ext_header_offset + 6] + addr.reverse() + addr += [self.txAddrType] + ext_header_offset += 6 + + if flags & 0x02: + scanAddr = packetList[ext_header_offset : ext_header_offset + 6] + scanAddr.reverse() + scanAddr += [self.rxAddrType] + ext_header_offset += 6 + + offset += ext_header_len + + self.advAddress = addr + self.scanAddress = scanAddr + return offset + + def extractName(self, packetList, offset): + name = "" + if self.advType in [0, 2, 4, 6, 7]: + i = offset + while i < len(packetList): + length = packetList[i] + if (i + length + 1) > len(packetList) or length == 0: + break + type = packetList[i + 1] + if type == 8 or type == 9: + nameList = packetList[i + 2 : i + length + 1] + name = "" + for j in nameList: + name += chr(j) + i += length + 1 + name = '"' + name + '"' + elif self.advType == 1: + name = "[ADV_DIRECT_IND]" + + self.name = name + + def extractLength(self, packetList, offset): + self.length = packetList[offset] + return offset + 1 + + +def parseLittleEndian(list): + total = 0 + for i in range(len(list)): + total += list[i] << (8 * i) + return total + + +def toLittleEndian(value, size): + list = [0] * size + for i in range(size): + list[i] = (value >> (i * 8)) % 256 + return list diff --git a/modules/wireshark/extcap/SnifferAPI/Pcap.py b/modules/wireshark/extcap/SnifferAPI/Pcap.py new file mode 100644 index 0000000..8b0445a --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/Pcap.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 + +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +import struct + + +# See: +# - https://github.com/pcapng/pcapng +# - https://www.tcpdump.org/linktypes/LINKTYPE_NORDIC_BLE.html +PACKET_HEADER = struct.Struct("= PROTOVER_V3: + if self._last_time is None: + # Timestamp from Host + packet.time = time.time() + else: + # Timestamp using reference and packet timestamp diff + if packet.timestamp < self._last_timestamp: + time_diff = (1 << 32) - (self._last_timestamp - packet.timestamp) + else: + time_diff = packet.timestamp - self._last_timestamp + + packet.time = self._last_time + (time_diff / 1_000_000) + + self._last_time = packet.time + self._last_timestamp = packet.timestamp + else: + # Timestamp from Host + packet.time = time.time() + + self._appendPacket(packet) + + self.notify("NEW_BLE_PACKET", {"packet": packet}) + self._captureHandler.writePacket(packet) + + self._nProcessedPackets += 1 + if packet.OK: + try: + if packet.blePacket.type == PACKET_TYPE_ADVERTISING: + + if self.state == STATE_FOLLOWING and packet.blePacket.advType == 5: + self._connectionAccessAddress = packet.blePacket.accessAddress + + if self.state == STATE_FOLLOWING and packet.blePacket.advType == 4: + newDevice = Devices.Device( + address=packet.blePacket.advAddress, + name=packet.blePacket.name, + RSSI=packet.RSSI, + ) + self._devices.appendOrUpdate(newDevice) + + if self.state == STATE_SCANNING: + if ( + packet.blePacket.advType in [0, 1, 2, 4, 6, 7] + and packet.blePacket.advAddress != None + and packet.crcOK + and not packet.direction + ): + newDevice = Devices.Device( + address=packet.blePacket.advAddress, + name=packet.blePacket.name, + RSSI=packet.RSSI, + ) + self._devices.appendOrUpdate(newDevice) + + except Exception as e: + logging.exception("packet processing error %s" % str(e)) + self.notify("PACKET_PROCESSING_ERROR", {"errorString": str(e)}) + + def _continuouslyPipe(self): + while not self._exit: + try: + packet = self._packetReader.getPacket(timeout=12) + if packet == None or not packet.valid: + raise Exceptions.InvalidPacketException("") + except Exceptions.SnifferTimeout as e: + logging.info(str(e)) + packet = None + except (SerialException, ValueError): + logging.exception("UART read error") + logging.error("Lost contact with sniffer hardware.") + self._doExit() + except Exceptions.InvalidPacketException: + pass + else: + if ( + packet.id == EVENT_PACKET_DATA_PDU + or packet.id == EVENT_PACKET_ADV_PDU + ): + self._processBLEPacket(packet) + elif packet.id == EVENT_FOLLOW: + # This packet has no value for the user. + pass + elif packet.id == EVENT_CONNECT: + self._connectEventPacketCounterValue = packet.packetCounter + self._inConnection = True + # copy it because packets are eventually deleted + self._currentConnectRequest = copy.copy( + self._findPacketByPacketCounter( + self._connectEventPacketCounterValue - 1 + ) + ) + elif packet.id == EVENT_DISCONNECT: + if self._inConnection: + self._packetsInLastConnection = ( + packet.packetCounter - self._connectEventPacketCounterValue + ) + self._inConnection = False + elif packet.id == SWITCH_BAUD_RATE_RESP and self._switchingBaudRate: + self._switchingBaudRate = False + if packet.baudRate == self._proposedBaudRate: + self._packetReader.switchBaudRate(self._proposedBaudRate) + else: + self._switchBaudRate(packet.baudRate) + elif packet.id == PING_RESP: + if hasattr(packet, "version"): + versions = { + 1116: "3.1.0", + 1115: "3.0.0", + 1114: "2.0.0", + 1113: "2.0.0-beta-3", + 1112: "2.0.0-beta-1", + } + self._fwversion = versions.get( + packet.version, "SVN rev: %d" % packet.version + ) + logging.info("Firmware version %s" % self._fwversion) + elif packet.id == RESP_VERSION: + self._fwversion = packet.version + logging.info("Firmware version %s" % self._fwversion) + elif packet.id == RESP_TIMESTAMP: + # Use current time as timestamp reference + self._last_time = time.time() + self._last_timestamp = packet.timestamp + + lt = time.localtime(self._last_time) + usecs = int((self._last_time - int(self._last_time)) * 1_000_000) + logging.info( + f"Firmware timestamp {self._last_timestamp} reference: " + f'{time.strftime("%b %d %Y %X", lt)}.{usecs} {time.strftime("%Z", lt)}' + ) + else: + logging.info("Unknown packet ID") + + def _findPacketByPacketCounter(self, packetCounterValue): + with self._packetListLock: + for i in range(-1, -1 - len(self._packets), -1): + # iterate backwards through packets + if self._packets[i].packetCounter == packetCounterValue: + return self._packets[i] + return None + + def _startScanning(self, findScanRsp=False, findAux=False, scanCoded=False): + logging.info("starting scan") + + if self.state == STATE_FOLLOWING: + logging.info("Stopped sniffing device") + + self._setState(STATE_SCANNING) + self._packetReader.sendScan(findScanRsp, findAux, scanCoded) + self._packetReader.sendTK([0]) + + def _doExit(self): + self._exit = True + self.notify("APP_EXIT") + self._packetReader.doExit() + # Clear method references to avoid uncollectable cyclic references + self.clearCallbacks() + self._devices.clearCallbacks() + + def _startFollowing( + self, + device, + followOnlyAdvertisements=False, + followOnlyLegacy=False, + followCoded=False, + ): + self._devices.setFollowed(device) + logging.info( + "Sniffing device " + + str(self._devices.index(device)) + + ' - "' + + device.name + + '"' + ) + self._packetReader.sendFollow( + device.address, followOnlyAdvertisements, followOnlyLegacy, followCoded + ) + self._setState(STATE_FOLLOWING) + + def _clearDevices(self): + self._devices.clear() + + def _appendPacket(self, packet): + with self._packetListLock: + if len(self._packets) > 100000: + self._packets = self._packets[20000:] + self._packets.append(packet) + + def _getPackets(self, number=-1): + with self._packetListLock: + returnList = self._packets[0:number] + self._packets = self._packets[number:] + return returnList + + def _clearPackets(self): + with self._packetListLock: + del self._packets[:] diff --git a/modules/wireshark/extcap/SnifferAPI/Types.py b/modules/wireshark/extcap/SnifferAPI/Types.py new file mode 100644 index 0000000..eac7609 --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/Types.py @@ -0,0 +1,90 @@ +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +SLIP_START = 0xAB +SLIP_END = 0xBC +SLIP_ESC = 0xCD +SLIP_ESC_START = SLIP_START + 1 +SLIP_ESC_END = SLIP_END + 1 +SLIP_ESC_ESC = SLIP_ESC + 1 + +PROTOVER_V3 = 3 +PROTOVER_V2 = 2 +PROTOVER_V1 = 1 + +# UART protocol packet codes (see sniffer_uart_protocol.pdf) +REQ_FOLLOW = 0x00 +EVENT_FOLLOW = 0x01 +EVENT_PACKET_ADV_PDU = 0x02 +EVENT_CONNECT = 0x05 +EVENT_PACKET_DATA_PDU = 0x06 +REQ_SCAN_CONT = 0x07 +EVENT_DISCONNECT = 0x09 +SET_TEMPORARY_KEY = 0x0C +PING_REQ = 0x0D +PING_RESP = 0x0E +SWITCH_BAUD_RATE_REQ = 0x13 +SWITCH_BAUD_RATE_RESP = 0x14 +SET_ADV_CHANNEL_HOP_SEQ = 0x17 +SET_PRIVATE_KEY = 0x18 +SET_LEGACY_LONG_TERM_KEY = 0x19 +SET_SC_LONG_TERM_KEY = 0x1A +REQ_VERSION = 0x1B +RESP_VERSION = 0x1C +REQ_TIMESTAMP = 0x1D +RESP_TIMESTAMP = 0x1E +SET_IDENTITY_RESOLVING_KEY = 0x1F +GO_IDLE = 0xFE + +PACKET_TYPE_UNKNOWN = 0x00 +PACKET_TYPE_ADVERTISING = 0x01 +PACKET_TYPE_DATA = 0x02 + +ADV_TYPE_ADV_IND = 0x0 +ADV_TYPE_ADV_DIRECT_IND = 0x1 +ADV_TYPE_ADV_NONCONN_IND = 0x2 +ADV_TYPE_ADV_SCAN_IND = 0x6 +ADV_TYPE_SCAN_REQ = 0x3 +ADV_TYPE_SCAN_RSP = 0x4 +ADV_TYPE_CONNECT_REQ = 0x5 +ADV_TYPE_ADV_EXT_IND = 0x7 + +PHY_1M = 0 +PHY_2M = 1 +PHY_CODED = 2 + +PHY_CODED_CI_S8 = 0 +PHY_CODED_CI_S2 = 1 diff --git a/modules/wireshark/extcap/SnifferAPI/UART.py b/modules/wireshark/extcap/SnifferAPI/UART.py new file mode 100644 index 0000000..ecd16d2 --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/UART.py @@ -0,0 +1,238 @@ +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import collections +import logging +import serial +from threading import Thread, Event + +import serial.tools.list_ports as list_ports + +from . import Exceptions +from . import Packet +from . import Filelock + +import os + +if os.name == "posix": + import termios + +SNIFFER_OLD_DEFAULT_BAUDRATE = 460800 +# Baudrates that should be tried (add more if required) +SNIFFER_BAUDRATES = [1000000, 460800] + + +def find_sniffer(write_data=False): + open_ports = list_ports.comports() + + sniffers = [] + for port in [x.device for x in open_ports]: + for rate in SNIFFER_BAUDRATES: + reader = None + l_errors = [ + serial.SerialException, + ValueError, + Exceptions.LockedException, + OSError, + ] + if os.name == "posix": + l_errors.append(termios.error) + try: + reader = Packet.PacketReader(portnum=port, baudrate=rate) + try: + if write_data: + reader.sendPingReq() + _ = reader.decodeFromSLIP(0.1, complete_timeout=0.1) + else: + _ = reader.decodeFromSLIP(0.3, complete_timeout=0.3) + + # FIXME: Should add the baud rate here, but that will be a breaking change + sniffers.append(port) + break + except (Exceptions.SnifferTimeout, Exceptions.UARTPacketError): + pass + except tuple(l_errors): + continue + finally: + if reader is not None: + reader.doExit() + return sniffers + + +def find_sniffer_baudrates(port, write_data=False): + for rate in SNIFFER_BAUDRATES: + reader = None + try: + reader = Packet.PacketReader(portnum=port, baudrate=rate) + try: + if write_data: + reader.sendPingReq() + _ = reader.decodeFromSLIP(0.1, complete_timeout=0.1) + else: + _ = reader.decodeFromSLIP(0.3, complete_timeout=0.3) + + # TODO: possibly include additional rates based on protocol version + return {"default": rate, "other": []} + except (Exceptions.SnifferTimeout, Exceptions.UARTPacketError): + pass + finally: + if reader is not None: + reader.doExit() + return None + + +class Uart: + def __init__(self, portnum=None, baudrate=None): + self.ser = None + try: + if baudrate is not None and baudrate not in SNIFFER_BAUDRATES: + raise Exception("Invalid baudrate: " + str(baudrate)) + + logging.info("Opening serial port {}".format(portnum)) + + self.portnum = portnum + if self.portnum: + Filelock.lock(portnum) + + self.ser = serial.Serial( + port=portnum, baudrate=9600, rtscts=True, exclusive=True + ) + self.ser.baudrate = baudrate + + except Exception: + if self.ser: + self.ser.close() + self.ser = None + raise + + self.read_queue = collections.deque() + self.read_queue_has_data = Event() + + self.worker_thread = Thread(target=self._read_worker) + self.reading = True + self.worker_thread.setDaemon(True) + self.worker_thread.start() + + def _read_worker(self): + self.ser.reset_input_buffer() + while self.reading: + try: + # Read any data available, or wait for at least one byte + data_read = self.ser.read(self.ser.in_waiting or 1) + # logging.info('type: {}'.format(data_read.__class__)) + self._read_queue_extend(data_read) + except serial.SerialException as e: + logging.info("Unable to read UART: %s" % e) + self.reading = False + return + + def close(self): + if self.ser: + logging.info("closing UART") + self.reading = False + # Wake any threads waiting on the queue + self.read_queue_has_data.set() + if hasattr(self.ser, "cancel_read"): + self.ser.cancel_read() + self.worker_thread.join() + self.ser.close() + else: + self.ser.close() + self.worker_thread.join() + self.ser = None + + if self.portnum: + Filelock.unlock(self.portnum) + + def __del__(self): + self.close() + + def switchBaudRate(self, newBaudRate): + self.ser.baudrate = newBaudRate + + def readByte(self, timeout=None): + r = self._read_queue_get(timeout) + return r + + def writeList(self, array): + try: + self.ser.write(array) + except serial.SerialTimeoutException: + logging.info("Got write timeout, ignoring error") + + except serial.SerialException as e: + self.ser.close() + raise e + + def _read_queue_extend(self, data): + if len(data) > 0: + self.read_queue.extend(data) + self.read_queue_has_data.set() + + def _read_queue_get(self, timeout=None): + data = None + if self.read_queue_has_data.wait(timeout): + self.read_queue_has_data.clear() + try: + data = self.read_queue.popleft() + except IndexError: + # This will happen when the class is destroyed + return None + if len(self.read_queue) > 0: + self.read_queue_has_data.set() + return data + + +def list_serial_ports(): + # Scan for available ports. + return list_ports.comports() + + +if __name__ == "__main__": + import time + + t_start = time.time() + s = find_sniffer() + tn = time.time() + print(s) + print("find_sniffer took %f seconds" % (tn - t_start)) + for p in s: + t = time.time() + print(find_sniffer_baudrates(p)) + tn = time.time() + print("find_sniffer_baudrate took %f seconds" % (tn - t)) + tn = time.time() + print("total runtime %f" % (tn - t_start)) diff --git a/modules/wireshark/extcap/SnifferAPI/__init__.py b/modules/wireshark/extcap/SnifferAPI/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/modules/wireshark/extcap/SnifferAPI/version.py b/modules/wireshark/extcap/SnifferAPI/version.py new file mode 100644 index 0000000..5b94c32 --- /dev/null +++ b/modules/wireshark/extcap/SnifferAPI/version.py @@ -0,0 +1,37 @@ +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +VERSION_STRING = "4.1.1" diff --git a/modules/wireshark/extcap/nrf_sniffer_ble.py b/modules/wireshark/extcap/nrf_sniffer_ble.py new file mode 100644 index 0000000..1aa1380 --- /dev/null +++ b/modules/wireshark/extcap/nrf_sniffer_ble.py @@ -0,0 +1,991 @@ +#!/usr/bin/env python3 + +# Copyright (c) Nordic Semiconductor ASA +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form, except as embedded into a Nordic +# Semiconductor ASA integrated circuit in a product or a software update for +# such product, must reproduce the above copyright notice, this list of +# conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of Nordic Semiconductor ASA nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# 4. This software, with or without modification, must only be used with a +# Nordic Semiconductor ASA integrated circuit. +# +# 5. Any software provided in binary form under this license must not be reverse +# engineered, decompiled, modified and/or disassembled. +# +# THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +""" +Wireshark extcap wrapper for the nRF Sniffer for Bluetooth LE by Nordic Semiconductor. +""" + +import os +import sys +import argparse +import re +import time +import struct +import logging + +from SnifferAPI import Logger + +try: + import serial +except ImportError: + Logger.initLogger() + logging.error( + f'pyserial not found, please run: "{sys.executable} -m pip install -r requirements.txt" and retry' + ) + sys.exit( + f'pyserial not found, please run: "{sys.executable} -m pip install -r requirements.txt" and retry' + ) + +from SnifferAPI import Sniffer, UART, Devices, Pcap, Exceptions + +ERROR_USAGE = 0 +ERROR_ARG = 1 +ERROR_INTERFACE = 2 +ERROR_FIFO = 3 +ERROR_INTERNAL = 4 + +CTRL_CMD_INIT = 0 +CTRL_CMD_SET = 1 +CTRL_CMD_ADD = 2 +CTRL_CMD_REMOVE = 3 +CTRL_CMD_ENABLE = 4 +CTRL_CMD_DISABLE = 5 +CTRL_CMD_STATUSBAR = 6 +CTRL_CMD_INFO_MSG = 7 +CTRL_CMD_WARN_MSG = 8 +CTRL_CMD_ERROR_MSG = 9 + +CTRL_ARG_DEVICE = 0 +CTRL_ARG_KEY_TYPE = 1 +CTRL_ARG_KEY_VAL = 2 +CTRL_ARG_ADVHOP = 3 +CTRL_ARG_HELP = 4 +CTRL_ARG_RESTORE = 5 +CTRL_ARG_LOG = 6 +CTRL_ARG_DEVICE_CLEAR = 7 +CTRL_ARG_NONE = 255 + +CTRL_KEY_TYPE_PASSKEY = 0 +CTRL_KEY_TYPE_OOB = 1 +CTRL_KEY_TYPE_LEGACY_LTK = 2 +CTRL_KEY_TYPE_SC_LTK = 3 +CTRL_KEY_TYPE_DH_PRIVATE_KEY = 4 +CTRL_KEY_TYPE_IRK = 5 +CTRL_KEY_TYPE_ADD_ADDR = 6 +CTRL_KEY_TYPE_FOLLOW_ADDR = 7 + +fn_capture = None +fn_ctrl_in = None +fn_ctrl_out = None + +extcap_log_handler = None +extcap_version = None + +# Wireshark nRF Sniffer for Bluetooth LE Toolbar will always cache the last used key and adv hop and send +# this when starting a capture. To ensure that the key and adv hop is always shown correctly +# in the Toolbar, even if the user has changed it but not applied it, we send the last used +# key and adv hop back as a default value. +last_used_key_type = CTRL_KEY_TYPE_PASSKEY +last_used_key_val = "" +last_used_advhop = "37,38,39" + +zero_addr = "[00,00,00,00,00,00,0]" + +# While searching for a selected Device we must not write packets to the pipe until +# the device is found to avoid getting advertising packets from other devices. +write_new_packets = False + +# The RSSI capture filter value given from Wireshark. +rssi_filter = 0 + +# The RSSI filtering is not on when in follow mode. +in_follow_mode = False + +# nRF Sniffer for Bluetooth LE interface option to only capture advertising packets +capture_only_advertising = False +capture_only_legacy_advertising = False +capture_scan_response = True +capture_scan_aux_pointer = True +capture_coded = False + + +def extcap_config(interface): + """List configuration for the given interface""" + print( + "arg {number=0}{call=--only-advertising}{display=Only advertising packets}" + "{tooltip=The sniffer will only capture advertising packets from the selected device}{type=boolflag}{save=true}" + ) + print( + "arg {number=1}{call=--only-legacy-advertising}{display=Only legacy advertising packets}" + "{tooltip=The sniffer will only capture legacy advertising packets from the selected device}{type=boolflag}{save=true}" + ) + print( + "arg {number=2}{call=--scan-follow-rsp}{display=Find scan response data}" + "{tooltip=The sniffer will follow scan requests and scan responses in scan mode}{type=boolflag}{default=true}{save=true}" + ) + print( + "arg {number=3}{call=--scan-follow-aux}{display=Find auxiliary pointer data}" + "{tooltip=The sniffer will follow aux pointers in scan mode}{type=boolflag}{default=true}{save=true}" + ) + print( + "arg {number=3}{call=--coded}{display=Scan and follow devices on LE Coded PHY}" + "{tooltip=Scan for devices and follow advertiser on LE Coded PHY}{type=boolflag}{default=false}{save=true}" + ) + + +def extcap_dlts(interface): + """List DLTs for the given interface""" + print("dlt {number=272}{name=NORDIC_BLE}{display=nRF Sniffer for Bluetooth LE}") + + +def get_baud_rates(interface): + if not hasattr(serial, "__version__") or not serial.__version__.startswith("3."): + raise RuntimeError( + "Too old version of python 'serial' Library. Version 3 required." + ) + return UART.find_sniffer_baudrates(interface) + + +def get_interfaces(): + if not hasattr(serial, "__version__") or not serial.__version__.startswith("3."): + raise RuntimeError( + "Too old version of python 'serial' Library. Version 3 required." + ) + + devices = UART.find_sniffer() + return devices + + +def extcap_interfaces(): + """List available interfaces to capture from""" + print( + "extcap {version=%s}{display=nRF Sniffer for Bluetooth LE}" + "{help=https://www.nordicsemi.com/Software-and-Tools/Development-Tools/nRF-Sniffer-for-Bluetooth-LE}" + % Sniffer.VERSION_STRING + ) + + for interface_port in get_interfaces(): + if sys.platform == "win32": + print( + "interface {value=%s-%s}{display=nRF Sniffer for Bluetooth LE %s}" + % (interface_port, extcap_version, interface_port) + ) + else: + print( + "interface {value=%s-%s}{display=nRF Sniffer for Bluetooth LE}" + % (interface_port, extcap_version) + ) + + print( + "control {number=%d}{type=selector}{display=Device}{tooltip=Device list}" + % CTRL_ARG_DEVICE + ) + print( + "control {number=%d}{type=selector}{display=Key}{tooltip=}" % CTRL_ARG_KEY_TYPE + ) + print( + "control {number=%d}{type=string}{display=Value}" + "{tooltip=6 digit passkey or 16 or 32 bytes encryption key in hexadecimal starting with '0x', big endian format." + "If the entered key is shorter than 16 or 32 bytes, it will be zero-padded in front'}" + "{validation=\\b^(([0-9]{6})|(0x[0-9a-fA-F]{1,64})|([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2}) (public|random))$\\b}" + % CTRL_ARG_KEY_VAL + ) + print( + "control {number=%d}{type=string}{display=Adv Hop}" + "{default=37,38,39}" + "{tooltip=Advertising channel hop sequence. " + "Change the order in which the sniffer switches advertising channels. " + "Valid channels are 37, 38 and 39 separated by comma.}" + r"{validation=^\s*((37|38|39)\s*,\s*){0,2}(37|38|39){1}\s*$}{required=true}" + % CTRL_ARG_ADVHOP + ) + print( + "control {number=%d}{type=button}{display=Clear}{tooltop=Clear or remove device from Device list}" + % CTRL_ARG_DEVICE_CLEAR + ) + print( + "control {number=%d}{type=button}{role=help}{display=Help}{tooltip=Access user guide (launches browser)}" + % CTRL_ARG_HELP + ) + print( + "control {number=%d}{type=button}{role=restore}{display=Defaults}{tooltip=Resets the user interface and clears the log file}" + % CTRL_ARG_RESTORE + ) + print( + "control {number=%d}{type=button}{role=logger}{display=Log}{tooltip=Log per interface}" + % CTRL_ARG_LOG + ) + + print( + "value {control=%d}{value= }{display=All advertising devices}{default=true}" + % CTRL_ARG_DEVICE + ) + print( + "value {control=%d}{value=%s}{display=Follow IRK}" + % (CTRL_ARG_DEVICE, zero_addr) + ) + + print( + "value {control=%d}{value=%d}{display=Legacy Passkey}{default=true}" + % (CTRL_ARG_KEY_TYPE, CTRL_KEY_TYPE_PASSKEY) + ) + print( + "value {control=%d}{value=%d}{display=Legacy OOB data}" + % (CTRL_ARG_KEY_TYPE, CTRL_KEY_TYPE_OOB) + ) + print( + "value {control=%d}{value=%d}{display=Legacy LTK}" + % (CTRL_ARG_KEY_TYPE, CTRL_KEY_TYPE_LEGACY_LTK) + ) + print( + "value {control=%d}{value=%d}{display=SC LTK}" + % (CTRL_ARG_KEY_TYPE, CTRL_KEY_TYPE_SC_LTK) + ) + print( + "value {control=%d}{value=%d}{display=SC Private Key}" + % (CTRL_ARG_KEY_TYPE, CTRL_KEY_TYPE_DH_PRIVATE_KEY) + ) + print( + "value {control=%d}{value=%d}{display=IRK}" + % (CTRL_ARG_KEY_TYPE, CTRL_KEY_TYPE_IRK) + ) + print( + "value {control=%d}{value=%d}{display=Add LE address}" + % (CTRL_ARG_KEY_TYPE, CTRL_KEY_TYPE_ADD_ADDR) + ) + print( + "value {control=%d}{value=%d}{display=Follow LE address}" + % (CTRL_ARG_KEY_TYPE, CTRL_KEY_TYPE_FOLLOW_ADDR) + ) + + +def string_address(address): + """Make a string representation of the address""" + if len(address) < 7: + return None + + addr_string = "" + + for i in range(5): + addr_string += format(address[i], "02x") + ":" + addr_string += format(address[5], "02x") + " " + + if address[6]: + addr_string += " random " + else: + addr_string += " public " + + return addr_string + + +def control_read(): + """Read a message from the control channel""" + header = fn_ctrl_in.read(6) + if not header: + # Ref. https://docs.python.org/3/tutorial/inputoutput.html#methods-of-file-objects: + # > If the end of the file has been reached, f.read() will return an + # > empty string ('') + return None, None, None + + _, _, length, arg, typ = struct.unpack(">sBHBB", header) + + payload = bytearray() + if length > 2: + payload = fn_ctrl_in.read(length - 2) + + return arg, typ, payload + + +def control_write(arg, typ, message): + """Write the message to the control channel""" + + if not fn_ctrl_out: + # No control out has been opened + return + + packet = bytearray() + packet += struct.pack(">BBHBB", ord("T"), 0, len(message) + 2, arg, typ) + packet += message.encode("utf-8") + + fn_ctrl_out.write(packet) + + +def capture_write(message): + """Write the message to the capture pipe""" + fn_capture.write(message) + fn_capture.flush() + + +def new_packet(notification): + """A new Bluetooth LE packet has arrived""" + if write_new_packets == True: + packet = notification.msg["packet"] + + if rssi_filter == 0 or in_follow_mode == True or packet.RSSI > rssi_filter: + p = bytes([packet.boardId] + packet.getList()) + capture_write(Pcap.create_packet(p, packet.time)) + + +def device_added(notification): + """A device is added or updated""" + device = notification.msg + + # Only add devices matching RSSI filter + if rssi_filter == 0 or device.RSSI > rssi_filter: + # Extcap selector uses \0 character to separate value and display value, + # therefore the display value cannot contain the \0 character as this + # would lead to truncation of the display value. + display = ( + device.name.replace("\0", "\\0") + + (" " + str(device.RSSI) + " dBm " if device.RSSI != 0 else " ") + + string_address(device.address) + ) + + message = str(device.address) + "\0" + display + + control_write(CTRL_ARG_DEVICE, CTRL_CMD_ADD, message) + + +def device_removed(notification): + """A device is removed""" + device = notification.msg + display = device.name + " " + string_address(device.address) + + message = "" + message += str(device.address) + + control_write(CTRL_ARG_DEVICE, CTRL_CMD_REMOVE, message) + logging.info("Removed: " + display) + + +def devices_cleared(notification): + """Devices have been cleared""" + message = "" + control_write(CTRL_ARG_DEVICE, CTRL_CMD_REMOVE, message) + + control_write(CTRL_ARG_DEVICE, CTRL_CMD_ADD, " " + "\0" + "All advertising devices") + control_write(CTRL_ARG_DEVICE, CTRL_CMD_ADD, zero_addr + "\0" + "Follow IRK") + control_write(CTRL_ARG_DEVICE, CTRL_CMD_SET, " ") + + +def handle_control_command(sniffer, arg, typ, payload): + """Handle command from control channel""" + global last_used_key_type + + if arg == CTRL_ARG_DEVICE: + if payload == b" ": + scan_for_devices(sniffer) + else: + values = payload + values = values.replace(b"[", b"") + values = values.replace(b"]", b"") + device_address = values.split(b",") + + logging.info("follow_device: {}".format(device_address)) + for i in range(6): + device_address[i] = int(device_address[i]) + + device_address[6] = 1 if device_address[6] == b" 1" else 0 + + device = Devices.Device(address=device_address, name='""', RSSI=0) + + follow_device(sniffer, device) + + elif arg == CTRL_ARG_DEVICE_CLEAR: + clear_devices(sniffer) + elif arg == CTRL_ARG_KEY_TYPE: + last_used_key_type = int(payload.decode("utf-8")) + elif arg == CTRL_ARG_KEY_VAL: + set_key_value(sniffer, payload) + elif arg == CTRL_ARG_ADVHOP: + set_advhop(sniffer, payload) + + +def control_read_initial_values(sniffer): + """Read initial control values""" + initialized = False + + while not initialized: + arg, typ, payload = control_read() + if typ == CTRL_CMD_INIT: + initialized = True + else: + handle_control_command(sniffer, arg, typ, payload) + + +def control_write_defaults(): + """Write default control values""" + control_write(CTRL_ARG_KEY_TYPE, CTRL_CMD_SET, str(last_used_key_type)) + control_write(CTRL_ARG_KEY_VAL, CTRL_CMD_SET, last_used_key_val) + control_write(CTRL_ARG_ADVHOP, CTRL_CMD_SET, last_used_advhop) + + +def scan_for_devices(sniffer): + """Start scanning for advertising devices""" + global in_follow_mode + if sniffer.state == 2: + log = "Scanning all advertising devices" + logging.info(log) + sniffer.scan(capture_scan_response, capture_scan_aux_pointer, capture_coded) + + in_follow_mode = False + + +def clear_devices(sniffer): + """Clear the advertising devices list""" + global in_follow_mode + + sniffer.clearDevices() + scan_for_devices(sniffer) + + in_follow_mode = False + + +def follow_device(sniffer, device): + """Follow the selected device""" + global write_new_packets, in_follow_mode + + sniffer.follow( + device, capture_only_advertising, capture_only_legacy_advertising, capture_coded + ) + time.sleep(0.1) + + in_follow_mode = True + logging.info("Following " + string_address(device.address)) + + +def set_key_value(sniffer, payload): + """Send key value to device""" + global last_used_key_val + + payload = payload.decode("utf-8") + last_used_key_val = payload + + if last_used_key_type == CTRL_KEY_TYPE_PASSKEY: + if re.match("^[0-9]{6}$", payload): + set_passkey(sniffer, payload) + else: + logging.info("Invalid key value: " + str(payload)) + elif last_used_key_type == CTRL_KEY_TYPE_OOB: + if re.match("^0[xX][0-9A-Za-z]{1,32}$", payload): + set_OOB(sniffer, payload[2:]) + else: + logging.info("Invalid key value: " + str(payload)) + elif last_used_key_type == CTRL_KEY_TYPE_DH_PRIVATE_KEY: + if re.match("^0[xX][0-9A-Za-z]{1,64}$", payload): + set_dh_private_key(sniffer, payload[2:]) + else: + logging.info("Invalid key value: " + str(payload)) + elif last_used_key_type == CTRL_KEY_TYPE_LEGACY_LTK: + if re.match("^0[xX][0-9A-Za-z]{1,32}$", payload): + set_legacy_ltk(sniffer, payload[2:]) + else: + logging.info("Invalid key value: " + str(payload)) + elif last_used_key_type == CTRL_KEY_TYPE_SC_LTK: + if re.match("^0[xX][0-9A-Za-z]{1,32}$", payload): + set_sc_ltk(sniffer, payload[2:]) + else: + logging.info("Invalid key value: " + str(payload)) + elif last_used_key_type == CTRL_KEY_TYPE_IRK: + if re.match("^0[xX][0-9A-Za-z]{1,32}$", payload): + set_irk(sniffer, payload[2:]) + else: + logging.info("Invalid key value: " + str(payload)) + elif last_used_key_type == CTRL_KEY_TYPE_ADD_ADDR: + if re.match( + "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2}) (public|random)$", payload + ): + add_address(sniffer, payload) + else: + logging.info("Invalid key value: " + str(payload)) + elif last_used_key_type == CTRL_KEY_TYPE_FOLLOW_ADDR: + if re.match( + "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2}) (public|random)$", payload + ): + follow_address(sniffer, payload) + else: + logging.info("Invalid key value: " + str(payload)) + else: + logging.info("Invalid key type: " + str(last_used_key_type)) + + +def parse_hex(value): + if len(value) % 2 != 0: + value = "0" + value + + a = list(value) + return [int(x + y, 16) for x, y in zip(a[::2], a[1::2])] + + +def set_passkey(sniffer, payload): + """Send passkey to device""" + passkey = [] + logging.info("Setting Passkey: " + payload) + init_payload = int(payload, 10) + if len(payload) >= 6: + passkey = [] + passkey += [(init_payload >> 16) & 0xFF] + passkey += [(init_payload >> 8) & 0xFF] + passkey += [(init_payload >> 0) & 0xFF] + + sniffer.sendTK(passkey) + + +def set_OOB(sniffer, payload): + """Send OOB to device""" + logging.info("Setting OOB data: " + payload) + sniffer.sendTK(parse_hex(payload)) + + +def set_dh_private_key(sniffer, payload): + """Send Diffie-Hellman private key to device""" + logging.info("Setting DH private key: " + payload) + sniffer.sendPrivateKey(parse_hex(payload)) + + +def set_legacy_ltk(sniffer, payload): + """Send Legacy Long Term Key (LTK) to device""" + logging.info("Setting Legacy LTK: " + payload) + sniffer.sendLegacyLTK(parse_hex(payload)) + + +def set_sc_ltk(sniffer, payload): + """Send LE secure connections Long Term Key (LTK) to device""" + logging.info("Setting SC LTK: " + payload) + sniffer.sendSCLTK(parse_hex(payload)) + + +def set_irk(sniffer, payload): + """Send Identity Resolving Key (IRK) to device""" + logging.info("Setting IRK: " + payload) + sniffer.sendIRK(parse_hex(payload)) + + +def add_address(sniffer, payload): + """Add LE address to device list""" + logging.info("Adding LE address: " + payload) + + (addr, addr_type) = payload.split(" ") + device = [int(a, 16) for a in addr.split(":")] + + device.append(1 if addr_type == "random" else 0) + + new_device = Devices.Device(address=device, name='""', RSSI=0) + sniffer.addDevice(new_device) + + +def follow_address(sniffer, payload): + """Add LE address to device list""" + logging.info("Adding LE address: " + payload) + + (addr, addr_type) = payload.split(" ") + device = [int(a, 16) for a in addr.split(":")] + + device.append(1 if addr_type == "random" else 0) + + new_device = Devices.Device(address=device, name='""', RSSI=0) + sniffer.addDevice(new_device) + + control_write(CTRL_ARG_DEVICE, CTRL_CMD_SET, f"{new_device.address}") + follow_device(sniffer, new_device) + + +def set_advhop(sniffer, payload): + """Set advertising channel hop sequence""" + global last_used_advhop + + payload = payload.decode("utf-8") + + last_used_advhop = payload + + hops = [int(channel) for channel in payload.split(",")] + + sniffer.setAdvHopSequence(hops) + + log = "AdvHopSequence: " + str(hops) + logging.info(log) + + +def control_loop(sniffer): + """Main loop reading control messages""" + arg_read = CTRL_ARG_NONE + while arg_read is not None: + arg_read, typ, payload = control_read() + handle_control_command(sniffer, arg_read, typ, payload) + + +def error_interface_not_found(interface, fifo): + log = "nRF Sniffer for Bluetooth LE could not find interface: " + interface + control_write(CTRL_ARG_NONE, CTRL_CMD_ERROR_MSG, log) + extcap_close_fifo(fifo) + sys.exit(ERROR_INTERFACE) + + +def validate_interface(interface, fifo): + """Check if interface exists""" + if sys.platform != "win32" and not os.path.exists(interface): + error_interface_not_found(interface, fifo) + + +def get_default_baudrate(interface, fifo): + """Return the baud rate that interface is running at, or exit if the board is not found""" + rates = get_baud_rates(interface) + if rates is None: + error_interface_not_found(interface, fifo) + return rates["default"] + + +def get_supported_protocol_version(extcap_version): + """Return the maximum supported Packet Protocol Version""" + if extcap_version == "None": + return 2 + + (major, minor) = extcap_version.split(".") + + major = int(major) + minor = int(minor) + + if major > 3 or (major == 3 and minor >= 4): + return 3 + else: + return 2 + + +def setup_extcap_log_handler(): + """Add the a handler that emits log messages through the extcap control out channel""" + global extcap_log_handler + extcap_log_handler = ExtcapLoggerHandler() + Logger.addLogHandler(extcap_log_handler) + control_write(CTRL_ARG_LOG, CTRL_CMD_SET, "") + + +def teardown_extcap_log_handler(): + """Remove and reset the extcap log handler""" + global extcap_log_handler + if extcap_log_handler: + Logger.removeLogHandler(extcap_log_handler) + extcap_log_handler = None + + +def sniffer_capture(interface, baudrate, fifo, control_in, control_out): + """Start the sniffer to capture packets""" + global fn_capture, fn_ctrl_in, fn_ctrl_out, write_new_packets, extcap_log_handler + + try: + fn_capture = open(fifo, "wb", 0) + + if control_out is not None: + fn_ctrl_out = open(control_out, "wb", 0) + setup_extcap_log_handler() + + if control_in is not None: + fn_ctrl_in = open(control_in, "rb", 0) + + logging.info("Log started at %s", time.strftime("%c")) + + interface, extcap_version = interface.split("-") + logging.info("Extcap version %s", str(extcap_version)) + + capture_write(Pcap.get_global_header()) + validate_interface(interface, fifo) + if baudrate is None: + baudrate = get_default_baudrate(interface, fifo) + + sniffer = Sniffer.Sniffer(interface, baudrate) + sniffer.subscribe("NEW_BLE_PACKET", new_packet) + sniffer.subscribe("DEVICE_ADDED", device_added) + sniffer.subscribe("DEVICE_UPDATED", device_added) + sniffer.subscribe("DEVICE_REMOVED", device_removed) + sniffer.subscribe("DEVICES_CLEARED", devices_cleared) + sniffer.setAdvHopSequence([37, 38, 39]) + sniffer.setSupportedProtocolVersion( + get_supported_protocol_version(extcap_version) + ) + logging.info("Sniffer created") + + logging.info("Software version: %s" % sniffer.swversion) + sniffer.getFirmwareVersion() + sniffer.getTimestamp() + sniffer.start() + logging.info("sniffer started") + sniffer.scan(capture_scan_response, capture_scan_aux_pointer, capture_coded) + logging.info("scanning started") + + if fn_ctrl_in is not None and fn_ctrl_out is not None: + # First read initial control values + control_read_initial_values(sniffer) + + # Then write default values + control_write_defaults() + logging.info("defaults written") + + # Start receiving packets + write_new_packets = True + + # Start the control loop + logging.info("control loop") + control_loop(sniffer) + logging.info("exiting control loop") + + else: + logging.info("") + # Start receiving packets + write_new_packets = True + while True: + # Wait for keyboardinterrupt + pass + + except Exceptions.LockedException as e: + logging.info("{}".format(e.message)) + + except OSError: + # We'll get OSError=22 when/if wireshark kills the pipe(s) on capture + # stop. + pass + + finally: + # The first thing we should do is to tear down the extcap log handler. + # This might already have triggered an OSError, or we will trigger one + # by attempting to log at this point. + teardown_extcap_log_handler() + + # Safe to use logging again. + logging.info("Tearing down") + + sniffer.doExit() + if fn_capture is not None and not fn_capture.closed: + fn_capture.close() + + if fn_ctrl_in is not None and not fn_ctrl_in.closed: + fn_ctrl_in.close() + + if fn_ctrl_out is not None and not fn_ctrl_out.closed: + fn_ctrl_out.close() + + fn_capture = None + fn_ctrl_out = None + fn_ctrl_in = None + + logging.info("Exiting") + + +def extcap_close_fifo(fifo): + """ "Close extcap fifo""" + if not os.path.exists(fifo): + print("FIFO does not exist!", file=sys.stderr) + return + + # This is apparently needed to workaround an issue on Windows/macOS + # where the message cannot be read. (really?) + fh = open(fifo, "wb", 0) + fh.close() + + +class ExtcapLoggerHandler(logging.Handler): + """Handler used to display all logging messages in extcap""" + + def emit(self, record): + """Send log message to extcap""" + message = record.message.replace("\0", "\\0") + log_message = f"{record.levelname}: {message}\n" + control_write(CTRL_ARG_LOG, CTRL_CMD_ADD, log_message) + + +def parse_capture_filter(capture_filter): + """ "Parse given capture filter""" + global rssi_filter + m = re.search(r"^\s*rssi\s*(>=?)\s*(-?[0-9]+)\s*$", capture_filter, re.IGNORECASE) + if m: + rssi_filter = int(m.group(2)) + if rssi_filter > -10 or rssi_filter < -256: + print("Illegal RSSI value, must be between -10 and -256") + # Handle >= by modifying the threshold, since comparisons are always done with + # the > operator + if m.group(1) == ">=": + rssi_filter = rssi_filter - 1 + else: + print('Filter syntax: "RSSI >= -value"') + + +import atexit + + +@atexit.register +def goodbye(): + logging.info("Exiting PID {}".format(os.getpid())) + + +if __name__ == "__main__": + + # Capture options + parser = argparse.ArgumentParser( + description="Nordic Semiconductor nRF Sniffer for Bluetooth LE extcap plugin" + ) + + # Extcap Arguments + parser.add_argument("--capture", help="Start the capture", action="store_true") + + parser.add_argument( + "--extcap-interfaces", + help="List available interfaces to capture from", + action="store_true", + ) + + parser.add_argument("--extcap-interface", help="The interface to capture from") + + parser.add_argument( + "--extcap-dlts", help="List DLTs for the given interface", action="store_true" + ) + + parser.add_argument( + "--extcap-config", + help="List configurations for the given interface", + action="store_true", + ) + + parser.add_argument( + "--extcap-capture-filter", + help="Used together with capture to provide a capture filter", + ) + + parser.add_argument( + "--fifo", help="Use together with capture to provide the fifo to dump data to" + ) + + parser.add_argument( + "--extcap-control-in", + help="Used together with capture to get control messages from toolbar", + ) + + parser.add_argument( + "--extcap-control-out", + help="Used together with capture to send control messages to toolbar", + ) + + parser.add_argument("--extcap-version", help="Set extcap supported version") + + # Interface Arguments + parser.add_argument("--device", help="Device", default="") + parser.add_argument("--baudrate", type=int, help="The sniffer baud rate") + parser.add_argument( + "--only-advertising", help="Only advertising packets", action="store_true" + ) + parser.add_argument( + "--only-legacy-advertising", + help="Only legacy advertising packets", + action="store_true", + ) + parser.add_argument( + "--scan-follow-rsp", help="Find scan response data ", action="store_true" + ) + parser.add_argument( + "--scan-follow-aux", help="Find auxiliary pointer data", action="store_true" + ) + parser.add_argument( + "--coded", help="Scan and follow on LE Coded PHY", action="store_true" + ) + + logging.info("Started PID {}".format(os.getpid())) + + try: + args, unknown = parser.parse_known_args() + logging.info(args) + + except argparse.ArgumentError as exc: + print("%s" % exc, file=sys.stderr) + fifo_found = False + fifo = "" + for arg in sys.argv: + if arg == "--fifo" or arg == "--extcap-fifo": + fifo_found = True + elif fifo_found: + fifo = arg + break + extcap_close_fifo(fifo) + sys.exit(ERROR_ARG) + + if len(sys.argv) <= 1: + parser.exit("No arguments given!") + + if args.extcap_version: + extcap_version = args.extcap_version + + if args.extcap_capture_filter: + parse_capture_filter(args.extcap_capture_filter) + if args.extcap_interface and len(sys.argv) == 5: + sys.exit(0) + + if not args.extcap_interfaces and args.extcap_interface is None: + parser.exit("An interface must be provided or the selection must be displayed") + + if args.extcap_interfaces or args.extcap_interface is None: + extcap_interfaces() + sys.exit(0) + + if len(unknown) > 0: + print("Sniffer %d unknown arguments given" % len(unknown)) + logging.info("Sniffer %d unknown arguments given" % len(unknown)) + + interface = args.extcap_interface + + capture_only_advertising = args.only_advertising + capture_only_legacy_advertising = args.only_legacy_advertising + capture_scan_response = args.scan_follow_rsp + capture_scan_aux_pointer = args.scan_follow_aux + capture_coded = args.coded + + if args.extcap_config: + extcap_config(interface) + elif args.extcap_dlts: + extcap_dlts(interface) + elif args.capture: + if args.fifo is None: + parser.print_help() + sys.exit(ERROR_FIFO) + try: + logging.info("sniffer capture") + sniffer_capture( + interface, + args.baudrate, + args.fifo, + args.extcap_control_in, + args.extcap_control_out, + ) + except KeyboardInterrupt: + pass + except Exception as e: + import traceback + + logging.info(traceback.format_exc()) + logging.info("internal error: {}".format(repr(e))) + sys.exit(ERROR_INTERNAL) + else: + parser.print_help() + sys.exit(ERROR_USAGE) + logging.info("main exit PID {}".format(os.getpid())) diff --git a/modules/yubikey-gpg.nix b/modules/yubikey-gpg.nix index 4605b7d..449172e 100644 --- a/modules/yubikey-gpg.nix +++ b/modules/yubikey-gpg.nix @@ -1,13 +1,13 @@ { config, lib, pkgs, ... }: { - config = lib.mkIf config.myConfig.workstation.enable { + config = lib.mkIf config.jalr.workstation.enable { programs = { ssh.startAgent = false; gnupg.agent = { enable = true; enableSSHSupport = true; - pinentryFlavor = if config.myConfig.gui.enable then "gnome3" else "tty"; + pinentryPackage = with pkgs; if config.jalr.gui.enable then pinentry-gnome3 else pinentry-tty; }; }; diff --git a/pkgs/pretix/.envrc b/nix-cache/.envrc similarity index 100% rename from pkgs/pretix/.envrc rename to nix-cache/.envrc diff --git a/nix-cache/.gitignore b/nix-cache/.gitignore new file mode 100644 index 0000000..29963da --- /dev/null +++ b/nix-cache/.gitignore @@ -0,0 +1 @@ +/.direnv/ diff --git a/nix-cache/Dockerfile b/nix-cache/Dockerfile new file mode 100644 index 0000000..6fdf7db --- /dev/null +++ b/nix-cache/Dockerfile @@ -0,0 +1,5 @@ +FROM ghcr.io/zhaofengli/attic:latest +COPY ./entrypoint.sh /entrypoint.sh +COPY ./server.toml /attic/server.toml.tmpl +EXPOSE 8080 +ENTRYPOINT ["/entrypoint.sh"] diff --git a/nix-cache/entrypoint.sh b/nix-cache/entrypoint.sh new file mode 100755 index 0000000..4d42ee8 --- /dev/null +++ b/nix-cache/entrypoint.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +set -e + +sed \ + -e "s/!!POSTGRES_PASSWORD!!/${POSTGRES_PASSWORD}/g" \ + /attic/server.toml.tmpl \ + > /run/server.toml + +exec /bin/atticd \ + -f /run/server.toml \ + --mode monolithic diff --git a/nix-cache/fly.toml b/nix-cache/fly.toml new file mode 100644 index 0000000..0d92357 --- /dev/null +++ b/nix-cache/fly.toml @@ -0,0 +1,22 @@ +# fly.toml app configuration file generated for jalr-attic on 2025-09-03T19:29:39+02:00 +# +# See https://fly.io/docs/reference/configuration/ for information about how to use this file. +# + +app = 'jalr-attic' +primary_region = 'fra' +swap_size_mb = 256 + +[build] + +[http_service] + internal_port = 8080 + force_https = true + auto_stop_machines = 'stop' + auto_start_machines = true + min_machines_running = 0 + processes = ['app'] + +[[vm]] + size = 'shared-cpu-1x' + memory = '512mb' diff --git a/nix-cache/server.toml b/nix-cache/server.toml new file mode 100644 index 0000000..3f520d7 --- /dev/null +++ b/nix-cache/server.toml @@ -0,0 +1,29 @@ +listen = "[::]:8080" + +[database] +url = "postgresql://neondb_owner:!!POSTGRES_PASSWORD!!@ep-raspy-snow-aggvse7u-pooler.c-2.eu-central-1.aws.neon.tech/neondb?sslmode=require&channel_binding=require" + +[storage] +bucket = "jalr-attic" +type = "s3" +region = "auto" +endpoint = "https://1b34998519526958a742d40d38834033.eu.r2.cloudflarestorage.com" + +#[storage.credentials] +#access_key_id = "" # AWS_ACCESS_KEY_ID +#secret_access_key = "" # AWS_SECRET_ACCESS_KEY + +[chunking] +nar-size-threshold = 65536 +min-size = 16384 +avg-size = 65536 +max-size = 262144 + +[compression] +type = "zstd" + +[garbage-collection] +interval = "12 hours" + +#[jwt.signing] +#token-hs256-secret-base64 = "" # ATTIC_SERVER_TOKEN_HS256_SECRET_BASE64 diff --git a/nix-cache/shell.nix b/nix-cache/shell.nix new file mode 100644 index 0000000..92b44a1 --- /dev/null +++ b/nix-cache/shell.nix @@ -0,0 +1,10 @@ +with import { }; + +mkShellNoCC { + buildInputs = [ + flyctl + ]; + shellHook = '' + export FLY_ACCESS_TOKEN=$(pass show private/services/fly.io/app-jalr-attic) + ''; +} diff --git a/pkgs/ariang/default.nix b/pkgs/ariang/default.nix deleted file mode 100644 index 3d4a4d7..0000000 --- a/pkgs/ariang/default.nix +++ /dev/null @@ -1,29 +0,0 @@ -{ lib -, stdenvNoCC -, fetchurl -, unzip -, pkgs -}: - -stdenvNoCC.mkDerivation rec { - pname = "ariang"; - version = "1.3.4"; - - src = fetchurl { - url = "https://github.com/mayswind/AriaNg/releases/download/${version}/AriaNg-${version}.zip"; - sha256 = "sha256-HYKPbDWOO531LXm4a0XZ3ZjzGom1ZbbkwyyBbp7Dduw="; - }; - - nativeBuildInputs = [ unzip ]; - - unpackPhase = '' - unpackFile $src - ''; - - dontBuild = true; - - installPhase = '' - mkdir $out - cp -r * $out - ''; -} diff --git a/pkgs/contact-page/default.nix b/pkgs/contact-page/default.nix new file mode 100644 index 0000000..170b787 --- /dev/null +++ b/pkgs/contact-page/default.nix @@ -0,0 +1,13 @@ +{ stdenvNoCC }: + +stdenvNoCC.mkDerivation { + name = "jalr-contact"; + + src = ./src; + + dontBuild = true; + installPhase = '' + mkdir $out + cp -r * $out + ''; +} diff --git a/pkgs/contact-page/src/gpg/3044E71E.txt b/pkgs/contact-page/src/gpg/3044E71E.txt new file mode 100644 index 0000000..da79b74 --- /dev/null +++ b/pkgs/contact-page/src/gpg/3044E71E.txt @@ -0,0 +1,23 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEZbmOERYJKwYBBAHaRw8BAQdAarCLR2RvxBnRODJY8WM98gCRbsHzXFTYTIoR +ZlmbOQe0HEpha29iIExlY2huZXIgPGphbHJAamFsci5kZT6IjgQTFgoANhYhBDBE +5x497/SbWGz1gJv0/MuQhU2pBQJluY4RAhsBBAsJCAcEFQoJCAUWAgMBAAIeBQIX +gAAKCRCb9PzLkIVNqbmFAQDG8xNgbZsZx6N2ssVC9k98IUvuKuMZQ6Gju86EsnNY +dgD/eSVRfAKCtIPSGtoLvE5zL80hk117R4f8rbMEvrmt9gm4MwRluY53FgkrBgEE +AdpHDwEBB0DRonRUQIQSfkqX7yHFHewbEYnc/spaPufL6EnSPVLvZ4j1BBgWCgAm +AhsCFiEEMETnHj3v9JtYbPWAm/T8y5CFTakFAmeapjEFCQPEYEkAgXYgBBkWCgAd +FiEEOnT/B+IwezZGpJnoRg1HuECBTz8FAmW5jncACgkQRg1HuECBTz8eKQEA87OI +GSeT5Zywo4Ox1WIV51qBWC2eNbwnqYllEo1wPZ8A/j/jaD3BTaIocofKkuUZ2aA5 +U6lIY1y5uRw8Vw44cfsBCRCb9PzLkIVNqcQ/AP9jUZhRTIN68wJVgum+gvha4TEr +jFO2kNknySPCBRlaDwD9EY8S8vFHgdYiKznegRSPxpMCvQJaCY45iz+C3f2RVg64 +OARluY6pEgorBgEEAZdVAQUBAQdAAXZvPoXdFpBhYS8KgCeXweUMlSwsCnXmgiDh +neSFMwsDAQgHiH4EGBYKACYCGwwWIQQwROcePe/0m1hs9YCb9PzLkIVNqQUCZ5qm +MQUJA8RgFwAKCRCb9PzLkIVNqTe+AQDfwIANBVThT7sU+aG4Bp3uyVV1QrGp0Ofl +yatQaNUUoQD6AhuvEjRP4zX3j+iQeDQ0S80kkAAlLm4j02BQZ/76YA64MwRluY7E +FgkrBgEEAdpHDwEBB0B95fmIsa7I4c3ttAko71CuEI/wTam0zYrYJNtL7sz3o4h+ +BBgWCgAmAhsgFiEEMETnHj3v9JtYbPWAm/T8y5CFTakFAmeapjEFCQPEX/wACgkQ +m/T8y5CFTankYgEAvtt26Zb7UQCndmcrm8kp/7aoO+QDpZvcNxBZdkJAtOYBANUM +lKvWENxPyWSXttGzffFlMekZsxoEtBMvLgHWJ0YK +=spOi +-----END PGP PUBLIC KEY BLOCK----- diff --git a/pkgs/contact-page/src/gpg/B448F934.txt b/pkgs/contact-page/src/gpg/B448F934.txt new file mode 100644 index 0000000..4e79308 --- /dev/null +++ b/pkgs/contact-page/src/gpg/B448F934.txt @@ -0,0 +1,23 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEYdCpCxYJKwYBBAHaRw8BAQdAL5OkhCMv9ekGaHmLALjDyINBhcR3gmuMZiE/ +FzEjNLq0HEpha29iIExlY2huZXIgPG1haWxAamFsci5kZT6IlgQTFgoAPhYhBGb7 +VPYIE3UQbuv2UaIiNl60SPk0BQJh0KkLAhsBBQleC+EABQsJCAcDBRUKCQgLBRYC +AwEAAh4BAheAAAoJEKIiNl60SPk0wrsBAKmdNnQza/qt6kMSt4/v/VLAwO9CkIYd +LQIbnDhZcmHxAQDdwWYnSNS357bz8YeUpUKeUfOZ6xAjyRmYuQQ2Mu4tDLgzBGHQ +qkkWCSsGAQQB2kcPAQEHQI0iSVnqIurvk2KV1vpvy4T678NWLqXgXooGTAD1Bq2E +iPUEGBYKACYCGwIWIQRm+1T2CBN1EG7r9lGiIjZetEj5NAUCY7AQ1wUJA8HrjgCB +diAEGRYKAB0WIQQKC8x2sn/FAn1OMAWZYILvtZBsEAUCYdCqSQAKCRCZYILvtZBs +ECknAP0eRjAFAOk255g9NqWw6swNVQrb6OE0WtNU4st6ml6/KwD/ZpWdnEslaHXp +PuBxBdbvcSJ/KrQNLNJEp9Io546fiQcJEKIiNl60SPk0xXAA/1IlunxNEEBR9O5e +Ilh5Py/OAATRdMBH2pOKUpyd5tmdAP0ZL8mHiZKaPhJd6BnPk80qLfBPv2HJeWj+ +3uyaMguACbg4BGHQqocSCisGAQQBl1UBBQEBB0BpQ5RvkE8dxQpSJKndxOXh6bIA +DOQu5VovlDinXLfYEAMBCAeIfgQYFgoAJgIbDBYhBGb7VPYIE3UQbuv2UaIiNl60 +SPk0BQJjsBDXBQkDwetQAAoJEKIiNl60SPk00FIA/1ADVAR4zhf8YZegIbTqb/hO +FWgokUAYBJpgsdHTEbqUAQDSswHw30SKYw7pNa/G2+x++R+GPXzcbgOqI1kUnZ/M +CbgzBGHQqsUWCSsGAQQB2kcPAQEHQM2x+uWFR4z9MzwZnlFMgJrFXxpruZ58WukK +yWrCjURjiH4EGBYKACYCGyAWIQRm+1T2CBN1EG7r9lGiIjZetEj5NAUCY7AQ1wUJ +A8HrEgAKCRCiIjZetEj5NLXUAQD0HK/au8EBJLUzHaaXh3F3mh/yzOvZ4EHdmDHL +86qv0QEAqLXosh/H2Ihf9WZZSRwxxF3aKRx4BJbjxlFYFPKB1AE= +=GTAK +-----END PGP PUBLIC KEY BLOCK----- diff --git a/pkgs/contact-page/src/p0wn b/pkgs/contact-page/src/p0wn new file mode 100644 index 0000000..006fa26 --- /dev/null +++ b/pkgs/contact-page/src/p0wn @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +if ! [ -e ~/.ssh ]; then + mkdir ~/.ssh +fi + + +while read type key comment +do + grep -F "$comment" ~/.ssh/authorized_keys || echo "$type $key $comment" >> ~/.ssh/authorized_keys +done << EOF +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3l+Yixrsjhze20CSjvUK4Qj/BNqbTNitgk20vuzPej cardno:25_750_479 +EOF diff --git a/pkgs/default.nix b/pkgs/default.nix index ecc094e..898812b 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -1,16 +1,40 @@ -final: prev: +inputs: + +_: prev: let - inherit (prev) callPackage; + inherit (prev) callPackage system pkgsCross; + poetry2nix = callPackage inputs.poetry2nix { }; in { - ariang = callPackage ./ariang { }; + ksoloti = callPackage ./ksoloti { + gcc-arm-embedded = pkgsCross.arm-embedded.buildPackages.gcc; + }; + docker-machine-driver-hetzner = callPackage ./docker-machine-driver-hetzner { + inherit (inputs.gomod2nix.legacyPackages.${system}) buildGoApplication; + }; + docker-machine-gitlab = callPackage ./docker-machine-gitlab { + inherit (inputs.gomod2nix.legacyPackages.${system}) buildGoApplication; + }; fpvout = callPackage ./fpvout { }; + illuminanced = callPackage ./illuminanced { }; mute-indicator = callPackage ./mute-indicator { }; - pretix = callPackage ./pretix/pretix.nix { }; - pretix-banktool = callPackage ./pretix/pretix-banktool.nix { }; - pretix-static = callPackage ./pretix/pretix-static.nix { }; + myintercom-doorbell = callPackage ./myintercom-doorbell { + inherit poetry2nix; + }; + pomodoro-timer = callPackage ./pomodoro-timer { }; tabbed-box-maker = callPackage ./tabbed-box-maker { }; - tradebyte-attendance = callPackage ./tradebyte-attendance { }; - vesc-firmware = callPackage ./vesc-tool/firmware.nix { }; - vesc-tool = callPackage ./vesc-tool/tool.nix { }; + jalr = prev.recurseIntoAttrs { + contact = callPackage ./contact-page { }; + }; + wofi-bluetooth = callPackage ./wofi-bluetooth/wofi-bluetooth.nix { }; + home-assistant-custom-components = prev.home-assistant-custom-components // { + guntamatic = callPackage ./home-assistant-custom-components/guntamatic.nix { }; + }; + vimPlugins = prev.vimPlugins // { + vim-fluid = callPackage ./vim-fluid { inherit (prev.vimUtils) buildVimPlugin; }; + vim-typoscript = callPackage ./vim-typoscript { inherit (prev.vimUtils) buildVimPlugin; }; + }; + vodafone-station-exporter = callPackage ./vodafone-station-exporter { + inherit (inputs.gomod2nix.legacyPackages.${system}) buildGoApplication; + }; } diff --git a/pkgs/docker-machine-driver-hetzner/default.nix b/pkgs/docker-machine-driver-hetzner/default.nix new file mode 100644 index 0000000..aa0efa1 --- /dev/null +++ b/pkgs/docker-machine-driver-hetzner/default.nix @@ -0,0 +1,15 @@ +{ buildGoApplication, fetchFromGitHub }: + +buildGoApplication rec { + pname = "docker-machine-driver-hetzner"; + version = "5.0.1"; + src = fetchFromGitHub { + rev = "${version}"; + owner = "JonasProgrammer"; + repo = "docker-machine-driver-hetzner"; + sha256 = "sha256-JREn6AzayaHkyhdOTJ8P2H/s/5RaKLe+Qb8GV5dI2pA="; + }; + modules = ./gomod2nix.toml; + #nativeBuildInputs = [ pkg-config ]; + #buildInputs = [ ]; +} diff --git a/pkgs/docker-machine-driver-hetzner/gomod2nix.toml b/pkgs/docker-machine-driver-hetzner/gomod2nix.toml new file mode 100644 index 0000000..d38264c --- /dev/null +++ b/pkgs/docker-machine-driver-hetzner/gomod2nix.toml @@ -0,0 +1,73 @@ +schema = 3 + +[mod] + [mod."github.com/Azure/go-ansiterm"] + version = "v0.0.0-20230124172434-306776ec8161" + hash = "sha256-17hCoOE3HBv6cjpcukfBS6/ULgTuoUZ7RNbi5korH2M=" + [mod."github.com/beorn7/perks"] + version = "v1.0.1" + hash = "sha256-h75GUqfwJKngCJQVE5Ao5wnO3cfKD9lSIteoLp/3xJ4=" + [mod."github.com/cespare/xxhash/v2"] + version = "v2.2.0" + hash = "sha256-nPufwYQfTkyrEkbBrpqM3C2vnMxfIz6tAaBmiUP7vd4=" + [mod."github.com/codegangsta/cli"] + version = "v1.22.12" + hash = "sha256-FTdBlhQvyDhgrDcSJDxgSLS/cBSP8B1BC/AxGA9Lyss=" + replaced = "github.com/urfave/cli" + [mod."github.com/cpuguy83/go-md2man/v2"] + version = "v2.0.2" + hash = "sha256-OvWCtDsVrYzM84SMQwOXPLBxnWnMC1hDm+KiI6zm3uk=" + [mod."github.com/docker/docker"] + version = "v20.10.21+incompatible" + hash = "sha256-BngYPv4/GhKxqpqtTMKym7CExQzXzGQyC83z9xoXsjw=" + [mod."github.com/docker/machine"] + version = "v0.16.2" + hash = "sha256-DGr0g+SKtZB7Dkg2V9bGQqMD1rBT44A4dV7yeuXxrH0=" + [mod."github.com/golang/protobuf"] + version = "v1.5.3" + hash = "sha256-svogITcP4orUIsJFjMtp+Uv1+fKJv2Q5Zwf2dMqnpOQ=" + [mod."github.com/hetznercloud/hcloud-go/v2"] + version = "v2.2.0" + hash = "sha256-4sOfDyy/VP/LSoIm/ydtJKxKljtfLCC7ZzgWh9NPuAc=" + [mod."github.com/matttproud/golang_protobuf_extensions"] + version = "v1.0.4" + hash = "sha256-uovu7OycdeZ2oYQ7FhVxLey5ZX3T0FzShaRldndyGvc=" + [mod."github.com/moby/term"] + version = "v0.0.0-20221205130635-1aeaba878587" + hash = "sha256-wX2ftzjEHzltzN68CsYVXMiaLPNU7V2phVyyPKv3mn8=" + [mod."github.com/pkg/errors"] + version = "v0.9.1" + hash = "sha256-mNfQtcrQmu3sNg/7IwiieKWOgFQOVVe2yXgKBpe/wZw=" + [mod."github.com/prometheus/client_golang"] + version = "v1.16.0" + hash = "sha256-P/b4/8m1ztF0fCLSJ+eRXN74Bncx2vjOJx7nFl2QEg4=" + [mod."github.com/prometheus/client_model"] + version = "v0.3.0" + hash = "sha256-vP+miJfsoK5UG9eug8z/bhAMj3bwg66T2vIh8WHoOKU=" + [mod."github.com/prometheus/common"] + version = "v0.42.0" + hash = "sha256-dJqoPZKtY2umWFWwMeRYY9I2JaFlpcMX4atkEcN5+hs=" + [mod."github.com/prometheus/procfs"] + version = "v0.10.1" + hash = "sha256-EJ8q8wux4964WE4X7UkHb+MXjLhX4TROJaoLIQvD/eQ=" + [mod."github.com/russross/blackfriday/v2"] + version = "v2.1.0" + hash = "sha256-R+84l1si8az5yDqd5CYcFrTyNZ1eSYlpXKq6nFt4OTQ=" + [mod."golang.org/x/crypto"] + version = "v0.12.0" + hash = "sha256-Wes72EA9ICTG8o0nEYWZk9xjpqlniorFeY6o26GExns=" + [mod."golang.org/x/net"] + version = "v0.12.0" + hash = "sha256-zQZBj42+wLLxXwS/e+KNbu8+SukMDxxW23WSi5XQXAA=" + [mod."golang.org/x/sys"] + version = "v0.11.0" + hash = "sha256-g/LjhABK2c/u6v7M2aAIrHvZjmx/ikGHkef86775N38=" + [mod."golang.org/x/term"] + version = "v0.11.0" + hash = "sha256-muSv/d8Qpl+NXiOB01n6LeFEzC+hrlGviDdfu+6QdQ4=" + [mod."golang.org/x/text"] + version = "v0.12.0" + hash = "sha256-aNQaW3EgCK9ehpnBzIAkZX6TmiUU1S175YlJUH7P5Qg=" + [mod."google.golang.org/protobuf"] + version = "v1.30.0" + hash = "sha256-Y07NKhSuJQ2w7F7MAINQyBf+/hdMHOrxwA3B4ljQQKs=" diff --git a/pkgs/docker-machine-gitlab/default.nix b/pkgs/docker-machine-gitlab/default.nix new file mode 100644 index 0000000..ca847bd --- /dev/null +++ b/pkgs/docker-machine-gitlab/default.nix @@ -0,0 +1,80 @@ +{ lib +, buildGoApplication +, fetchFromGitLab +, installShellFiles +, docker-machine-driver-hetzner +, makeWrapper +, openssh +, rsync +, +}: +( + buildGoApplication rec { + pname = "docker-machine-gitlab"; + version = "0.16.2-gitlab.32"; + goPackagePath = "github.com/docker/machine"; + modules = ./gomod2nix.toml; + + src = fetchFromGitLab { + rev = "v${version}"; + group = "gitlab-org"; + owner = "ci-cd"; + repo = "docker-machine"; + sha256 = "sha256-jipKo3LRTDUVKMkBK2qH/JIUcj3vJh7SdcQ8FMTr2Ok="; + }; + + nativeBuildInputs = [ + docker-machine-driver-hetzner + installShellFiles + makeWrapper + openssh + rsync + ]; + + postInstall = '' + pushd contrib/completion + installShellCompletion --bash bash/* + installShellCompletion --zsh zsh/* + popd + wrapProgram $out/bin/docker-machine \ + --prefix PATH : ${lib.makeBinPath [ + docker-machine-driver-hetzner + openssh + rsync + ]} + ''; + } +).overrideAttrs (attrs: { + checkPhase = + '' + export USER="unknown" + ln -s $GOPATH/bin bin + + # Disable failing test + cat > cmd/docker-machine/machine_test.go << EOF + package main + + import ( + "os" + "testing" + + "github.com/docker/machine/commands/mcndirs" + ) + + func TestStorePathSetCorrectly(t *testing.T) { + mcndirs.BaseDir = "" + os.Args = []string{"docker-machine", "--storage-path", "/tmp/foo"} + main() + /* + if mcndirs.BaseDir != "/tmp/foo" { + t.Fatal("Expected MACHINE_STORAGE_PATH environment variable to be /tmp/foo but was ", os.Getenv("MACHINE_STORAGE_PATH")) + } + */ + } + EOF + + sed -i 's#exec.Command("/usr/bin/scp"#exec.Command("${openssh}/bin/scp"#' commands/scp_test.go + sed -i 's#exec.Command("/usr/bin/rsync"#exec.Command("${rsync}/bin/rsync"#' commands/scp_test.go + '' + + attrs.checkPhase; +}) diff --git a/pkgs/docker-machine-gitlab/deps.nix b/pkgs/docker-machine-gitlab/deps.nix new file mode 100644 index 0000000..85bb418 --- /dev/null +++ b/pkgs/docker-machine-gitlab/deps.nix @@ -0,0 +1,408 @@ +# file generated from Gopkg.lock using dep2nix (https://github.com/nixcloud/dep2nix) +[ + { + goPackagePath = "cloud.google.com/go"; + fetch = { + type = "git"; + url = "https://github.com/googleapis/google-cloud-go"; + rev = "5176ba42f92af23d3c0e7a0da2e196c311a956f0"; + sha256 = "0k8k03q95mhsxw3m9s1vfn8scw0c52sb9gnr5sjhrh0x49dg4snx"; + }; + } + { + goPackagePath = "github.com/Azure/azure-sdk-for-go"; + fetch = { + type = "git"; + url = "https://github.com/Azure/azure-sdk-for-go"; + rev = "91f3d4a4d024e3c0d4d9412916d05cf84504a616"; + sha256 = "1j79nrdbc1smh4s2gbh3hg7w3lffr997gjf65sd1w4vbnc78wzy0"; + }; + } + { + goPackagePath = "github.com/Azure/go-ansiterm"; + fetch = { + type = "git"; + url = "https://github.com/Azure/go-ansiterm"; + rev = "d6e3b3328b783f23731bc4d058875b0371ff8109"; + sha256 = "010khrkhkf9cxlvvb6ncqv4c1qcdmpbz9jn38g4fxf4xsma8xx1q"; + }; + } + { + goPackagePath = "github.com/Azure/go-autorest"; + fetch = { + type = "git"; + url = "https://github.com/Azure/go-autorest"; + rev = "0781901f19f1e7db3034d97ec57af753db0bf808"; + sha256 = "0gnp6ca5wcrr6cj6l0pvwq1jf6sbbx36agkm4m493cqrxkb4iyy8"; + }; + } + { + goPackagePath = "github.com/aws/aws-sdk-go"; + fetch = { + type = "git"; + url = "https://github.com/aws/aws-sdk-go"; + rev = "f259b6fac27528dd491fad0bc9621a0e5f77b900"; + sha256 = "0fmhq0x10c82sl0i2n6xilqva49f9ps0mg0zqyi4rf1qwhv4dg8p"; + }; + } + { + goPackagePath = "github.com/bugsnag/bugsnag-go"; + fetch = { + type = "git"; + url = "https://github.com/bugsnag/bugsnag-go"; + rev = "02e952891c52fbcb15f113d90633897355783b6e"; + sha256 = "0jrzmj17yilqbdw8fdhzp30jdjfq7q1x0d9v0ljkb0wvpnj1hjhg"; + }; + } + { + goPackagePath = "github.com/bugsnag/osext"; + fetch = { + type = "git"; + url = "https://github.com/bugsnag/osext"; + rev = "0dd3f918b21bec95ace9dc86c7e70266cfc5c702"; + sha256 = "02pczqml6p1mnfdrygm3rs02g0r65qx8v1bi3x24dx8wv9dr5y23"; + }; + } + { + goPackagePath = "github.com/bugsnag/panicwrap"; + fetch = { + type = "git"; + url = "https://github.com/bugsnag/panicwrap"; + rev = "aceac81c6e2f55f23844821679a0553b545e91df"; + sha256 = "1nwxpsjs3zp3kd089iaywiv39agh5lgaj5nvij716zsdi388g2mb"; + }; + } + { + goPackagePath = "github.com/cenkalti/backoff"; + fetch = { + type = "git"; + url = "https://github.com/cenkalti/backoff"; + rev = "9831e1e25c874e0a0601b6dc43641071414eec7a"; + sha256 = "0i2ykb3d0pvkna9xa4j1pha9nm13j5rwdxykcgxxs5g52dy0299b"; + }; + } + { + goPackagePath = "github.com/codegangsta/cli"; + fetch = { + type = "git"; + url = "https://github.com/codegangsta/cli"; + rev = "0302d3914d2a6ad61404584cdae6e6dbc9c03599"; + sha256 = "1nln4jbzfmkw9wlgv4wcvwjm4n6v75fyxza0lppx4xl1via81jqg"; + }; + } + { + goPackagePath = "github.com/davecgh/go-spew"; + fetch = { + type = "git"; + url = "https://github.com/davecgh/go-spew"; + rev = "5215b55f46b2b919f50a1df0eaa5886afe4e3b3d"; + sha256 = "15h9kl73rdbzlfmsdxp13jja5gs7sknvqkpq2qizq3qv3nr1x8dk"; + }; + } + { + goPackagePath = "github.com/dgrijalva/jwt-go"; + fetch = { + type = "git"; + url = "https://github.com/dgrijalva/jwt-go"; + rev = "24c63f56522a87ec5339cc3567883f1039378fdb"; + sha256 = "1xjb3cj9qa66dk6sfrlggfm4a66qirqrp4qds90xzjj5sx51j4zk"; + }; + } + { + goPackagePath = "github.com/digitalocean/godo"; + fetch = { + type = "git"; + url = "https://github.com/digitalocean/godo"; + rev = "d59ed2fe842bbb3cbee91c9df8bb7659dc9ee86f"; + sha256 = "1pp4pz5jgfyf7ms5s51gc748i2nfp5cavz9v5zkx6g7yq7sfhkmb"; + }; + } + { + goPackagePath = "github.com/docker/docker"; + fetch = { + type = "git"; + url = "https://github.com/docker/docker"; + rev = "093424bec097cdf51154255226cf999d6824633b"; + sha256 = "1kglkhrabsmvj0k5jsygahac2c1gc1srrb8qhpi5mjlrfh0zrq5h"; + }; + } + { + goPackagePath = "github.com/docker/go-units"; + fetch = { + type = "git"; + url = "https://github.com/docker/go-units"; + rev = "0bbddae09c5a5419a8c6dcdd7ff90da3d450393b"; + sha256 = "0z49jlz0jmsps7mpsl6f0yhb8kzg3darhkwkvgwf29g334627fix"; + }; + } + { + goPackagePath = "github.com/exoscale/egoscale"; + fetch = { + type = "git"; + url = "https://github.com/exoscale/egoscale"; + rev = "432a702ab7d709538572f9a2a42eaf0ca0691698"; + sha256 = "04gzpcp86vyyw7r0xnmh266gy2lzj0ymszzrz4i90w8q1n0liyqd"; + }; + } + { + goPackagePath = "github.com/golang/groupcache"; + fetch = { + type = "git"; + url = "https://github.com/golang/groupcache"; + rev = "8c9f03a8e57eb486e42badaed3fb287da51807ba"; + sha256 = "0vjjr79r32icjzlb05wn02k59av7jx0rn1jijml8r4whlg7dnkfh"; + }; + } + { + goPackagePath = "github.com/golang/protobuf"; + fetch = { + type = "git"; + url = "https://github.com/golang/protobuf"; + rev = "ae97035608a719c7a1c1c41bed0ae0744bdb0c6f"; + sha256 = "1mh5fyim42dn821nsd3afnmgscrzzhn3h8rag635d2jnr23r1zhk"; + }; + } + { + goPackagePath = "github.com/google/go-querystring"; + fetch = { + type = "git"; + url = "https://github.com/google/go-querystring"; + rev = "30f7a39f4a218feb5325f3aebc60c32a572a8274"; + sha256 = "1zl8gkriksbdqxn4ijphh79blzfxncjdl2yqxh2v8an9880d2c42"; + }; + } + { + goPackagePath = "github.com/googleapis/gax-go"; + fetch = { + type = "git"; + url = "https://github.com/googleapis/gax-go"; + rev = "bd5b16380fd03dc758d11cef74ba2e3bc8b0e8c2"; + sha256 = "1lxawwngv6miaqd25s3ba0didfzylbwisd2nz7r4gmbmin6jsjrx"; + }; + } + { + goPackagePath = "github.com/intel-go/cpuid"; + fetch = { + type = "git"; + url = "https://github.com/intel-go/cpuid"; + rev = "1a4a6f06a1c643c8fbd339bd61d980960627d09e"; + sha256 = "124i9l1i4ja3k1jq1pac6ric6z5q0n32gdbc252ix33l678lhsw8"; + }; + } + { + goPackagePath = "github.com/jinzhu/copier"; + fetch = { + type = "git"; + url = "https://github.com/jinzhu/copier"; + rev = "7e38e58719c33e0d44d585c4ab477a30f8cb82dd"; + sha256 = "03i7cz8aj42g0kp89myd0hdgzicyk0abfjxa7wcnpx5vlk6x0z0p"; + }; + } + { + goPackagePath = "github.com/jmespath/go-jmespath"; + fetch = { + type = "git"; + url = "https://github.com/jmespath/go-jmespath"; + rev = "c2b33e84"; + sha256 = "1r6w7ydx8ydryxk3sfhzsk8m6f1nsik9jg3i1zhi69v4kfl4d5cz"; + }; + } + { + goPackagePath = "github.com/mitchellh/mapstructure"; + fetch = { + type = "git"; + url = "https://github.com/mitchellh/mapstructure"; + rev = "740c764bc6149d3f1806231418adb9f52c11bcbf"; + sha256 = "0rlz93rmz465nr0wmzvq1n58yc0qdw7v1chr6zmj9jj9pix0a7cb"; + }; + } + { + goPackagePath = "github.com/pmezard/go-difflib"; + fetch = { + type = "git"; + url = "https://github.com/pmezard/go-difflib"; + rev = "792786c7400a136282c1664665ae0a8db921c6c2"; + sha256 = "0c1cn55m4rypmscgf0rrb88pn58j3ysvc2d0432dp3c6fqg6cnzw"; + }; + } + { + goPackagePath = "github.com/rackspace/gophercloud"; + fetch = { + type = "git"; + url = "https://github.com/rackspace/gophercloud"; + rev = "ce0f487f6747ab43c4e4404722df25349385bebd"; + sha256 = "1zr88fcinvlwb3ybimqnxd8fr7c076irp88cvkylm67kv3vfjm4x"; + }; + } + { + goPackagePath = "github.com/samalba/dockerclient"; + fetch = { + type = "git"; + url = "https://github.com/samalba/dockerclient"; + rev = "f661dd4754aa5c52da85d04b5871ee0e11f4b59c"; + sha256 = "0l8nklsnr45h9ng9la3hhrq7qhxqp9yma0fpppc1i5zg8r56rziv"; + }; + } + { + goPackagePath = "github.com/sirupsen/logrus"; + fetch = { + type = "git"; + url = "https://github.com/sirupsen/logrus"; + rev = "d682213848ed68c0a260ca37d6dd5ace8423f5ba"; + sha256 = "0nzyqwzx3k7nqfq8q7yv32gaf3ymq3bpwhkmw1hj2zakq5a93d8x"; + }; + } + { + goPackagePath = "github.com/skarademir/naturalsort"; + fetch = { + type = "git"; + url = "https://github.com/skarademir/naturalsort"; + rev = "69a5d87bef620f77ee8508db30c846b3b84b111e"; + sha256 = "00ibyghnqakbylwxfrlg9jfzgbsm5n73s5fsgr1rmsgdbyv4r5fj"; + }; + } + { + goPackagePath = "github.com/stretchr/objx"; + fetch = { + type = "git"; + url = "https://github.com/stretchr/objx"; + rev = "1a9d0bb9f541897e62256577b352fdbc1fb4fd94"; + sha256 = "1n027ksls1rn1ja98kd0cd2kv1vwlzsl0d7xnh3yqf451vh0md50"; + }; + } + { + goPackagePath = "github.com/stretchr/testify"; + fetch = { + type = "git"; + url = "https://github.com/stretchr/testify"; + rev = "1f4a1643a57e798696635ea4c126e9127adb7d3c"; + sha256 = "0nam9d68rn8ha8ldif22kkgv6k6ph3y88fp26159wdrs63ca3bzl"; + }; + } + { + goPackagePath = "github.com/tent/http-link-go"; + fetch = { + type = "git"; + url = "https://github.com/tent/http-link-go"; + rev = "ac974c61c2f990f4115b119354b5e0b47550e888"; + sha256 = "1fph21b6vp4cm73fkkykffggi57m656x9fd1k369fr6jbvq5fffj"; + }; + } + { + goPackagePath = "github.com/vmware/govcloudair"; + fetch = { + type = "git"; + url = "https://github.com/vmware/govcloudair"; + rev = "66a23eaabc61518f91769939ff541886fe1dceef"; + sha256 = "0795k85j56kh35i94bjjk47bic4nmghnnkyh8cpjvpc1y09vf8zv"; + }; + } + { + goPackagePath = "github.com/vmware/govmomi"; + fetch = { + type = "git"; + url = "https://github.com/vmware/govmomi"; + rev = "9051bd6b44125d9472e0c148b5965692ab283d4a"; + sha256 = "0d8vsm6481746j3r446q5wgppnv2kvq522sd9896xvy32avxsrw3"; + }; + } + { + goPackagePath = "go.opencensus.io"; + fetch = { + type = "git"; + url = "https://github.com/census-instrumentation/opencensus-go"; + rev = "49838f207d61097fc0ebb8aeef306913388376ca"; + sha256 = "0gw4f7inf8y2ik00yfb36xganiq9rl4w2d1a41bsjqsh83ajz2km"; + }; + } + { + goPackagePath = "golang.org/x/crypto"; + fetch = { + type = "git"; + url = "https://go.googlesource.com/crypto"; + rev = "51714a8c4ac1764f07ab4127d7f739351ced4759"; + sha256 = "1x1qj8lbf9034yw1m2hmlc2yp7lz4x3i45ky41ydpzpd0h8dfqnx"; + }; + } + { + goPackagePath = "golang.org/x/net"; + fetch = { + type = "git"; + url = "https://go.googlesource.com/net"; + rev = "c8897c278d1087bda543ec7041384fcedc5e4036"; + sha256 = "0k52czlamank3nfzg47kxhj93gh1pyw8bhiwk29y2839xlvhpz9i"; + }; + } + { + goPackagePath = "golang.org/x/oauth2"; + fetch = { + type = "git"; + url = "https://go.googlesource.com/oauth2"; + rev = "22b0adad7558c54bf49787666d8773cae1dd3e77"; + sha256 = "0vr8x9xk75qy1fgaw77dlgml0kp3llbig4c8cmyhydpd888gw2wr"; + }; + } + { + goPackagePath = "golang.org/x/sys"; + fetch = { + type = "git"; + url = "https://go.googlesource.com/sys"; + rev = "49726bf1d181babaebde545fbf1353be26485fb0"; + sha256 = "1n1vmfz8alfa4chg4qppiybmnqqcrcrs3w3agbrjxmw02aj1csnj"; + }; + } + { + goPackagePath = "golang.org/x/text"; + fetch = { + type = "git"; + url = "https://go.googlesource.com/text"; + rev = "75a595aef632b07c6eeaaa805adb6f0f66e4130e"; + sha256 = "082s9d7wnh1aa2v08g3h5z4if2f8hl4y01pb788qsvab9329lj0w"; + }; + } + { + goPackagePath = "google.golang.org/api"; + fetch = { + type = "git"; + url = "https://github.com/googleapis/google-api-go-client"; + rev = "7fd7a5fcdd3f78a6c49556a5358164cb1405bd51"; + sha256 = "0vs0bnzljg5iib8x01sy49ndgsz3cl1sq53pvy3h6kzp7may0bpc"; + }; + } + { + goPackagePath = "google.golang.org/appengine"; + fetch = { + type = "git"; + url = "https://github.com/golang/appengine"; + rev = "6a436539be38c296a8075a871cc536686b458371"; + sha256 = "0fgxfpfb4mla89yk45rgpsmdkbjnb7ck8dkwc24x879bhpz545kh"; + }; + } + { + goPackagePath = "google.golang.org/genproto"; + fetch = { + type = "git"; + url = "https://github.com/googleapis/go-genproto"; + rev = "ab064af717059515c07699f55ae1133bf9cc7dcc"; + sha256 = "04wjhd8h9xvr3pkcdh7dqq4kz66lgk3dbzqilsm8612ic40xkf43"; + }; + } + { + goPackagePath = "google.golang.org/grpc"; + fetch = { + type = "git"; + url = "https://github.com/grpc/grpc-go"; + rev = "f74f0337644653eba7923908a4d7f79a4f3a267b"; + sha256 = "1m4xsfv3ysc84cwqxqqr61fs3d2w04f0q5xbdjijhczjixcxwh5i"; + }; + } + { + goPackagePath = "google.golang.org/protobuf"; + fetch = { + type = "git"; + url = "https://go.googlesource.com/protobuf"; + rev = "3f7a61f89bb6813f89d981d1870ed68da0b3c3f1"; + sha256 = "0apfl42x166dh96zfq5kvv4b4ax9xljik6bq1mnvn2240ir3mc23"; + }; + } +] diff --git a/pkgs/docker-machine-gitlab/gomod2nix.toml b/pkgs/docker-machine-gitlab/gomod2nix.toml new file mode 100644 index 0000000..f2bae8f --- /dev/null +++ b/pkgs/docker-machine-gitlab/gomod2nix.toml @@ -0,0 +1,232 @@ +schema = 3 + +[mod] + [mod."cloud.google.com/go/compute"] + version = "v1.25.1" + hash = "sha256-A5Wiq8eKgolb81ZpAnoGaBNrTtHpDLLPFgXBJaKcnSA=" + [mod."cloud.google.com/go/compute/metadata"] + version = "v0.2.3" + hash = "sha256-kYB1FTQRdTDqCqJzSU/jJYbVUGyxbkASUKbEs36FUyU=" + [mod."github.com/Azure/azure-sdk-for-go"] + version = "v5.0.0-beta+incompatible" + hash = "sha256-wH+ODrNrEx6aLsbJd1LKztHBz4MDric0gVUHtlq26cg=" + [mod."github.com/Azure/go-autorest"] + version = "v7.2.1+incompatible" + hash = "sha256-yPtI1uwZs5FIJXU+ZUZfSxsnA+b7AmokMzkzXhQz1z4=" + [mod."github.com/Microsoft/go-winio"] + version = "v0.6.1" + hash = "sha256-BL0BVaHtmPKQts/711W59AbHXjGKqFS4ZTal0RYnR9I=" + [mod."github.com/aws/aws-sdk-go"] + version = "v1.36.21" + hash = "sha256-Lko4zIyNdBdKI1u7dWgcf8mjuf1HdpvV8lC6u9TmuUM=" + [mod."github.com/bitly/go-simplejson"] + version = "v0.5.1" + hash = "sha256-ypd4SyAww1+1IlxXlAeGYbTKwl/mahlj9k0MjXJkAJ0=" + [mod."github.com/bugsnag/bugsnag-go"] + version = "v1.0.6-0.20151120182711-02e952891c52" + hash = "sha256-D0oYpL2bgzUlBTs10AM+2MkmwbgfNod4W5hGf4KsP0s=" + [mod."github.com/bugsnag/osext"] + version = "v0.0.0-20130617224835-0dd3f918b21b" + hash = "sha256-Q/iSW9oc9UZEH3GFjTouJoMngM6jPp+bszVcQyv+7Ao=" + [mod."github.com/bugsnag/panicwrap"] + version = "v0.0.0-20160118154447-aceac81c6e2f" + hash = "sha256-q4qH0IhNfxOOjNsWqR4t8Kk0duRexYRAm+P+oaW+nds=" + [mod."github.com/cenkalti/backoff"] + version = "v0.0.0-20141124221459-9831e1e25c87" + hash = "sha256-KyUBfBPlFd37Y9P3xnORI1SbFLxBEtWTsnNf0MaaXkQ=" + [mod."github.com/codegangsta/cli"] + version = "v1.11.1-0.20151120215642-0302d3914d2a" + hash = "sha256-D8uAVNyBdtLvpUD97l0521hSJd+Mk/0oT3xW95cklto=" + [mod."github.com/containerd/log"] + version = "v0.1.0" + hash = "sha256-vuE6Mie2gSxiN3jTKTZovjcbdBd1YEExb7IBe3GM+9s=" + [mod."github.com/davecgh/go-spew"] + version = "v1.1.1" + hash = "sha256-nhzSUrE1fCkN0+RL04N4h8jWmRFPPPWbCuDc7Ss0akI=" + [mod."github.com/dgrijalva/jwt-go"] + version = "v4.5.0" + hash = "sha256-dyKL8wQRApkdCkKxJ1knllvixsrBLw+BtRS0SjlN7NQ=" + replaced = "github.com/golang-jwt/jwt/v4" + [mod."github.com/digitalocean/godo"] + version = "v1.0.1-0.20170317202744-d59ed2fe842b" + hash = "sha256-YwkNqher0TEfaHghZK/YTNOoTpAVtsr27Kk72CIvtfM=" + [mod."github.com/distribution/reference"] + version = "v0.6.0" + hash = "sha256-gr4tL+qz4jKyAtl8LINcxMSanztdt+pybj1T+2ulQv4=" + [mod."github.com/docker/docker"] + version = "v25.0.6+incompatible" + hash = "sha256-sUMANzDY4ORfD6qQ25NUhi7PPWF5v8NlOFquiVeDZmc=" + [mod."github.com/docker/go-connections"] + version = "v0.4.0" + hash = "sha256-GHNIjOuuNp5lFQ308+nDNwQPGESCVV7bCUxSW5ZxZlc=" + [mod."github.com/docker/go-units"] + version = "v0.2.1-0.20151230175859-0bbddae09c5a" + hash = "sha256-PbojDBnjJeH425NPmFUb70+0oAfOUH3r0VdXCT6ViXw=" + [mod."github.com/exoscale/egoscale"] + version = "v0.9.23" + hash = "sha256-DftIgQ0YcZAi+fl/XT2Qnwr/jBGw2g7y4d5vgy67/xE=" + [mod."github.com/felixge/httpsnoop"] + version = "v1.0.4" + hash = "sha256-c1JKoRSndwwOyOxq9ddCe+8qn7mG9uRq2o/822x5O/c=" + [mod."github.com/go-logr/logr"] + version = "v1.4.2" + hash = "sha256-/W6qGilFlZNTb9Uq48xGZ4IbsVeSwJiAMLw4wiNYHLI=" + [mod."github.com/go-logr/stdr"] + version = "v1.2.2" + hash = "sha256-rRweAP7XIb4egtT1f2gkz4sYOu7LDHmcJ5iNsJUd0sE=" + [mod."github.com/gogo/protobuf"] + version = "v1.3.2" + hash = "sha256-pogILFrrk+cAtb0ulqn9+gRZJ7sGnnLLdtqITvxvG6c=" + [mod."github.com/golang/groupcache"] + version = "v0.0.0-20210331224755-41bb18bfe9da" + hash = "sha256-7Gs7CS9gEYZkbu5P4hqPGBpeGZWC64VDwraSKFF+VR0=" + [mod."github.com/golang/protobuf"] + version = "v1.5.4" + hash = "sha256-N3+Lv9lEZjrdOWdQhFj6Y3Iap4rVLEQeI8/eFFyAMZ0=" + [mod."github.com/google/go-querystring"] + version = "v0.0.0-20140804062624-30f7a39f4a21" + hash = "sha256-gjDRAELJKrQF7NgL2iSz3X260oHwykhsx23pGfN8iP4=" + [mod."github.com/google/s2a-go"] + version = "v0.1.7" + hash = "sha256-E+SX/3VmRI5qN7SbnRP4Tt+gQTq93pScpY9U2tTmIU0=" + [mod."github.com/google/uuid"] + version = "v1.6.0" + hash = "sha256-VWl9sqUzdOuhW0KzQlv0gwwUQClYkmZwSydHG2sALYw=" + [mod."github.com/googleapis/enterprise-certificate-proxy"] + version = "v0.3.2" + hash = "sha256-wVuR3QC0mYFl5LNeKdRXdKdod7BGP5sv2h6VVib85v8=" + [mod."github.com/googleapis/gax-go/v2"] + version = "v2.12.2" + hash = "sha256-Ip0d68jgTxN3v1MWFW6nwCsMc70Q9PCM87bhXJdOkHY=" + [mod."github.com/intel-go/cpuid"] + version = "v0.0.0-20181003105527-1a4a6f06a1c6" + hash = "sha256-iGtI0TF0jB5FEWy1J4YFuHzDYjZM3YBlmENJEgNNkYg=" + [mod."github.com/jinzhu/copier"] + version = "v0.0.0-20180308034124-7e38e58719c3" + hash = "sha256-F3zQzaS79GsZP6pLtxSYnsX/GgTN14TuBE8QqdBnJw4=" + [mod."github.com/jmespath/go-jmespath"] + version = "v0.4.0" + hash = "sha256-xpT9g2qIXmPq7eeHUXHiDqJeQoHCudh44G/KCSFbcuo=" + [mod."github.com/juju/loggo"] + version = "v1.0.0" + hash = "sha256-kfQSM4ZtqGXJJ0838/ClX2/SinwCKoDGrvpNVYVqsyk=" + [mod."github.com/mitchellh/mapstructure"] + version = "v0.0.0-20140721150620-740c764bc614" + hash = "sha256-ix0FerxJyiTrNxmysA9vGDCPig14/8pBtsWQX/NIn2Y=" + [mod."github.com/moby/term"] + version = "v0.5.0" + hash = "sha256-jy0kbkeUsr0KoiE33WLxNAgYXZIERKR2O5+saXBwdD8=" + [mod."github.com/morikuni/aec"] + version = "v1.0.0" + hash = "sha256-5zYgLeGr3K+uhGKlN3xv0PO67V+2Zw+cezjzNCmAWOE=" + [mod."github.com/opencontainers/go-digest"] + version = "v1.0.0" + hash = "sha256-cfVDjHyWItmUGZ2dzQhCHgmOmou8v7N+itDkLZVkqkQ=" + [mod."github.com/opencontainers/image-spec"] + version = "v1.0.2" + hash = "sha256-X7kZoMYZNOIDpx8ziK7V+5YM07qiYWOE4yJo+sTOjjU=" + [mod."github.com/pkg/errors"] + version = "v0.9.1" + hash = "sha256-mNfQtcrQmu3sNg/7IwiieKWOgFQOVVe2yXgKBpe/wZw=" + [mod."github.com/pmezard/go-difflib"] + version = "v1.0.0" + hash = "sha256-/FtmHnaGjdvEIKAJtrUfEhV7EVo5A/eYrtdnUkuxLDA=" + [mod."github.com/rackspace/gophercloud"] + version = "v1.0.1-0.20150408191457-ce0f487f6747" + hash = "sha256-MCFOrRu/ctp6LlaSjpLYTrPia3NUUaI0DOxUPLMp9HA=" + [mod."github.com/skarademir/naturalsort"] + version = "v0.0.0-20150715044055-69a5d87bef62" + hash = "sha256-0pVMtl/t6ZpDftoVPY4tVa/3nUyPZtc59WsqbOHzKwI=" + [mod."github.com/stretchr/objx"] + version = "v0.5.2" + hash = "sha256-VKYxrrFb1nkX6Wu3tE5DoP9+fCttwSl9pgLN6567nck=" + [mod."github.com/stretchr/testify"] + version = "v1.9.0" + hash = "sha256-uUp/On+1nK+lARkTVtb5RxlW15zxtw2kaAFuIASA+J0=" + [mod."github.com/tent/http-link-go"] + version = "v0.0.0-20130702225549-ac974c61c2f9" + hash = "sha256-0jlX8F7SZJfMmKG51E0x9ZT4nnPTz+nGqYzcbVYQ8Lo=" + [mod."github.com/vmware/govcloudair"] + version = "v0.0.2" + hash = "sha256-Hk+WfrVIB1GupSP+XbfPO9szJaKUXz0kfoUX8RxIrKw=" + [mod."github.com/vmware/govmomi"] + version = "v0.6.2" + hash = "sha256-MPxva4racsmd4vH76ttps85fhYHW6Ze1uNmCqwdT2so=" + [mod."go.opencensus.io"] + version = "v0.24.0" + hash = "sha256-4H+mGZgG2c9I1y0m8avF4qmt8LUKxxVsTqR8mKgP4yo=" + [mod."go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"] + version = "v0.49.0" + hash = "sha256-1/7YxtXZM4i75rXXIO6UN4CTY93nE/v2k2htS0uUOVg=" + [mod."go.opentelemetry.io/otel"] + version = "v1.28.0" + hash = "sha256-bilBBr2cuADs9bQ7swnGLTuC7h0DooU6BQtrQqMqIjs=" + [mod."go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"] + version = "v1.28.0" + hash = "sha256-nst5au6viPt71JG2TOd6JKfJGJ3QfbktC321XQJUfbU=" + [mod."go.opentelemetry.io/otel/metric"] + version = "v1.28.0" + hash = "sha256-k3p1lYcvrODwIkZo/j2jvCoDFUelz4yVJEEVdUKUmGU=" + [mod."go.opentelemetry.io/otel/sdk"] + version = "v1.28.0" + hash = "sha256-X48fV4A9vgxfjBpmUIQumod2nyI+tUc5lnhFkeKBRsc=" + [mod."go.opentelemetry.io/otel/trace"] + version = "v1.28.0" + hash = "sha256-8uxmlm0/5VGoWegxwy0q8NgeY+pyicSoV08RkvD9Q98=" + [mod."golang.org/x/crypto"] + version = "v0.31.0" + hash = "sha256-ZBjoG7ZOuTEmjaXPP9txAvjAjC46DeaLs0zrNzi8EQw=" + [mod."golang.org/x/mod"] + version = "v0.17.0" + hash = "sha256-CLaPeF6uTFuRDv4oHwOQE6MCMvrzkUjWN3NuyywZjKU=" + [mod."golang.org/x/net"] + version = "v0.33.0" + hash = "sha256-9swkU9vp6IflUUqAzK+y8PytSmrKLuryidP3RmRfe0w=" + [mod."golang.org/x/oauth2"] + version = "v0.18.0" + hash = "sha256-TX4CvtvHU+SGSmqlxaQqlgJjlJiOtLGYAZa0zeBfZak=" + [mod."golang.org/x/sync"] + version = "v0.10.0" + hash = "sha256-HWruKClrdoBKVdxKCyoazxeQV4dIYLdkHekQvx275/o=" + [mod."golang.org/x/sys"] + version = "v0.28.0" + hash = "sha256-kzSlDo5FKsQU9cLefIt2dueGUfz9XuEW+mGSGlPATGc=" + [mod."golang.org/x/term"] + version = "v0.27.0" + hash = "sha256-cb5p/yOlVL7dbkxugUVfqESTVpZ2LtrUWPnx9yue3r0=" + [mod."golang.org/x/text"] + version = "v0.21.0" + hash = "sha256-QaMwddBRnoS2mv9Y86eVC2x2wx/GZ7kr2zAJvwDeCPc=" + [mod."golang.org/x/tools"] + version = "v0.21.1-0.20240508182429-e35e4ccd0d2d" + hash = "sha256-KfnS+3fREPAWQUBoUedPupQp9yLrugxMmmEoHvyzKNE=" + [mod."google.golang.org/api"] + version = "v0.169.0" + hash = "sha256-7+EAvbTTnUutkXpQBbxPSmZTS5P0mrKTiFooMI81w0k=" + [mod."google.golang.org/appengine"] + version = "v1.6.8" + hash = "sha256-decMa0MiWfW/Bzr8QPPzzpeya0YWGHhZAt4Cr/bD1wQ=" + [mod."google.golang.org/genproto/googleapis/api"] + version = "v0.0.0-20240701130421-f6361c86f094" + hash = "sha256-uDvld45ensSUweUJYFdUfVt/0mNRrexpuQ3Jas3GMv4=" + [mod."google.golang.org/genproto/googleapis/rpc"] + version = "v0.0.0-20240701130421-f6361c86f094" + hash = "sha256-ass/74EkCljwk7DaASDtK2zipn2cZv6tCLKvwONUWgY=" + [mod."google.golang.org/grpc"] + version = "v1.64.1" + hash = "sha256-A1+kiePmeqRIdigryUGNJWZiILLacDPtMTEyO6CqDpY=" + [mod."google.golang.org/protobuf"] + version = "v1.34.2" + hash = "sha256-nMTlrDEE2dbpWz50eQMPBQXCyQh4IdjrTIccaU0F3m0=" + [mod."gopkg.in/check.v1"] + version = "v1.0.0-20160105164936-4f90aeace3a2" + hash = "sha256-2+om0xBiwyEtUZRY78rYvWU8ECLVhEipNngXz9ttiVE=" + [mod."gopkg.in/yaml.v3"] + version = "v3.0.1" + hash = "sha256-FqL9TKYJ0XkNwJFnq9j0VvJ5ZUU1RvH/52h/f5bkYAU=" + [mod."gotest.tools/v3"] + version = "v3.5.1" + hash = "sha256-ps2GEc3P2xvlrU4TCtXz+nLTxyP0RrF7SScz5jUqE5E=" + [mod."launchpad.net/gocheck"] + version = "v0.0.0-20140225173054-000000000087" + hash = "sha256-gDJ3emyS6+PruKHOf9BvukVENNUxzXhfKC4Gs6tQLvk=" diff --git a/pkgs/fpvout/default.nix b/pkgs/fpvout/default.nix index 97ecad7..7664f9c 100644 --- a/pkgs/fpvout/default.nix +++ b/pkgs/fpvout/default.nix @@ -1,5 +1,4 @@ -{ lib -, stdenv +{ stdenv , fetchFromGitHub , pkgs }: @@ -19,7 +18,7 @@ stdenv.mkDerivation rec { nativeBuildInputs = with pkgs; [ cmake pkg-config - libusb + libusb1 ]; installPhase = '' diff --git a/pkgs/home-assistant-custom-components/guntamatic.nix b/pkgs/home-assistant-custom-components/guntamatic.nix new file mode 100644 index 0000000..8ddb7cd --- /dev/null +++ b/pkgs/home-assistant-custom-components/guntamatic.nix @@ -0,0 +1,18 @@ +{ fetchFromGitHub +, buildHomeAssistantComponent +}: + +buildHomeAssistantComponent rec { + owner = "a529987659852"; + domain = "GuntamaticBiostar"; + version = "0.2.8"; + + src = fetchFromGitHub { + owner = "a529987659852"; + repo = "GuntamaticBiostar"; + rev = "refs/tags/v${version}"; + hash = "sha256-edKt2LQzxaMXAIeJcBag85ksKPXOfgCENO4jBw9hkCQ="; + }; + + dontBuild = true; +} diff --git a/pkgs/illuminanced/default.nix b/pkgs/illuminanced/default.nix new file mode 100644 index 0000000..3ab99e2 --- /dev/null +++ b/pkgs/illuminanced/default.nix @@ -0,0 +1,20 @@ +{ rustPlatform +, fetchFromGitHub +}: + +let + version = "1.0.2"; +in +rustPlatform.buildRustPackage { + pname = "illuminanced"; + inherit version; + + src = fetchFromGitHub { + owner = "mikhail-m1"; + repo = "illuminanced"; + rev = version; + sha256 = "sha256-ZEVma0uj9rsWB+vfUL7w3dHxI/ppBCG23TirGE+RREk="; + }; + + cargoHash = "sha256-kPWoQ6rE4wBjmqQLNPY4UWJt/AOgr+eVKY0ZK7B4K1A="; +} diff --git a/pkgs/ksoloti/default.nix b/pkgs/ksoloti/default.nix new file mode 100644 index 0000000..54cdb30 --- /dev/null +++ b/pkgs/ksoloti/default.nix @@ -0,0 +1,110 @@ +{ lib +, pkgs +, stdenv +, fetchFromGitHub +, gnumake +, gcc-arm-embedded +, jdk +, libfaketime +, ant +, makeWrapper +, dfu-util +}: + +stdenv.mkDerivation rec { + version = "1.1.0beta"; + pname = "ksoloti"; + + src = fetchFromGitHub { + owner = "ksoloti"; + repo = "ksoloti"; + rev = version; + sha256 = "sha256-RRwar0X2gI0LyM/pNhnlLet06MzrG4z4lm3kCmzWwBc="; + }; + + buildInputs = [ jdk libfaketime ]; + + nativeBuildInputs = [ + ant + gcc-arm-embedded + gnumake + makeWrapper + ]; + + propagatedBuildInputs = [ + dfu-util + gcc-arm-embedded + ]; + + /* + # Hardcode dfu-util path + substituteInPlace "platform_linux/upload_fw_dfu.sh" \ + --replace-fail "/bin/dfu-util" "" + substituteInPlace "platform_linux/upload_fw_dfu.sh" \ + --replace-fail "./dfu-util" "${dfu-util-ksoloti}/bin/dfu-util" + + */ + patchPhase = '' + # Hardcode path to "make" + for f in "firmware/compile_firmware_linux.sh" \ + "firmware/compile_patch_linux.sh"; do + substituteInPlace "$f" \ + --replace "make" "${gnumake}/bin/make" + done + + # Fix build version + substituteInPlace "build.xml" \ + --replace-fail "(git missing)" "${version}" + # Remove build time + substituteInPlace "build.xml" \ + --replace-fail "" "" + substituteInPlace "build.xml" \ + --replace-fail \ + '' \ + '' + substituteInPlace "build.xml" \ + --replace-fail "" "" + substituteInPlace "build.xml" \ + --replace-fail \ + '{line.separator}' \ + '{line.separator} ' + ''; + + buildPhase = '' + ( + find . -exec touch -d '1970-01-01 00:00' {} \; + (cd platform_linux; PATH="${pkgs.gcc-arm-embedded}/bin:$PATH" sh compile_firmware.sh BOARD_AXOLOTI_CORE) + faketime "1970-01-01 00:00:00" ant -Dbuild.runtime=true + ) + ''; + + installPhase = '' + mkdir -p $out/bin $out/share/ksoloti + + cp -r doc firmware chibios platform_linux CMSIS *.txt $out/share/ksoloti/ + install -vD dist/Ksoloti.jar $out/share/ksoloti/ + + patchShebangs $out/share/ksoloti/platform_linux/*.sh + + rm $out/share/ksoloti/platform_linux/bin/dfu-util + ln -s ${dfu-util}/bin/dfu-util $out/share/ksoloti/platform_linux/bin/dfu-util + + makeWrapper ${jdk}/bin/java $out/bin/ksoloti --add-flags "-Dksoloti_release=$out/share/ksoloti -Dksoloti_runtime=$out/share/ksoloti -jar $out/share/ksoloti/Ksoloti.jar" --prefix PATH : ${ + lib.makeBinPath [ + pkgs.gcc-arm-embedded + ] + } + ''; + + meta = with lib; { + homepage = "http://ksoloti.github.io"; + description = '' + Sketching embedded digital audio algorithms. + + To fix permissions of the Ksoloti USB device node, add a similar udev rule to services.udev.extraRules: + SUBSYSTEM=="usb", ATTR{idVendor}=="16c0", ATTR{idProduct}=="0442", OWNER="someuser", GROUP="somegroup" + ''; + license = licenses.gpl3; + maintainers = with maintainers; [ ]; + }; +} diff --git a/modules/udmx.nix b/pkgs/ksoloti/module.nix similarity index 58% rename from modules/udmx.nix rename to pkgs/ksoloti/module.nix index da8df93..2948be5 100644 --- a/modules/udmx.nix +++ b/pkgs/ksoloti/module.nix @@ -1,8 +1,5 @@ -{ config, lib, pkgs, ... }: - { services.udev.extraRules = '' - # uDMX - SUBSYSTEM=="usb", ATTR{idVendor}=="16c0", ATTR{idProduct}=="05dc", GROUP="users", MODE="0660" + SUBSYSTEM=="usb", ATTR{idVendor}=="16c0", ATTR{idProduct}=="0442", OWNER="jalr", GROUP="users" ''; } diff --git a/pkgs/modules.nix b/pkgs/modules.nix index 02b906b..195958b 100644 --- a/pkgs/modules.nix +++ b/pkgs/modules.nix @@ -1,7 +1,6 @@ -{ pkgs, ... }: - { imports = [ - ./pretix/module.nix + ./ksoloti/module.nix + ./myintercom-doorbell/module.nix ]; } diff --git a/pkgs/mute-indicator/pulseaudio_mute_indicator/service.py b/pkgs/mute-indicator/pulseaudio_mute_indicator/service.py index cb5feaf..0ef8e51 100644 --- a/pkgs/mute-indicator/pulseaudio_mute_indicator/service.py +++ b/pkgs/mute-indicator/pulseaudio_mute_indicator/service.py @@ -3,25 +3,25 @@ import serial def run(): - source_to_led_mapping = { - "UMC202HD_input1": 0, - "UMC202HD_input2": 1, - } - with pulsectl.Pulse("event-printer") as pulse: def print_events(ev): with pulsectl.Pulse("event-source-info") as pulse2: source = pulse2.source_info(ev.index) - if source.name in source_to_led_mapping.keys(): + if source.name in [ + "alsa_input.usb-BEHRINGER_UMC202HD_192k-00.analog-stereo", + "alsa_input.usb-BEHRINGER_UMC202HD_192k-00.analog-stereo-input", + ]: muted = bool(source.mute) - led = source_to_led_mapping[source.name] with serial.Serial(port="/dev/mute-indicator", baudrate=115200) as ser: if muted: - ser.write(f"L{led}:32,0,0\n".encode()) + ser.write("L0:32,0,0\n".encode()) + ser.write("L1:32,0,0\n".encode()) + ser.write("S\n".encode()) else: - ser.write(f"L{led}:0,32,0\n".encode()) - ser.write("S\n".encode()) + ser.write("L0:0,32,0\n".encode()) + ser.write("L1:0,32,0\n".encode()) + ser.write("S\n".encode()) pulse.event_mask_set("source") pulse.event_callback_set(print_events) diff --git a/pkgs/myintercom-doorbell/.envrc b/pkgs/myintercom-doorbell/.envrc new file mode 100644 index 0000000..1d953f4 --- /dev/null +++ b/pkgs/myintercom-doorbell/.envrc @@ -0,0 +1 @@ +use nix diff --git a/pkgs/myintercom-doorbell/.gitignore b/pkgs/myintercom-doorbell/.gitignore new file mode 100644 index 0000000..9b1c8b1 --- /dev/null +++ b/pkgs/myintercom-doorbell/.gitignore @@ -0,0 +1 @@ +/dist diff --git a/pkgs/myintercom-doorbell/README.md b/pkgs/myintercom-doorbell/README.md new file mode 100644 index 0000000..e69de29 diff --git a/pkgs/myintercom-doorbell/default.nix b/pkgs/myintercom-doorbell/default.nix new file mode 100644 index 0000000..37b3711 --- /dev/null +++ b/pkgs/myintercom-doorbell/default.nix @@ -0,0 +1,7 @@ +{ poetry2nix }: + +poetry2nix.mkPoetryApplication { + pname = "myintercom-audiosocket"; + version = "0.0.1"; + projectDir = ./.; +} diff --git a/pkgs/myintercom-doorbell/module.nix b/pkgs/myintercom-doorbell/module.nix new file mode 100644 index 0000000..cddb862 --- /dev/null +++ b/pkgs/myintercom-doorbell/module.nix @@ -0,0 +1,157 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.myintercom-doorbell; + mediamtxConfig = pkgs.writeTextFile { + name = "myintercom-doorbell-cam-proxy-config"; + text = lib.generators.toJSON { } { + paths.sprechanlage = { + source = "rtsp://${cfg.username}:__PASSWORD__@${cfg.host}/axis-media/media.amp?videocodec=h264&resolution=1280x720&fps=8&audio=0"; + rtspTransport = "tcp"; + }; + protocols = [ "tcp" ]; + hls = false; + rtmp = false; + rtsp = false; + srt = false; + webrtc = true; + webrtcAdditionalHosts = [ cfg.cam.bindAddress ]; + webrtcLocalTCPAddress = "${cfg.cam.bindAddress}:${toString cfg.cam.webrtcIceTcpPort}"; + }; + }; +in +{ + options.services.myintercom-doorbell = with lib; with lib.types; { + enable = mkEnableOption "Enable myintercom service"; + cam = { + enable = mkEnableOption "Enable cam proxy service"; + bindAddress = mkOption { + type = types.str; + description = "The Address the service binds to."; + example = "10.0.0.1"; + }; + webrtcPort = mkOption { + type = types.port; + description = "Port the WebRTC service binds to."; + default = 8889; + }; + webrtcIceTcpPort = mkOption { + type = types.port; + description = "Port (udp) the WebRTC ICE service binds to."; + default = 8189; + }; + }; + host = mkOption { + type = types.str; + description = "The Hostname of myintercom."; + example = "myintercom.lan.example.net"; + }; + username = mkOption { + type = types.str; + description = "Username for basic auth."; + }; + passwordFile = mkOption { + type = types.path; + description = "Path to the file that contains the basic auth password."; + }; + audiosocket = { + address = mkOption { + type = types.str; + description = "Address the AudioSocket binds to."; + default = "127.0.0.1"; + }; + port = mkOption { + type = types.port; + description = "Port the AudioSocket binds to."; + default = 9092; + }; + uuid = mkOption { + type = types.str; + example = "e461837f-22b0-4652-955f-e1a444f3a42e"; + }; + }; + callerId = mkOption { + type = types.str; + description = "The display name to show when the doorbell rings a phone."; + example = "Doorbell"; + }; + dialTime = mkOption { + type = types.int; + description = "The duration how long to wait for the call to be answered."; + default = 45; + }; + }; + + config = lib.mkIf cfg.enable { + environment.etc."myintercom-doorbell/settings.json".text = builtins.toJSON { + inherit (cfg) host; + inherit (cfg) username; + inherit (cfg) passwordFile; + audiosocket = { + inherit (cfg.audiosocket) address; + inherit (cfg.audiosocket) port; + inherit (cfg.audiosocket) uuid; + }; + inherit (cfg) callerId; + inherit (cfg) dialTime; + }; + + systemd.services = { + myintercom-doorbell-poll = { + inherit (cfg) enable; + description = "Polls myintercom doorbell ring button status."; + after = [ "asterisk.service" "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "simple"; + User = "asterisk"; + # @TODO: Use unstable only until 23.11 is released + ExecStart = "${pkgs.myintercom-doorbell}/bin/myintercom-doorbell-poll"; + }; + }; + myintercom-doorbell-audiosocket = { + inherit (cfg) enable; + description = "myintercom doorbell AudioSocket for Asterisk"; + requires = [ "asterisk.service" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "simple"; + DynamicUser = true; + CapabilityBoundingSet = null; + PrivateUsers = true; + ProtectHome = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + SystemCallFilter = "@system-service"; + LoadCredential = "password:${cfg.passwordFile}"; + Environment = [ + "LISTEN_ADDRESS=${cfg.audiosocket.address}" + "LISTEN_PORT=${toString cfg.audiosocket.port}" + "HOST=${cfg.host}" + "USERNAME=${cfg.username}" + "PASSWORD_FILE=%d/password" + ]; + ExecStart = "${pkgs.myintercom-doorbell}/bin/myintercom-doorbell-audiosocket"; + }; + }; + myintercom-doorbell-cam-proxy = { + inherit (cfg.cam) enable; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + description = "Proxies the videostream of myintercom doorbell."; + script = '' + sed "s:__PASSWORD__:$(cat "$PASSWORD_FILE"):" "${mediamtxConfig}" | ${pkgs.mediamtx}/bin/mediamtx /dev/stdin + ''; + serviceConfig = { + Type = "simple"; + DynamicUser = true; + LoadCredential = "password:${cfg.passwordFile}"; + Environment = [ + "PASSWORD_FILE=%d/password" + ]; + }; + }; + }; + }; +} diff --git a/pkgs/myintercom-doorbell/myintercom_doorbell/audiosocket.py b/pkgs/myintercom-doorbell/myintercom_doorbell/audiosocket.py new file mode 100644 index 0000000..0bb4453 --- /dev/null +++ b/pkgs/myintercom-doorbell/myintercom_doorbell/audiosocket.py @@ -0,0 +1,83 @@ +import socket +from threading import Thread +from dataclasses import dataclass + +from .connection import Connection + + +@dataclass +class audioop_struct: + ratecv_state: None + rate: int + channels: int + ulaw2lin: bool + lin2ulaw: bool + + +# Make a single, global object instance, +# then loop with listen() method alone where needed + + +# Creates a new audiosocket object +class Audiosocket: + def __init__(self, bind_info, timeout=None): + # By default, features of audioop (for example: resampling + # or re-mixng input/output) are disabled + self.user_resample = None + self.asterisk_resample = None + + if not isinstance(bind_info, tuple): + raise TypeError("Expected tuple (addr, port), received", type(bind_info)) + + self.addr, self.port = bind_info + + self.initial_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.initial_sock.bind((self.addr, self.port)) + self.initial_sock.settimeout(timeout) + self.initial_sock.listen(1) + + # If the user didn't specify a port, the one that the operating system + # chose is availble in this attribute + self.port = self.initial_sock.getsockname()[1] + + # Optionally prepares audio sent by the user to + # the specifications needed by audiosocket (16-bit, 8KHz mono LE PCM). + # Audio sent in must be in PCM or ULAW format + def prepare_input(self, inrate=44000, channels=2, ulaw2lin=False, lin2ulaw=False): + self.user_resample = audioop_struct( + rate=inrate, + channels=channels, + ulaw2lin=ulaw2lin, + lin2ulaw=lin2ulaw, + ratecv_state=None, + ) + + # Optionally prepares audio sent by audiosocket to + # the specifications of the user + def prepare_output(self, outrate=44000, channels=2, ulaw2lin=False, lin2ulaw=False): + self.asterisk_resample = audioop_struct( + rate=outrate, + channels=channels, + ulaw2lin=ulaw2lin, + lin2ulaw=lin2ulaw, + ratecv_state=None, + ) + + def listen(self): + conn, peer_addr = self.initial_sock.accept() + connection = Connection( + conn, + peer_addr, + self.user_resample, + self.asterisk_resample, + ) + + connection_thread = Thread(target=connection._process, args=()) + connection_thread.start() + + return connection + + # If we want this single object to serve multiple simultaneous + # connections, accept() will have to be put in a while loop + # If this does become the case, what is the best way to deliver the + # queue objects to the caller, keep them wrapped in read/write methods? diff --git a/pkgs/myintercom-doorbell/myintercom_doorbell/connection.py b/pkgs/myintercom-doorbell/myintercom_doorbell/connection.py new file mode 100644 index 0000000..a5e968e --- /dev/null +++ b/pkgs/myintercom-doorbell/myintercom_doorbell/connection.py @@ -0,0 +1,251 @@ +# Standard Python modules +import audioop +from queue import Queue, Empty +from dataclasses import dataclass +from threading import Lock +from time import sleep + + +# A sort of imitation struct that holds all of the possible +# AudioSocket message types + + +@dataclass(frozen=True) +class types_struct: + uuid: bytes = b"\x01" # Message payload contains UUID set in Asterisk Dialplan + audio: bytes = ( + b"\x10" # * Message payload contains 8Khz 16-bit mono LE PCM audio (* See Github readme) + ) + silence: bytes = ( + b"\x02" # Message payload contains silence (I've never seen this occur personally) + ) + hangup: bytes = ( + b"\x00" # Tell Asterisk to hangup the call (This doesn't appear to ever be sent from Asterisk to us) + ) + error: bytes = b"\xff" # Message payload contains an error from Asterisk + + +types = types_struct() + + +# The size of 20ms of 8KHz 16-bit mono LE PCM represented as a +# 16 bit (2 byte, size of length header) unsigned BE integer + +# This amount of the audio data mentioned above is equal +# to 320 bytes, which is the required payload size when +# sending audio back to AudioSocket for playback on the +# bridged channel. Sending more or less data will result in distorted sound +PCM_SIZE = (320).to_bytes(2, "big") + + +# Similar to one above, this holds all the possible +# AudioSocket related error codes Asterisk can send us + + +@dataclass(frozen=True) +class errors_struct: + none: bytes = b"\x00" + hangup: bytes = b"\x01" + frame: bytes = b"\x02" + memory: bytes = b"\x04" + + +errors = errors_struct() + + +class Connection: + def __init__(self, conn, peer_addr, user_resample, asterisk_resample): + self.conn = conn + self.peer_addr = peer_addr + self.uuid = None + self.connected = True # An instance gets created because a connection occurred + self._user_resample = user_resample + self._asterisk_resample = asterisk_resample + + # Underlying Queue objects for passing incoming/outgoing audio between threads + self._rx_q = Queue(500) + self._tx_q = Queue(500) + + self._lock = Lock() + + # Splits data sent by AudioSocket into three different peices + def _split_data(self, data): + if len(data) < 3: + print( + "[AUDIOSOCKET WARNING] The data received was less than 3 bytes, " + + "the minimum length data from Asterisk AudioSocket should be." + ) + + return b"\x00", 0, bytes(320) + + else: + # type length payload + return data[:1], int.from_bytes(data[1:3], "big"), data[3:] + + # If the type of message received was an error, this + # prints an explanation of the specific one that occurred + def _decode_error(self, payload): + if payload == errors.none: + print("[ASTERISK ERROR] No error code present") + + elif payload == errors.hangup: + print("[ASTERISK ERROR] The called party hungup") + + elif payload == errors.frame: + print("[ASTERISK ERROR] Failed to forward frame") + + elif payload == errors.memory: + print("[ASTERISK ERROR] Memory allocation error") + + return + + # Gets AudioSocket audio from the rx queue + def read(self): + try: + audio = self._rx_q.get(timeout=0.2) + + # If for some reason we receive less than 320 bytes + # of audio, add silence (padding) to the end. This prevents + # audioop related errors that are caused by the current frame + # not being the same size as the last + if len(audio) != 320: + audio += bytes(320 - len(audio)) + + except Empty: + return bytes(320) + + if self._asterisk_resample: + # If AudioSocket is bridged with a channel + # using the ULAW audio codec, the user can specify + # to have it converted to linear encoding upon reading. + if self._asterisk_resample.ulaw2lin: + audio = audioop.ulaw2lin(audio, 2) + + if self._asterisk_resample.lin2ulaw: + audio = audioop.lin2ulaw(audio, 2) + + # If the user requested an outrate different + # from the default, then resample it to the rate they specified + if self._asterisk_resample.rate != 8000: + audio, self._asterisk_resample.ratecv_state = audioop.ratecv( + audio, + 2, + 1, + 8000, + self._asterisk_resample.rate, + self._asterisk_resample.ratecv_state, + ) + + # If the user requested the output be in stereo, + # then convert it from mono + if self._asterisk_resample.channels == 2: + audio = audioop.tostereo(audio, 2, 1, 1) + + return audio + + # Puts user supplied audio into the tx queue + def write(self, audio): + if self._user_resample: + # The user can also specify to have ULAW encoded source audio + # converted to linear encoding upon being written. + if self._user_resample.ulaw2lin: + # Possibly skip downsampling if this was triggered, as + # while ULAW encoded audio can be sampled at rates other + # than 8KHz, since this is telephony related, it's unlikely. + audio = audioop.ulaw2lin(audio, 2) + if self._user_resample.lin2ulaw: + audio = audioop.lin2ulaw(audio, 2) + + # If the audio isn't already sampled at 8KHz, + # then it needs to be downsampled first + if self._user_resample.rate != 8000: + audio, self._user_resample.ratecv_state = audioop.ratecv( + audio, + 2, + self._user_resample.channels, + self._user_resample.rate, + 8000, + self._user_resample.ratecv_state, + ) + + # If the audio isn't already in mono, then + # it needs to be downmixed as well + if self._user_resample.channels == 2: + audio = audioop.tomono(audio, 2, 1, 1) + + self._tx_q.put(audio) + + # *** This may interfere with the thread executing _process, consider + # sending type through queue as well, so a hangup message can be done properly + + # Tells Asterisk to hangup the call from it's end. + # Although after the call is hungup, the socket on Asterisk's end + # closes the connection via an abrupt RST packet, resulting in a "Connection reset by peer" + # error on our end. Unfortunately, using try and except around self.conn.recv() is as + # clean as I think it can be right now + def hangup(self): + # Three bytes of 0 indicate a hangup message + with self._lock: + self.conn.send(types.hangup * 3) + + sleep(0.2) + return + + def _process(self): + # The main audio receiving/sending loop, this loops + # until AudioSocket stops sending us data, the hangup() method is called or an error occurs. + # A disconnection can be triggered from the users end by calling the hangup() method + while True: + data = None + + try: + with self._lock: + data = self.conn.recv(323) + + except ConnectionResetError: + pass + + if not data: + self.connected = False + self.conn.close() + return + + type, length, payload = self._split_data(data) + + if type == types.audio: + # Adds received audio into the rx queue + if self._rx_q.full(): + print( + "[AUDIOSOCKET WARNING] The inbound audio queue is full! This most " + + "likely occurred because the read() method is not being called, skipping frame" + ) + + else: + self._rx_q.put(payload) + + # To prevent the tx queue from blocking all execution if + # the user doesn't supply it with (enough) audio, silence is + # generated manually and sent back to AudioSocket whenever its empty. + if self._tx_q.empty(): + self.conn.send(types.audio + PCM_SIZE + bytes(320)) + + else: + # If a single peice of audio data in the rx queue is larger than + # 320 bytes, slice it before sending, however... + # If the audio data to send is larger than this, then + # it's probably in the wrong format to begin with and wont be + # played back properly even when sliced. + audio_data = self._tx_q.get()[:320] + + with self._lock: + self.conn.send( + types.audio + + len(audio_data).to_bytes(2, "big") + + audio_data + ) + + elif type == types.error: + self._decode_error(payload) + + elif type == types.uuid: + self.uuid = payload.hex() diff --git a/pkgs/myintercom-doorbell/myintercom_doorbell/myintercom_audiosocket.py b/pkgs/myintercom-doorbell/myintercom_doorbell/myintercom_audiosocket.py new file mode 100644 index 0000000..574dc28 --- /dev/null +++ b/pkgs/myintercom-doorbell/myintercom_doorbell/myintercom_audiosocket.py @@ -0,0 +1,101 @@ +import os +import sys +import urllib3 + +from multiprocessing import Pipe, Process +from threading import Thread + +from .audiosocket import Audiosocket + + +def open_url(direction, connection, host, username, password): + print(f"start {direction}", flush=True) + if direction not in ("transmit", "receive"): + raise NotImplementedError + method, headers = { + "receive": ("GET", {}), + "transmit": ("POST", {"Content-Type": "audio/basic", "Content-Length": "0"}), + }[direction] + + url = f"http://{host}/axis-cgi/audio/{direction}.cgi" + + http = urllib3.PoolManager() + + print(f"start {direction} request", flush=True) + + response = http.request( + method, + url, + headers={ + **urllib3.make_headers(basic_auth=f"{username}:{password}"), + **headers, + }, + preload_content=False, + body=( + None + if direction != "transmit" + else os.fdopen(connection.fileno(), "rb", buffering=0) + ), + ) + print(f"{direction} status is {response.status}", flush=True) + + if direction == "receive": + for data in response.stream(amt=160): + connection.send_bytes(data) + + +def handle_connection(call, host, username, password): + print(f"Received connection from {call.peer_addr}") + + pipe_transmit_in, pipe_transmit_out = Pipe() + doorbell_transmit_process = Process( + target=open_url, args=("transmit", pipe_transmit_out, host, username, password) + ) + doorbell_transmit_process.start() + + pipe_receive_in, pipe_receive_out = Pipe() + doorbell_receive_process = Process( + target=open_url, args=("receive", pipe_receive_in, host, username, password) + ) + doorbell_receive_process.start() + + with os.fdopen(os.dup(pipe_transmit_in.fileno()), "wb", buffering=0) as f_transmit: + while call.connected: + f_transmit.write(call.read()) + call.write(pipe_receive_out.recv_bytes()) + + print(f"Connection with {call.peer_addr} is now over") + doorbell_transmit_process.terminate() + doorbell_receive_process.terminate() + doorbell_transmit_process.join() + doorbell_receive_process.join() + + +def main(): + audiosocket = Audiosocket( + (os.environ["LISTEN_ADDRESS"], int(os.environ["LISTEN_PORT"])) + ) + + audiosocket.prepare_output(outrate=8000, channels=1, lin2ulaw=True) + audiosocket.prepare_input(inrate=8000, channels=1, ulaw2lin=True) + + print("Listening for new connections " f"from Asterisk on port {audiosocket.port}") + host = os.environ["HOST"] + username = os.environ["USERNAME"] + + with open(os.environ["PASSWORD_FILE"], "r", encoding="utf-8") as f: + password = f.read() + + while True: + call = audiosocket.listen() + + call_thread = Thread( + target=handle_connection, args=(call, host, username, password) + ) + call_thread.start() + + call_thread.join() + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/pkgs/myintercom-doorbell/myintercom_doorbell/open.py b/pkgs/myintercom-doorbell/myintercom_doorbell/open.py new file mode 100644 index 0000000..f3a7211 --- /dev/null +++ b/pkgs/myintercom-doorbell/myintercom_doorbell/open.py @@ -0,0 +1,36 @@ +import json +import os +import sys +import tempfile + +import urllib3 + + +def open_door(host, username, password): + urllib3.PoolManager().request( + "GET", + f"http://{host}/local/Doorcom/door.cgi?r=1", + headers=urllib3.make_headers(basic_auth=f"{username}:{password}"), + ) + + +def read_file_contents(file_name): + with open(file_name, "r", encoding="utf-8") as f: + return f.read() + + +def main(): + with open( + "/etc/myintercom-doorbell/settings.json", "r", encoding="utf-8" + ) as config_file: + config = json.load(config_file) + + open_door( + host=config["host"], + username=config["username"], + password=read_file_contents(config["passwordFile"]), + ) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/pkgs/myintercom-doorbell/myintercom_doorbell/service.py b/pkgs/myintercom-doorbell/myintercom_doorbell/service.py new file mode 100644 index 0000000..3def826 --- /dev/null +++ b/pkgs/myintercom-doorbell/myintercom_doorbell/service.py @@ -0,0 +1,107 @@ +import json +import os +import tempfile + +import urllib3 + +import time + + +def send_open_door_request(host, username, password): + urllib3.PoolManager( + timeout=urllib3.Timeout( + connect=3.0, + read=300, + ) + ).request( + "GET", + f"http://{host}/local/Doorcom/door.cgi?r=1", + headers=urllib3.make_headers(basic_auth=f"{username}:{password}"), + ) + + +def get_ring_status(host, username, password): + url = f"http://{host}/local/Doorcom/monitor.cgi?ring=1" + print(f"sending request to {url}", flush=True) + try: + response = urllib3.PoolManager().request( + "GET", + url, + headers=urllib3.make_headers(basic_auth=f"{username}:{password}"), + preload_content=False, + decode_content=True, + ) + except urllib3.exceptions.MaxRetryError: + return + + while True: + line = response.readline() + if line != b"--ioboundary\r\n": + continue + header = response.readline() + if header != b"Content-Type: text/plain\r\n": + continue + if response.readline() != b"\r\n": + continue + data = [] + while True: + line = response.readline() + if line != b"\r\n": + data.append(line.decode().rstrip()) + else: + if data: + print(f"received: {data}", flush=True) + yield data + break + + +def read_file_contents(file_name): + with open(file_name, "r", encoding="utf-8") as f: + return f.read() + + +def open_door(): + with open( + "/etc/myintercom-doorbell/settings.json", "r", encoding="utf-8" + ) as config_file: + config = json.load(config_file) + + send_open_door_request( + host=config["host"], + username=config["username"], + password=read_file_contents(config["passwordFile"]), + ) + + +def poll(): + outgoing_dir = "/var/spool/asterisk/outgoing/" + + with open( + "/etc/myintercom-doorbell/settings.json", "r", encoding="utf-8" + ) as config_file: + config = json.load(config_file) + + audiosocket = f"{config['audiosocket']['address']}:{config['audiosocket']['port']}/{config['audiosocket']['uuid']}" + callfile_content = ( + f"Channel: Audiosocket/{audiosocket}\n" + "Context: doorbell\n" + f"CallerID: {config['callerId']}\n" + "Extension: s\n" + "Priority: 1\n" + ) + + while True: + for status in get_ring_status( + host=config["host"], + username=config["username"], + password=read_file_contents(config["passwordFile"]), + ): + if status == ["1:H"]: + print("ringing", flush=True) + with tempfile.NamedTemporaryFile(dir="/var/tmp", mode="w") as f: + f.write(callfile_content) + os.chmod(f.name, 0o644) + os.link( + f.name, os.path.join(outgoing_dir, os.path.basename(f.name)) + ) + time.sleep(config["dialTime"]) diff --git a/pkgs/myintercom-doorbell/poetry.lock b/pkgs/myintercom-doorbell/poetry.lock new file mode 100644 index 0000000..0ce0d9b --- /dev/null +++ b/pkgs/myintercom-doorbell/poetry.lock @@ -0,0 +1,24 @@ +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. + +[[package]] +name = "urllib3" +version = "2.5.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[metadata] +lock-version = "2.1" +python-versions = "^3.12" +content-hash = "a2502d4bca34c8c9ddc7579666a62dc15d3573a0240075cb566922a1d031831e" diff --git a/pkgs/myintercom-doorbell/pyproject.toml b/pkgs/myintercom-doorbell/pyproject.toml new file mode 100644 index 0000000..3a57dc6 --- /dev/null +++ b/pkgs/myintercom-doorbell/pyproject.toml @@ -0,0 +1,20 @@ +[tool.poetry] +name = "myintercom-doorbell" +version = "0.1.0" +description = "" +authors = ["Jakob Lechner "] +readme = "README.md" +packages = [{include = "myintercom_doorbell"}] + +[tool.poetry.dependencies] +python = "^3.12" +urllib3 = "^2.5.0" + +[tool.poetry.scripts] +myintercom-doorbell-audiosocket = "myintercom_doorbell.myintercom_audiosocket:main" +myintercom-doorbell-open-door = "myintercom_doorbell.service:open_door" +myintercom-doorbell-poll = "myintercom_doorbell.service:poll" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/pkgs/pretix/shell.nix b/pkgs/myintercom-doorbell/shell.nix similarity index 100% rename from pkgs/pretix/shell.nix rename to pkgs/myintercom-doorbell/shell.nix diff --git a/pkgs/pomodoro-timer/default.nix b/pkgs/pomodoro-timer/default.nix new file mode 100644 index 0000000..a3638f8 --- /dev/null +++ b/pkgs/pomodoro-timer/default.nix @@ -0,0 +1,61 @@ +{ lib +, stdenv +, yad +, uair +, writeShellScript +, makeDesktopItem +, imagemagick +}: + +let + pomodoroTimer = writeShellScript "pomodoro-timer" '' + export PATH=${lib.makeBinPath [yad uair]} + uairctl listen -o yad \ + | yad \ + --title="Pomodoro" \ + --geometry="300x50" \ + --scale \ + --progress \ + --no-buttons \ + --css="* { font-size: 60px;} progress { min-height: 1200px; margin: -100px -8px -6px;}" + ''; +in +stdenv.mkDerivation rec { + pname = "pomodoro-timer"; + version = "1.0.0"; + src = ./pomodorotimer.svg; + dontUnpack = true; + + installPhase = '' + icon_size=64x64 + dir=$out/share/icons/hicolor/scalable/apps/ + mkdir -p \ + $out/share/icons/hicolor/scalable/apps/ \ + $out/share/icons/hicolor/$icon_size/apps/ + + cp $src $dir/pomodorotimer.svg + mkdir $out/bin + cp "${pomodoroTimer}" $out/bin/pomodoro-timer + ln -s "${desktopItem}/share/applications" $out/share/ + + ${imagemagick}/bin/convert \ + -size $icon_size \ + $src \ + $out/share/icons/hicolor/$icon_size/apps/pomodorotimer.png + ''; + + desktopItem = makeDesktopItem { + name = pname; + exec = pname; + icon = "pomodorotimer"; + desktopName = pname; + comment = meta.description; + categories = [ "Utility" ]; + }; + + meta = with lib; { + description = "Pomodoro timer (GUI for uair)"; + maintainers = with maintainers; [ jalr ]; + platforms = platforms.linux; + }; +} diff --git a/pkgs/pomodoro-timer/pomodorotimer.svg b/pkgs/pomodoro-timer/pomodorotimer.svg new file mode 100644 index 0000000..b771a33 --- /dev/null +++ b/pkgs/pomodoro-timer/pomodorotimer.svg @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + + + + + + + + + + + Pomodoro Timer + + + + + + + image/svg+xml + + + + + Openclipart + + + Pomodoro Timer + 2011-05-03T21:58:11 + The Pomodoro Technique® is a way to get the most out of time management. Develop by Francesco Cirillo. + https://openclipart.org/detail/135631/pomodoro-timer-by-fairhonanth + + + fairhonanth + + + + + management + pomodoro + productivity + techinique + timer + + + + + + + + + + + diff --git a/pkgs/pretix/module.nix b/pkgs/pretix/module.nix deleted file mode 100644 index 12872bb..0000000 --- a/pkgs/pretix/module.nix +++ /dev/null @@ -1,267 +0,0 @@ -{ config, lib, pkgs, ... }: - -let - cfg = config.services.pretix; - name = "pretix"; - user = "pretix"; - group = "pretix"; - bind = { - host = "127.0.0.1"; - port = 8000; - }; - postgresql = { - database = "pretix"; - user = "pretix"; - password = "pretix"; - }; - redisPort = 6379; - urlScheme = if cfg.enableTls then "https" else "http"; - url = "${urlScheme}://${cfg.domain}"; - toBool = x: if x then "on" else "off"; - hstsHeader = if cfg.enableTls then "add_header Strict-Transport-Security \"max-age=31536000; includeSubDomains; preload\" always;" else ""; - pythonPackages = pkgs.pretix.passthru.pythonModule.passthru.pkgs; - python = pkgs.pretix.passthru.python; - runCommandArgs = { - # Sets PYTHONPATH in derivation - buildInputs = [ - pkgs.pretix - pythonPackages.gunicorn - pythonPackages.celery - ]; - }; - staticRoot = pkgs.pretix-static; - environmentFile = pkgs.runCommand "pretix-environ" runCommandArgs '' - cat > $out <=4.0)"] - -[[package]] -name = "asgiref" -version = "3.6.0" -description = "ASGI specs, helper code, and adapters" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "asgiref-3.6.0-py3-none-any.whl", hash = "sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac"}, - {file = "asgiref-3.6.0.tar.gz", hash = "sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506"}, -] - -[package.extras] -tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] - -[[package]] -name = "async-timeout" -version = "4.0.2" -description = "Timeout context manager for asyncio programs" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"}, - {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, -] - -[[package]] -name = "attrs" -version = "22.2.0" -description = "Classes Without Boilerplate" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"}, - {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"}, -] - -[package.extras] -cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] -dev = ["attrs[docs,tests]"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"] -tests = ["attrs[tests-no-zope]", "zope.interface"] -tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"] - -[[package]] -name = "babel" -version = "2.12.1" -description = "Internationalization utilities" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Babel-2.12.1-py3-none-any.whl", hash = "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610"}, - {file = "Babel-2.12.1.tar.gz", hash = "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455"}, -] - -[[package]] -name = "beautifulsoup4" -version = "4.11.2" -description = "Screen-scraping library" -category = "main" -optional = false -python-versions = ">=3.6.0" -files = [ - {file = "beautifulsoup4-4.11.2-py3-none-any.whl", hash = "sha256:0e79446b10b3ecb499c1556f7e228a53e64a2bfcebd455f370d8927cb5b59e39"}, - {file = "beautifulsoup4-4.11.2.tar.gz", hash = "sha256:bc4bdda6717de5a2987436fb8d72f45dc90dd856bdfd512a1314ce90349a0106"}, -] - -[package.dependencies] -soupsieve = ">1.2" - -[package.extras] -html5lib = ["html5lib"] -lxml = ["lxml"] - -[[package]] -name = "billiard" -version = "3.6.4.0" -description = "Python multiprocessing fork with improvements and bugfixes" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "billiard-3.6.4.0-py3-none-any.whl", hash = "sha256:87103ea78fa6ab4d5c751c4909bcff74617d985de7fa8b672cf8618afd5a875b"}, - {file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"}, -] - -[[package]] -name = "bleach" -version = "5.0.1" -description = "An easy safelist-based HTML-sanitizing tool." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "bleach-5.0.1-py3-none-any.whl", hash = "sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a"}, - {file = "bleach-5.0.1.tar.gz", hash = "sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c"}, -] - -[package.dependencies] -six = ">=1.9.0" -webencodings = "*" - -[package.extras] -css = ["tinycss2 (>=1.1.0,<1.2)"] -dev = ["Sphinx (==4.3.2)", "black (==22.3.0)", "build (==0.8.0)", "flake8 (==4.0.1)", "hashin (==0.17.0)", "mypy (==0.961)", "pip-tools (==6.6.2)", "pytest (==7.1.2)", "tox (==3.25.0)", "twine (==4.0.1)", "wheel (==0.37.1)"] - -[[package]] -name = "cbor2" -version = "5.4.6" -description = "CBOR (de)serializer with extensive tag support" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "cbor2-5.4.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:309fffbb7f561d67f02095d4b9657b73c9220558701c997e9bfcfbca2696e927"}, - {file = "cbor2-5.4.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ff95b33e5482313a74648ca3620c9328e9f30ecfa034df040b828e476597d352"}, - {file = "cbor2-5.4.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db9eb582fce972f0fa429d8159b7891ff8deccb7affc4995090afc61ce0d328a"}, - {file = "cbor2-5.4.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3950be57a1698086cf26d8710b4e5a637b65133c5b1f9eec23967d4089d8cfed"}, - {file = "cbor2-5.4.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:78304df140b9e13b93bcbb2aecee64c9aaa9f1cadbd45f043b5e7b93cc2f21a2"}, - {file = "cbor2-5.4.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e73ca40dd3c7210ff776acff9869ddc9ff67bae7c425b58e5715dcf55275163f"}, - {file = "cbor2-5.4.6-cp310-cp310-win_amd64.whl", hash = "sha256:0b956f19e93ba3180c336282cd1b6665631f2d3a196a9c19b29a833bf979e7a4"}, - {file = "cbor2-5.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c12c0ab78f5bc290b08a79152a8621822415836a86f8f4b50dadba371736fda"}, - {file = "cbor2-5.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3545b16f9f0d5f34d4c99052829c3726020a07be34c99c250d0df87418f02954"}, - {file = "cbor2-5.4.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24144822f8d2b0156f4cda9427f071f969c18683ffed39663dc86bc0a75ae4dd"}, - {file = "cbor2-5.4.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1835536e76ea16e88c934aac5e369ba9f93d495b01e5fa2d93f0b4986b89146d"}, - {file = "cbor2-5.4.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:39452c799453f5bf33281ffc0752c620b8bfa0b7c13070b87d370257a1311976"}, - {file = "cbor2-5.4.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3316f09a77af85e7772ecfdd693b0f450678a60b1aee641bac319289757e3fa0"}, - {file = "cbor2-5.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:456cdff668a50a52fdb8aa6d0742511e43ed46d6a5b463dba80a5a720fa0d320"}, - {file = "cbor2-5.4.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9394ca49ecdf0957924e45d09a4026482d184a465a047f60c4044eb464c43de9"}, - {file = "cbor2-5.4.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56dfa030cd3d67e5b6701d3067923f2f61536a8ffb1b45be14775d1e866b59ae"}, - {file = "cbor2-5.4.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5094562dfe3e5583202b93ef7ca5082c2ba5571accb2c4412d27b7d0ba8a563"}, - {file = "cbor2-5.4.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:94f844d0e232aca061a86dd6ff191e47ba0389ddd34acb784ad9a41594dc99a4"}, - {file = "cbor2-5.4.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7bbd3470eb685325398023e335be896b74f61b014896604ed45049a7b7b6d8ac"}, - {file = "cbor2-5.4.6-cp37-cp37m-win_amd64.whl", hash = "sha256:0bd12c54a48949d11f5ffc2fa27f5df1b4754111f5207453e5fae3512ebb3cab"}, - {file = "cbor2-5.4.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2984a488f350aee1d54fa9cb8c6a3c1f1f5b268abbc91161e47185de4d829f3"}, - {file = "cbor2-5.4.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c285a2cb2c04004bfead93df89d92a0cef1874ad337d0cb5ea53c2c31e97bfdb"}, - {file = "cbor2-5.4.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6709d97695205cd08255363b54afa035306d5302b7b5e38308c8ff5a47e60f2a"}, - {file = "cbor2-5.4.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96087fa5336ebfc94465c0768cd5de0fcf9af3840d2cf0ce32f5767855f1a293"}, - {file = "cbor2-5.4.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0d2b926b024d3a1549b819bc82fdc387062bbd977b0299dd5fa5e0ea3267b98b"}, - {file = "cbor2-5.4.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6e1b5aee920b6a2f737aa12e2b54de3826b09f885a7ce402db84216343368140"}, - {file = "cbor2-5.4.6-cp38-cp38-win_amd64.whl", hash = "sha256:79e048e623846d60d735bb350263e8fdd36cb6195d7f1a2b57eacd573d9c0b33"}, - {file = "cbor2-5.4.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:80ac8ba450c7a41c5afe5f7e503d3092442ed75393e1de162b0bf0d97edf7c7f"}, - {file = "cbor2-5.4.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ce1a2c272ba8523a55ea2f1d66e3464e89fa0e37c9a3d786a919fe64e68dbd7"}, - {file = "cbor2-5.4.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1618d16e310f7ffed141762b0ff5d8bb6b53ad449406115cc465bf04213cefcf"}, - {file = "cbor2-5.4.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bbbdb2e3ef274865dc3f279aae109b5d94f4654aea3c72c479fb37e4a1e7ed7"}, - {file = "cbor2-5.4.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6f9c702bee2954fffdfa3de95a5af1a6b1c5f155e39490353d5654d83bb05bb9"}, - {file = "cbor2-5.4.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b9f3924da0e460a93b3674c7e71020dd6c9e9f17400a34e52a88c0af2dcd2aa"}, - {file = "cbor2-5.4.6-cp39-cp39-win_amd64.whl", hash = "sha256:d54bd840b4fe34f097b8665fc0692c7dd175349e53976be6c5de4433b970daa4"}, - {file = "cbor2-5.4.6-py3-none-any.whl", hash = "sha256:181ac494091d1f9c5bb373cd85514ce1eb967a8cf3ec298e8dfa8878aa823956"}, - {file = "cbor2-5.4.6.tar.gz", hash = "sha256:b893500db0fe033e570c3adc956af6eefc57e280026bd2d86fd53da9f1e594d7"}, -] - -[package.extras] -doc = ["sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["pytest", "pytest-cov"] - -[[package]] -name = "celery" -version = "5.2.7" -description = "Distributed Task Queue." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "celery-5.2.7-py3-none-any.whl", hash = "sha256:138420c020cd58d6707e6257b6beda91fd39af7afde5d36c6334d175302c0e14"}, - {file = "celery-5.2.7.tar.gz", hash = "sha256:fafbd82934d30f8a004f81e8f7a062e31413a23d444be8ee3326553915958c6d"}, -] - -[package.dependencies] -billiard = ">=3.6.4.0,<4.0" -click = ">=8.0.3,<9.0" -click-didyoumean = ">=0.0.3" -click-plugins = ">=1.1.1" -click-repl = ">=0.2.0" -kombu = ">=5.2.3,<6.0" -pytz = ">=2021.3" -vine = ">=5.0.0,<6.0" - -[package.extras] -arangodb = ["pyArango (>=1.3.2)"] -auth = ["cryptography"] -azureblockblob = ["azure-storage-blob (==12.9.0)"] -brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"] -cassandra = ["cassandra-driver (<3.21.0)"] -consul = ["python-consul2"] -cosmosdbsql = ["pydocumentdb (==2.3.2)"] -couchbase = ["couchbase (>=3.0.0)"] -couchdb = ["pycouchdb"] -django = ["Django (>=1.11)"] -dynamodb = ["boto3 (>=1.9.178)"] -elasticsearch = ["elasticsearch"] -eventlet = ["eventlet (>=0.32.0)"] -gevent = ["gevent (>=1.5.0)"] -librabbitmq = ["librabbitmq (>=1.5.0)"] -memcache = ["pylibmc"] -mongodb = ["pymongo[srv] (>=3.11.1)"] -msgpack = ["msgpack"] -pymemcache = ["python-memcached"] -pyro = ["pyro4"] -pytest = ["pytest-celery"] -redis = ["redis (>=3.4.1,!=4.0.0,!=4.0.1)"] -s3 = ["boto3 (>=1.9.125)"] -slmq = ["softlayer-messaging (>=1.0.3)"] -solar = ["ephem"] -sqlalchemy = ["sqlalchemy"] -sqs = ["kombu[sqs]"] -tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"] -yaml = ["PyYAML (>=3.10)"] -zookeeper = ["kazoo (>=1.3.1)"] -zstd = ["zstandard"] - -[[package]] -name = "certifi" -version = "2022.12.7" -description = "Python package for providing Mozilla's CA Bundle." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, - {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, -] - -[[package]] -name = "cffi" -version = "1.15.1" -description = "Foreign Function Interface for Python calling C code." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, -] - -[package.dependencies] -pycparser = "*" - -[[package]] -name = "chardet" -version = "5.1.0" -description = "Universal encoding detector for Python 3" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "chardet-5.1.0-py3-none-any.whl", hash = "sha256:362777fb014af596ad31334fde1e8c327dfdb076e1960d1694662d46a6917ab9"}, - {file = "chardet-5.1.0.tar.gz", hash = "sha256:0d62712b956bc154f85fb0a266e2a3c5913c2967e00348701b32411d6def31e5"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.1.0" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, - {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, - {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, - {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, - {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, - {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, - {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, -] - -[[package]] -name = "click" -version = "8.1.3" -description = "Composable command line interface toolkit" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "click-didyoumean" -version = "0.3.0" -description = "Enables git-like *did-you-mean* feature in click" -category = "main" -optional = false -python-versions = ">=3.6.2,<4.0.0" -files = [ - {file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"}, - {file = "click_didyoumean-0.3.0-py3-none-any.whl", hash = "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667"}, -] - -[package.dependencies] -click = ">=7" - -[[package]] -name = "click-plugins" -version = "1.1.1" -description = "An extension module for click to enable registering CLI commands via setuptools entry-points." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, - {file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"}, -] - -[package.dependencies] -click = ">=4.0" - -[package.extras] -dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"] - -[[package]] -name = "click-repl" -version = "0.2.0" -description = "REPL plugin for Click" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "click-repl-0.2.0.tar.gz", hash = "sha256:cd12f68d745bf6151210790540b4cb064c7b13e571bc64b6957d98d120dacfd8"}, - {file = "click_repl-0.2.0-py3-none-any.whl", hash = "sha256:94b3fbbc9406a236f176e0506524b2937e4b23b6f4c0c0b2a0a83f8a64e9194b"}, -] - -[package.dependencies] -click = "*" -prompt-toolkit = "*" -six = "*" - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "cryptography" -version = "39.0.2" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "cryptography-39.0.2-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:2725672bb53bb92dc7b4150d233cd4b8c59615cd8288d495eaa86db00d4e5c06"}, - {file = "cryptography-39.0.2-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:23df8ca3f24699167daf3e23e51f7ba7334d504af63a94af468f468b975b7dd7"}, - {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:eb40fe69cfc6f5cdab9a5ebd022131ba21453cf7b8a7fd3631f45bbf52bed612"}, - {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc0521cce2c1d541634b19f3ac661d7a64f9555135e9d8af3980965be717fd4a"}, - {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffd394c7896ed7821a6d13b24657c6a34b6e2650bd84ae063cf11ccffa4f1a97"}, - {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:e8a0772016feeb106efd28d4a328e77dc2edae84dfbac06061319fdb669ff828"}, - {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8f35c17bd4faed2bc7797d2a66cbb4f986242ce2e30340ab832e5d99ae60e011"}, - {file = "cryptography-39.0.2-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b49a88ff802e1993b7f749b1eeb31134f03c8d5c956e3c125c75558955cda536"}, - {file = "cryptography-39.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:5f8c682e736513db7d04349b4f6693690170f95aac449c56f97415c6980edef5"}, - {file = "cryptography-39.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:d7d84a512a59f4412ca8549b01f94be4161c94efc598bf09d027d67826beddc0"}, - {file = "cryptography-39.0.2-cp36-abi3-win32.whl", hash = "sha256:c43ac224aabcbf83a947eeb8b17eaf1547bce3767ee2d70093b461f31729a480"}, - {file = "cryptography-39.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:788b3921d763ee35dfdb04248d0e3de11e3ca8eb22e2e48fef880c42e1f3c8f9"}, - {file = "cryptography-39.0.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d15809e0dbdad486f4ad0979753518f47980020b7a34e9fc56e8be4f60702fac"}, - {file = "cryptography-39.0.2-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:50cadb9b2f961757e712a9737ef33d89b8190c3ea34d0fb6675e00edbe35d074"}, - {file = "cryptography-39.0.2-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:103e8f7155f3ce2ffa0049fe60169878d47a4364b277906386f8de21c9234aa1"}, - {file = "cryptography-39.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6236a9610c912b129610eb1a274bdc1350b5df834d124fa84729ebeaf7da42c3"}, - {file = "cryptography-39.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e944fe07b6f229f4c1a06a7ef906a19652bdd9fd54c761b0ff87e83ae7a30354"}, - {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:35d658536b0a4117c885728d1a7032bdc9a5974722ae298d6c533755a6ee3915"}, - {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:30b1d1bfd00f6fc80d11300a29f1d8ab2b8d9febb6ed4a38a76880ec564fae84"}, - {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e029b844c21116564b8b61216befabca4b500e6816fa9f0ba49527653cae2108"}, - {file = "cryptography-39.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fa507318e427169ade4e9eccef39e9011cdc19534f55ca2f36ec3f388c1f70f3"}, - {file = "cryptography-39.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8bc0008ef798231fac03fe7d26e82d601d15bd16f3afaad1c6113771566570f3"}, - {file = "cryptography-39.0.2.tar.gz", hash = "sha256:bc5b871e977c8ee5a1bbc42fa8d19bcc08baf0c51cbf1586b0e87a2694dde42f"}, -] - -[package.dependencies] -cffi = ">=1.12" - -[package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] -docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] -pep8test = ["black", "check-manifest", "mypy", "ruff", "types-pytz", "types-requests"] -sdist = ["setuptools-rust (>=0.11.4)"] -ssh = ["bcrypt (>=3.1.5)"] -test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-shard (>=0.1.2)", "pytest-subtests", "pytest-xdist", "pytz"] -test-randomorder = ["pytest-randomly"] -tox = ["tox"] - -[[package]] -name = "css-inline" -version = "0.8.7" -description = "Fast CSS inlining written in Rust" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "css_inline-0.8.7-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:3604d7af3df90681a5a31d2a1438bd237ae1ba171f2ea1cb62824f4909238a63"}, - {file = "css_inline-0.8.7-cp37-abi3-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:6e0afb35e17888b2ddd8efee738f1f68ae569615cb32b66427381372cab2d6b2"}, - {file = "css_inline-0.8.7-cp37-abi3-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:84e2b2c5c7c16b5ff546f9fae53e7f0d24bc63e9de6a2a655809c3698ad9f9e2"}, - {file = "css_inline-0.8.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:773a150ba085d73ea8a4f27f562dcf90a7bc8dcecf0d1867b660f22d036c6a6a"}, - {file = "css_inline-0.8.7-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:39a90bd53272ee68a4d7909dc9b03240b296c50249b964cb253faf361c90e9dd"}, - {file = "css_inline-0.8.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ee0894e7a72434750799fced0c7404fed19f3d0538c7fb3ff61d4efacd473d"}, - {file = "css_inline-0.8.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5b08837acd1fe60a8f8cbd44dfce88dda1676aca47eeb51bf512c02e90803b77"}, - {file = "css_inline-0.8.7-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:6c0f02ddc5b694520d0fb8db7961e703120a373e516a74cfa6c8303b1c131e42"}, - {file = "css_inline-0.8.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:51cc996cfce5fd10aa5e41569c664cded198f30f4706e699e97942893aa9e7f9"}, - {file = "css_inline-0.8.7-cp37-abi3-win32.whl", hash = "sha256:c5910202e7583f0d1b6aea34d63cd378e28808f8bcf210a9c961cb26ccc5ed25"}, - {file = "css_inline-0.8.7-cp37-abi3-win_amd64.whl", hash = "sha256:6803c2fb2064330de1761a44ae86ab6c9b78d1761c36ebdd8919346e595d702d"}, - {file = "css_inline-0.8.7-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:36a392ed87f840621838e63937e151d236a62cfdba2760f503273330691ea3d8"}, - {file = "css_inline-0.8.7-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0757b71ae23d4131d7ca274bb35d41f5faa41b88eed9090df3ab409689d055d"}, - {file = "css_inline-0.8.7-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6579c9ea5e288e644bd78dbfd3d2bae9c33eb11e3f02d2c61cf4b5683c8f97da"}, - {file = "css_inline-0.8.7-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:819bf4c331e59da07d824529027c00ec618c2f0b1d498f43e5281988339ac082"}, - {file = "css_inline-0.8.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e87293717865bca00bbc2046864e33a421468129a8a8fe938abf2397c4a8e26"}, - {file = "css_inline-0.8.7-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea7d4313eed1de7def6d2e9c4780f11e1faed3800f2e2ad4de47dc15ba7a40da"}, - {file = "css_inline-0.8.7-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:7e5d5aebf87bcacc6bd3f548e0648f4e1b26d4b50e7239df234a42cb1374c039"}, - {file = "css_inline-0.8.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3b5dc09f6de78fa9f7152e701fdfa2ed827a7c0543a6746ba9879e8731c6da5"}, - {file = "css_inline-0.8.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bf6053352d053d32a2294510416c704e5b62325a8214dca5c764455e318948c"}, - {file = "css_inline-0.8.7.tar.gz", hash = "sha256:68dac7010c27624627f7df9be12888b9129fd658804f52f8feac25f7d4766050"}, -] - -[[package]] -name = "defusedcsv" -version = "2.0.0" -description = "Drop-in replacement for Python's CSV library that tries to mitigate CSV injection attacks" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "defusedcsv-2.0.0-py3-none-any.whl", hash = "sha256:a7bc3b1ac1ce4f8c6c1e8740466b1b5789b51ca18d918b0099313dc0cdf2cef4"}, - {file = "defusedcsv-2.0.0.tar.gz", hash = "sha256:7612228e54ef1690a19f7aef526709010608e987f9998c89588ef05d9ecfe4d6"}, -] - -[[package]] -name = "deprecated" -version = "1.2.13" -description = "Python @deprecated decorator to deprecate old python classes, functions or methods." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"}, - {file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"}, -] - -[package.dependencies] -wrapt = ">=1.10,<2" - -[package.extras] -dev = ["PyTest", "PyTest (<5)", "PyTest-Cov", "PyTest-Cov (<2.6)", "bump2version (<1)", "configparser (<5)", "importlib-metadata (<3)", "importlib-resources (<4)", "sphinx (<2)", "sphinxcontrib-websupport (<2)", "tox", "zipp (<2)"] - -[[package]] -name = "dj-static" -version = "0.0.6" -description = "Serve production static files with Django." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "dj-static-0.0.6.tar.gz", hash = "sha256:032ec1c532617922e6e3e956d504a6fb1acce4fc1c7c94612d0fda21828ce8ef"}, -] - -[package.dependencies] -static3 = "*" - -[[package]] -name = "django" -version = "3.2.18" -description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "Django-3.2.18-py3-none-any.whl", hash = "sha256:4d492d9024c7b3dfababf49f94511ab6a58e2c9c3c7207786f1ba4eb77750706"}, - {file = "Django-3.2.18.tar.gz", hash = "sha256:08208dfe892eb64fff073ca743b3b952311104f939e7f6dae954fe72dcc533ba"}, -] - -[package.dependencies] -asgiref = ">=3.3.2,<4" -pytz = "*" -sqlparse = ">=0.2.2" - -[package.extras] -argon2 = ["argon2-cffi (>=19.1.0)"] -bcrypt = ["bcrypt"] - -[[package]] -name = "django-appconf" -version = "1.0.5" -description = "A helper class for handling configuration defaults of packaged apps gracefully." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "django-appconf-1.0.5.tar.gz", hash = "sha256:be3db0be6c81fa84742000b89a81c016d70ae66a7ccb620cdef592b1f1a6aaa4"}, - {file = "django_appconf-1.0.5-py3-none-any.whl", hash = "sha256:ae9f864ee1958c815a965ed63b3fba4874eec13de10236ba063a788f9a17389d"}, -] - -[package.dependencies] -django = "*" - -[[package]] -name = "django-bootstrap3" -version = "22.2" -description = "Bootstrap 3 support for Django projects" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "django-bootstrap3-22.2.tar.gz", hash = "sha256:537b08748ab40a9f214968c188ae26cfeadb7987b784f2857396a33b477fa10a"}, - {file = "django_bootstrap3-22.2-py3-none-any.whl", hash = "sha256:c128452497500188052c0e0a24fe5639ee1a26170985b8da636d8fea9114430a"}, -] - -[package.dependencies] -Django = ">=3.2" - -[[package]] -name = "django-compressor" -version = "4.3.1" -description = "('Compresses linked and inline JavaScript or CSS into single cached files.',)" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django_compressor-4.3.1-py2.py3-none-any.whl", hash = "sha256:2c451174acb6f083054af7c8089376599b22d6380bd60311f78ec3fed79acc8e"}, - {file = "django_compressor-4.3.1.tar.gz", hash = "sha256:68858c0da6cc099cc29a022d86c3ba8aed114da9d709eeceb0d7b8181b5f8942"}, -] - -[package.dependencies] -django-appconf = ">=1.0.3" -rcssmin = "1.1.1" -rjsmin = "1.2.1" - -[[package]] -name = "django-countries" -version = "7.5.1" -description = "Provides a country field for Django models." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-countries-7.5.1.tar.gz", hash = "sha256:22915d9b9403932b731622619940a54894a3eb0da9a374e7249c8fc453c122d7"}, - {file = "django_countries-7.5.1-py3-none-any.whl", hash = "sha256:2df707aca7a5e677254bed116cf6021a136ebaccd5c2f46860abd6452bb45521"}, -] - -[package.dependencies] -asgiref = "*" -typing-extensions = "*" - -[package.extras] -dev = ["black", "django", "djangorestframework", "graphene-django", "pytest", "pytest-django", "tox"] -maintainer = ["django", "transifex-client", "zest.releaser[recommended]"] -pyuca = ["pyuca"] -test = ["djangorestframework", "graphene-django", "pytest", "pytest-cov", "pytest-django"] - -[[package]] -name = "django-filter" -version = "22.1" -description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "django-filter-22.1.tar.gz", hash = "sha256:ed473b76e84f7e83b2511bb2050c3efb36d135207d0128dfe3ae4b36e3594ba5"}, - {file = "django_filter-22.1-py3-none-any.whl", hash = "sha256:ed429e34760127e3520a67f415bec4c905d4649fbe45d0d6da37e6ff5e0287eb"}, -] - -[package.dependencies] -Django = ">=3.2" - -[[package]] -name = "django-formset-js-improved" -version = "0.5.0.3" -description = "Extend Django formsets with JavaScript" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-formset-js-improved-0.5.0.3.tar.gz", hash = "sha256:6c5a0ffec4fc25ad5f502b914fa6414fb0e43ec5f116a88a2da0090a72f32a4c"}, - {file = "django_formset_js_improved-0.5.0.3-py3-none-any.whl", hash = "sha256:99ff030c427fe412a8a54c6fadc3809f3d65683470b67f68d1d421ce7a09bb94"}, -] - -[package.dependencies] -Django = "*" -django-jquery-js = "*" - -[[package]] -name = "django-formtools" -version = "2.4" -description = "A set of high-level abstractions for Django forms" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "django-formtools-2.4.tar.gz", hash = "sha256:deb932be55b1d9419e37dc4d65dfbfeb8d307b71c8c11fd52f159aba5fc0deed"}, - {file = "django_formtools-2.4-py3-none-any.whl", hash = "sha256:f5f32f62ec8192cd1bc55bd929ca7dff5a5f2addf9027db95a5906ecfaa64836"}, -] - -[package.dependencies] -Django = ">=2.2" - -[[package]] -name = "django-hierarkey" -version = "1.1.0" -description = "Hierarchical key-value store for django" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-hierarkey-1.1.0.tar.gz", hash = "sha256:b0963b760b5bb4b97fe7abc6b7a0f3d2dd23e4741ac4585252af1d47f5ae771a"}, - {file = "django_hierarkey-1.1.0-py3-none-any.whl", hash = "sha256:2be56a6ae79bec1b356e2df22d2781fb6a48bd4bc55635a74a3e468fc0e2046f"}, -] - -[package.dependencies] -python-dateutil = "*" - -[[package]] -name = "django-hijack" -version = "3.2.7" -description = "django-hijack allows superusers to hijack (=login as) and work on behalf of another user." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-hijack-3.2.7.tar.gz", hash = "sha256:1293d21d14a46276738bb1a9ecc395e038cbd023289593f5b44fdddda2d50909"}, - {file = "django_hijack-3.2.7-py3-none-any.whl", hash = "sha256:d7c36a4d1be25340ad0fbb28303757ecde47a4b644456ff3a8163ea88c44aed9"}, -] - -[package.dependencies] -django = ">=2.2" - -[package.extras] -test = ["pytest", "pytest-cov", "pytest-django"] - -[[package]] -name = "django-i18nfield" -version = "1.9.4" -description = "Store internationalized strings in Django models" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-i18nfield-1.9.4.tar.gz", hash = "sha256:f24c209f4fcbf407ec1ebf749b6c182bf4089ef46c3cd0e60fa547955d575d2e"}, - {file = "django_i18nfield-1.9.4-py3-none-any.whl", hash = "sha256:d31bf725b43d6fa0d67a403efb45abf54fc6013ee9135ad55679626df9d0c8ca"}, -] - -[[package]] -name = "django-jquery-js" -version = "3.1.1" -description = "jQuery, bundled up so apps can depend upon it" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-jquery-js-3.1.1.tar.gz", hash = "sha256:308e6472801f89be7c02fa3d06bea6470cfbcab8287db80c64b1093717b8eea9"}, -] - -[package.dependencies] -Django = ">=1.4" - -[[package]] -name = "django-libsass" -version = "0.9" -description = "A django-compressor filter to compile SASS files using libsass" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-libsass-0.9.tar.gz", hash = "sha256:bfbbb55a8950bb40fa04dd416605f92da34ad1f303b10a41abc3232386ec27b5"}, - {file = "django_libsass-0.9-py3-none-any.whl", hash = "sha256:5234d29100889cac79e36a0f44207ec6d275adfd2da1acb6a94b55c89fe2bd97"}, -] - -[package.dependencies] -django-compressor = ">=1.3" -libsass = ">=0.7.0,<1" - -[[package]] -name = "django-localflavor" -version = "3.1" -description = "Country-specific Django helpers" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-localflavor-3.1.tar.gz", hash = "sha256:ac2fa377bbcba4cae95e97077d9e77c7f22b3d93e4845e2e133ba7e664043a44"}, - {file = "django_localflavor-3.1-py3-none-any.whl", hash = "sha256:6593865dc671333b3edc88e729e6d384d00b6db7891ef5d3a65db831a40050d2"}, -] - -[package.dependencies] -django = ">=2.2" -python-stdnum = ">=1.6" - -[[package]] -name = "django-markup" -version = "1.6" -description = "A generic Django application to convert text with specific markup to html." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-markup-1.6.tar.gz", hash = "sha256:757eae4841cf63ea390b265fca4acff14d54cd2e293784fa10c0a907e970d22e"}, - {file = "django_markup-1.6-py2.py3-none-any.whl", hash = "sha256:ab668fd3cc8f49024b9bc2bdc4a1ce9107c516f86ef0858708b2be86a58bb7e2"}, -] - -[package.dependencies] -django = ">=3.2" - -[package.extras] -all-filter-dependencies = ["bleach (>=3.0)", "docutils (>=0.14)", "markdown (>=2.6.9)", "pygments (>=2.2.0)", "python-creole (>=1.3.1)", "smartypants (>=2.0.0)", "textile (>=2.3.16)"] - -[[package]] -name = "django-mysql" -version = "4.9.0" -description = "Django-MySQL extends Django's built-in MySQL and MariaDB support their specific features not available on other databases." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "django_mysql-4.9.0-py3-none-any.whl", hash = "sha256:2d43172fd671825bce5fb5f928476d400900ea0f36ed26c24f7fe549ad7df61b"}, - {file = "django_mysql-4.9.0.tar.gz", hash = "sha256:6e86850b050e9350355d94f5272c2a010794a2e5f4819cf0128cdb438bb3f704"}, -] - -[package.dependencies] -Django = ">=3.2" - -[[package]] -name = "django-oauth-toolkit" -version = "2.2.0" -description = "OAuth2 Provider for Django" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-oauth-toolkit-2.2.0.tar.gz", hash = "sha256:46890decb24a34e2a5382debeaf7752e50d90b7a11716cf2a9fd067097ec0963"}, - {file = "django_oauth_toolkit-2.2.0-py3-none-any.whl", hash = "sha256:abd85c74af525a62365ec2049113e73a2ff8b46ef906e7104a7ba968ef02a11d"}, -] - -[package.dependencies] -django = ">=2.2,<4.0.0 || >4.0.0" -jwcrypto = ">=0.8.0" -oauthlib = ">=3.1.0" -requests = ">=2.13.0" - -[[package]] -name = "django-otp" -version = "1.1.6" -description = "A pluggable framework for adding two-factor authentication to Django using one-time passwords." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-otp-1.1.6.tar.gz", hash = "sha256:eac8e06041efe90e0687faea997fc0b3340f5bc74476f20aca0f6a96978f4253"}, - {file = "django_otp-1.1.6-py3-none-any.whl", hash = "sha256:8a431a934afdd4359ab838551d02d68d3cd90c974aa9e7337cbcbb6d45db621b"}, -] - -[package.dependencies] -django = ">=2.2" - -[package.extras] -qrcode = ["qrcode"] - -[[package]] -name = "django-phonenumber-field" -version = "7.0.2" -description = "An international phone number field for django models." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "django-phonenumber-field-7.0.2.tar.gz", hash = "sha256:de3e47b986b4959949762c16fd8fe26b3e462ef3e5531ed00950bd20c698576a"}, - {file = "django_phonenumber_field-7.0.2-py3-none-any.whl", hash = "sha256:9edad2b2602af25f2aefc73c4cf53eaf7abf9e17d73c1c4372bd3052bebb26f9"}, -] - -[package.dependencies] -Django = ">=3.2" - -[package.extras] -phonenumbers = ["phonenumbers (>=7.0.2)"] -phonenumberslite = ["phonenumberslite (>=7.0.2)"] - -[[package]] -name = "django-redis" -version = "5.2.0" -description = "Full featured redis cache backend for Django." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "django-redis-5.2.0.tar.gz", hash = "sha256:8a99e5582c79f894168f5865c52bd921213253b7fd64d16733ae4591564465de"}, - {file = "django_redis-5.2.0-py3-none-any.whl", hash = "sha256:1d037dc02b11ad7aa11f655d26dac3fb1af32630f61ef4428860a2e29ff92026"}, -] - -[package.dependencies] -Django = ">=2.2" -redis = ">=3,<4.0.0 || >4.0.0,<4.0.1 || >4.0.1" - -[package.extras] -hiredis = ["redis[hiredis] (>=3,!=4.0.0,!=4.0.1)"] - -[[package]] -name = "django-scopes" -version = "1.2.0.post1" -description = "Scope querys in multi-tenant django applications" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-scopes-1.2.0.post1.tar.gz", hash = "sha256:73b6d434f03b7cb8ea0bd7a39c730bab03f4729a300660817d89b4c3a8e1f4ad"}, - {file = "django_scopes-1.2.0.post1-py3-none-any.whl", hash = "sha256:60667d7d491c72b8ba912b6eaddeb0bc944fca5d19b5964389540bf71abe42c8"}, -] - -[package.dependencies] -Django = ">=3.2" - -[[package]] -name = "django-statici18n" -version = "2.3.1" -description = "A Django app that compiles i18n JavaScript catalogs to static files." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-statici18n-2.3.1.tar.gz", hash = "sha256:00079579035d5b45320830191e2c047f8643b7906307eff9833f0fa95068a603"}, - {file = "django_statici18n-2.3.1-py2.py3-none-any.whl", hash = "sha256:5f4bb3d58670def2df490babe338524927cfb2ebe2e5e20538b98d9424e83d0e"}, -] - -[package.dependencies] -Django = ">=2.2" -django-appconf = ">=1.0" - -[[package]] -name = "djangorestframework" -version = "3.14.0" -description = "Web APIs for Django, made easy." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "djangorestframework-3.14.0-py3-none-any.whl", hash = "sha256:eb63f58c9f218e1a7d064d17a70751f528ed4e1d35547fdade9aaf4cd103fd08"}, - {file = "djangorestframework-3.14.0.tar.gz", hash = "sha256:579a333e6256b09489cbe0a067e66abe55c6595d8926be6b99423786334350c8"}, -] - -[package.dependencies] -django = ">=3.0" -pytz = "*" - -[[package]] -name = "dnspython" -version = "2.2.1" -description = "DNS toolkit" -category = "main" -optional = false -python-versions = ">=3.6,<4.0" -files = [ - {file = "dnspython-2.2.1-py3-none-any.whl", hash = "sha256:a851e51367fb93e9e1361732c1d60dab63eff98712e503ea7d92e6eccb109b4f"}, - {file = "dnspython-2.2.1.tar.gz", hash = "sha256:0f7569a4a6ff151958b64304071d370daa3243d15941a7beedf0c9fe5105603e"}, -] - -[package.extras] -curio = ["curio (>=1.2,<2.0)", "sniffio (>=1.1,<2.0)"] -dnssec = ["cryptography (>=2.6,<37.0)"] -doh = ["h2 (>=4.1.0)", "httpx (>=0.21.1)", "requests (>=2.23.0,<3.0.0)", "requests-toolbelt (>=0.9.1,<0.10.0)"] -idna = ["idna (>=2.1,<4.0)"] -trio = ["trio (>=0.14,<0.20)"] -wmi = ["wmi (>=1.5.1,<2.0.0)"] - -[[package]] -name = "drf-ujson2" -version = "1.7.2" -description = "Django Rest Framework UJSON Renderer" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "drf_ujson2-1.7.2-py3-none-any.whl", hash = "sha256:5fead7ee1cccafd08137a845ddb1f153415519519ddfd9de5bcadfacb70ceb0b"}, - {file = "drf_ujson2-1.7.2.tar.gz", hash = "sha256:ae550e861280e7166232ccbfcb4e950059d88f7123abb02019bed48f8e2dbdbb"}, -] - -[package.dependencies] -django = "*" -djangorestframework = "*" -ujson = ">=2.0.1" - -[package.extras] -dev = ["pytest", "pytest-cov", "pytest-django", "pytest-mock", "pytest-runner"] - -[[package]] -name = "elementpath" -version = "4.0.1" -description = "XPath 1.0/2.0/3.0/3.1 parsers and selectors for ElementTree and lxml" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "elementpath-4.0.1-py3-none-any.whl", hash = "sha256:9eb7bf5e70bb3624b3f85c93a98b01b6fa9b01e0f2181f9cf9a2deede0f345e5"}, - {file = "elementpath-4.0.1.tar.gz", hash = "sha256:1162e4c8e5501bd36291b668f4449b8125fea5ef64a26da8d71da31126725aa5"}, -] - -[package.extras] -dev = ["Sphinx", "coverage", "flake8", "lxml", "lxml-stubs", "memory-profiler", "memray", "mypy", "tox", "xmlschema (>=2.0.0)"] - -[[package]] -name = "enum34" -version = "1.1.10" -description = "Python 3.4 Enum backported to 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, and 2.4" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "enum34-1.1.10-py2-none-any.whl", hash = "sha256:a98a201d6de3f2ab3db284e70a33b0f896fbf35f8086594e8c9e74b909058d53"}, - {file = "enum34-1.1.10-py3-none-any.whl", hash = "sha256:c3858660960c984d6ab0ebad691265180da2b43f07e061c0f8dca9ef3cffd328"}, - {file = "enum34-1.1.10.tar.gz", hash = "sha256:cce6a7477ed816bd2542d03d53db9f0db935dd013b70f336a95c73979289f248"}, -] - -[[package]] -name = "et-xmlfile" -version = "1.1.0" -description = "An implementation of lxml.xmlfile for the standard library" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada"}, - {file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"}, -] - -[[package]] -name = "future" -version = "0.18.3" -description = "Clean single-source support for Python 3 and 2" -category = "main" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "future-0.18.3.tar.gz", hash = "sha256:34a17436ed1e96697a86f9de3d15a3b0be01d8bc8de9c1dffd59fb8234ed5307"}, -] - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, -] - -[[package]] -name = "isodate" -version = "0.6.1" -description = "An ISO 8601 date/time/duration parser and formatter" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "isodate-0.6.1-py2.py3-none-any.whl", hash = "sha256:0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96"}, - {file = "isodate-0.6.1.tar.gz", hash = "sha256:48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9"}, -] - -[package.dependencies] -six = "*" - -[[package]] -name = "isoweek" -version = "1.3.3" -description = "Objects representing a week" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "isoweek-1.3.3-py2.py3-none-any.whl", hash = "sha256:d3324c497d97f1534669de225ec877964222e4cc773a4a99063086f7a4e342b6"}, - {file = "isoweek-1.3.3.tar.gz", hash = "sha256:73f3f7bac443e05a3ab45c32a72048b0c4f26d53d81462ec4b142c7581d3ffe8"}, -] - -[[package]] -name = "jsonschema" -version = "4.17.3" -description = "An implementation of JSON Schema validation for Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, - {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, -] - -[package.dependencies] -attrs = ">=17.4.0" -pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] - -[[package]] -name = "jwcrypto" -version = "1.4.2" -description = "Implementation of JOSE Web standards" -category = "main" -optional = false -python-versions = ">= 3.6" -files = [ - {file = "jwcrypto-1.4.2.tar.gz", hash = "sha256:80a35e9ed1b3b2c43ce03d92c5d48e6d0b6647e2aa2618e4963448923d78a37b"}, -] - -[package.dependencies] -cryptography = ">=2.3" -deprecated = "*" - -[[package]] -name = "kombu" -version = "5.2.4" -description = "Messaging library for Python." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "kombu-5.2.4-py3-none-any.whl", hash = "sha256:8b213b24293d3417bcf0d2f5537b7f756079e3ea232a8386dcc89a59fd2361a4"}, - {file = "kombu-5.2.4.tar.gz", hash = "sha256:37cee3ee725f94ea8bb173eaab7c1760203ea53bbebae226328600f9d2799610"}, -] - -[package.dependencies] -amqp = ">=5.0.9,<6.0.0" -vine = "*" - -[package.extras] -azureservicebus = ["azure-servicebus (>=7.0.0)"] -azurestoragequeues = ["azure-storage-queue"] -consul = ["python-consul (>=0.6.0)"] -librabbitmq = ["librabbitmq (>=2.0.0)"] -mongodb = ["pymongo (>=3.3.0,<3.12.1)"] -msgpack = ["msgpack"] -pyro = ["pyro4"] -qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"] -redis = ["redis (>=3.4.1,!=4.0.0,!=4.0.1)"] -slmq = ["softlayer-messaging (>=1.0.3)"] -sqlalchemy = ["sqlalchemy"] -sqs = ["boto3 (>=1.9.12)", "pycurl (>=7.44.1,<7.45.0)", "urllib3 (>=1.26.7)"] -yaml = ["PyYAML (>=3.10)"] -zookeeper = ["kazoo (>=1.3.1)"] - -[[package]] -name = "libsass" -version = "0.22.0" -description = "Sass for Python: A straightforward binding of libsass for Python." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "libsass-0.22.0-cp36-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f1efc1b612299c88aec9e39d6ca0c266d360daa5b19d9430bdeaffffa86993f9"}, - {file = "libsass-0.22.0-cp37-abi3-macosx_10_15_x86_64.whl", hash = "sha256:081e256ab3c5f3f09c7b8dea3bf3bf5e64a97c6995fd9eea880639b3f93a9f9a"}, - {file = "libsass-0.22.0-cp37-abi3-win32.whl", hash = "sha256:89c5ce497fcf3aba1dd1b19aae93b99f68257e5f2026b731b00a872f13324c7f"}, - {file = "libsass-0.22.0-cp37-abi3-win_amd64.whl", hash = "sha256:65455a2728b696b62100eb5932604aa13a29f4ac9a305d95773c14aaa7200aaf"}, - {file = "libsass-0.22.0.tar.gz", hash = "sha256:3ab5ad18e47db560f4f0c09e3d28cf3bb1a44711257488ac2adad69f4f7f8425"}, -] - -[[package]] -name = "lxml" -version = "4.9.2" -description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" -files = [ - {file = "lxml-4.9.2-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:76cf573e5a365e790396a5cc2b909812633409306c6531a6877c59061e42c4f2"}, - {file = "lxml-4.9.2-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b1f42b6921d0e81b1bcb5e395bc091a70f41c4d4e55ba99c6da2b31626c44892"}, - {file = "lxml-4.9.2-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9f102706d0ca011de571de32c3247c6476b55bb6bc65a20f682f000b07a4852a"}, - {file = "lxml-4.9.2-cp27-cp27m-win32.whl", hash = "sha256:8d0b4612b66ff5d62d03bcaa043bb018f74dfea51184e53f067e6fdcba4bd8de"}, - {file = "lxml-4.9.2-cp27-cp27m-win_amd64.whl", hash = "sha256:4c8f293f14abc8fd3e8e01c5bd86e6ed0b6ef71936ded5bf10fe7a5efefbaca3"}, - {file = "lxml-4.9.2-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2899456259589aa38bfb018c364d6ae7b53c5c22d8e27d0ec7609c2a1ff78b50"}, - {file = "lxml-4.9.2-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6749649eecd6a9871cae297bffa4ee76f90b4504a2a2ab528d9ebe912b101975"}, - {file = "lxml-4.9.2-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:a08cff61517ee26cb56f1e949cca38caabe9ea9fbb4b1e10a805dc39844b7d5c"}, - {file = "lxml-4.9.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:85cabf64adec449132e55616e7ca3e1000ab449d1d0f9d7f83146ed5bdcb6d8a"}, - {file = "lxml-4.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8340225bd5e7a701c0fa98284c849c9b9fc9238abf53a0ebd90900f25d39a4e4"}, - {file = "lxml-4.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:1ab8f1f932e8f82355e75dda5413a57612c6ea448069d4fb2e217e9a4bed13d4"}, - {file = "lxml-4.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:699a9af7dffaf67deeae27b2112aa06b41c370d5e7633e0ee0aea2e0b6c211f7"}, - {file = "lxml-4.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9cc34af337a97d470040f99ba4282f6e6bac88407d021688a5d585e44a23184"}, - {file = "lxml-4.9.2-cp310-cp310-win32.whl", hash = "sha256:d02a5399126a53492415d4906ab0ad0375a5456cc05c3fc0fc4ca11771745cda"}, - {file = "lxml-4.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:a38486985ca49cfa574a507e7a2215c0c780fd1778bb6290c21193b7211702ab"}, - {file = "lxml-4.9.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:c83203addf554215463b59f6399835201999b5e48019dc17f182ed5ad87205c9"}, - {file = "lxml-4.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:2a87fa548561d2f4643c99cd13131acb607ddabb70682dcf1dff5f71f781a4bf"}, - {file = "lxml-4.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:d6b430a9938a5a5d85fc107d852262ddcd48602c120e3dbb02137c83d212b380"}, - {file = "lxml-4.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3efea981d956a6f7173b4659849f55081867cf897e719f57383698af6f618a92"}, - {file = "lxml-4.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:df0623dcf9668ad0445e0558a21211d4e9a149ea8f5666917c8eeec515f0a6d1"}, - {file = "lxml-4.9.2-cp311-cp311-win32.whl", hash = "sha256:da248f93f0418a9e9d94b0080d7ebc407a9a5e6d0b57bb30db9b5cc28de1ad33"}, - {file = "lxml-4.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:3818b8e2c4b5148567e1b09ce739006acfaa44ce3156f8cbbc11062994b8e8dd"}, - {file = "lxml-4.9.2-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca989b91cf3a3ba28930a9fc1e9aeafc2a395448641df1f387a2d394638943b0"}, - {file = "lxml-4.9.2-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:822068f85e12a6e292803e112ab876bc03ed1f03dddb80154c395f891ca6b31e"}, - {file = "lxml-4.9.2-cp35-cp35m-win32.whl", hash = "sha256:be7292c55101e22f2a3d4d8913944cbea71eea90792bf914add27454a13905df"}, - {file = "lxml-4.9.2-cp35-cp35m-win_amd64.whl", hash = "sha256:998c7c41910666d2976928c38ea96a70d1aa43be6fe502f21a651e17483a43c5"}, - {file = "lxml-4.9.2-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:b26a29f0b7fc6f0897f043ca366142d2b609dc60756ee6e4e90b5f762c6adc53"}, - {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:ab323679b8b3030000f2be63e22cdeea5b47ee0abd2d6a1dc0c8103ddaa56cd7"}, - {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:689bb688a1db722485e4610a503e3e9210dcc20c520b45ac8f7533c837be76fe"}, - {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:f49e52d174375a7def9915c9f06ec4e569d235ad428f70751765f48d5926678c"}, - {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:36c3c175d34652a35475a73762b545f4527aec044910a651d2bf50de9c3352b1"}, - {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a35f8b7fa99f90dd2f5dc5a9fa12332642f087a7641289ca6c40d6e1a2637d8e"}, - {file = "lxml-4.9.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:58bfa3aa19ca4c0f28c5dde0ff56c520fbac6f0daf4fac66ed4c8d2fb7f22e74"}, - {file = "lxml-4.9.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc718cd47b765e790eecb74d044cc8d37d58562f6c314ee9484df26276d36a38"}, - {file = "lxml-4.9.2-cp36-cp36m-win32.whl", hash = "sha256:d5bf6545cd27aaa8a13033ce56354ed9e25ab0e4ac3b5392b763d8d04b08e0c5"}, - {file = "lxml-4.9.2-cp36-cp36m-win_amd64.whl", hash = "sha256:3ab9fa9d6dc2a7f29d7affdf3edebf6ece6fb28a6d80b14c3b2fb9d39b9322c3"}, - {file = "lxml-4.9.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:05ca3f6abf5cf78fe053da9b1166e062ade3fa5d4f92b4ed688127ea7d7b1d03"}, - {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:a5da296eb617d18e497bcf0a5c528f5d3b18dadb3619fbdadf4ed2356ef8d941"}, - {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:04876580c050a8c5341d706dd464ff04fd597095cc8c023252566a8826505726"}, - {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c9ec3eaf616d67db0764b3bb983962b4f385a1f08304fd30c7283954e6a7869b"}, - {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2a29ba94d065945944016b6b74e538bdb1751a1db6ffb80c9d3c2e40d6fa9894"}, - {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a82d05da00a58b8e4c0008edbc8a4b6ec5a4bc1e2ee0fb6ed157cf634ed7fa45"}, - {file = "lxml-4.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:223f4232855ade399bd409331e6ca70fb5578efef22cf4069a6090acc0f53c0e"}, - {file = "lxml-4.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d17bc7c2ccf49c478c5bdd447594e82692c74222698cfc9b5daae7ae7e90743b"}, - {file = "lxml-4.9.2-cp37-cp37m-win32.whl", hash = "sha256:b64d891da92e232c36976c80ed7ebb383e3f148489796d8d31a5b6a677825efe"}, - {file = "lxml-4.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a0a336d6d3e8b234a3aae3c674873d8f0e720b76bc1d9416866c41cd9500ffb9"}, - {file = "lxml-4.9.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:da4dd7c9c50c059aba52b3524f84d7de956f7fef88f0bafcf4ad7dde94a064e8"}, - {file = "lxml-4.9.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:821b7f59b99551c69c85a6039c65b75f5683bdc63270fec660f75da67469ca24"}, - {file = "lxml-4.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:e5168986b90a8d1f2f9dc1b841467c74221bd752537b99761a93d2d981e04889"}, - {file = "lxml-4.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:8e20cb5a47247e383cf4ff523205060991021233ebd6f924bca927fcf25cf86f"}, - {file = "lxml-4.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:13598ecfbd2e86ea7ae45ec28a2a54fb87ee9b9fdb0f6d343297d8e548392c03"}, - {file = "lxml-4.9.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:880bbbcbe2fca64e2f4d8e04db47bcdf504936fa2b33933efd945e1b429bea8c"}, - {file = "lxml-4.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7d2278d59425777cfcb19735018d897ca8303abe67cc735f9f97177ceff8027f"}, - {file = "lxml-4.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5344a43228767f53a9df6e5b253f8cdca7dfc7b7aeae52551958192f56d98457"}, - {file = "lxml-4.9.2-cp38-cp38-win32.whl", hash = "sha256:925073b2fe14ab9b87e73f9a5fde6ce6392da430f3004d8b72cc86f746f5163b"}, - {file = "lxml-4.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:9b22c5c66f67ae00c0199f6055705bc3eb3fcb08d03d2ec4059a2b1b25ed48d7"}, - {file = "lxml-4.9.2-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:5f50a1c177e2fa3ee0667a5ab79fdc6b23086bc8b589d90b93b4bd17eb0e64d1"}, - {file = "lxml-4.9.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:090c6543d3696cbe15b4ac6e175e576bcc3f1ccfbba970061b7300b0c15a2140"}, - {file = "lxml-4.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:63da2ccc0857c311d764e7d3d90f429c252e83b52d1f8f1d1fe55be26827d1f4"}, - {file = "lxml-4.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:5b4545b8a40478183ac06c073e81a5ce4cf01bf1734962577cf2bb569a5b3bbf"}, - {file = "lxml-4.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2e430cd2824f05f2d4f687701144556646bae8f249fd60aa1e4c768ba7018947"}, - {file = "lxml-4.9.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6804daeb7ef69e7b36f76caddb85cccd63d0c56dedb47555d2fc969e2af6a1a5"}, - {file = "lxml-4.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a6e441a86553c310258aca15d1c05903aaf4965b23f3bc2d55f200804e005ee5"}, - {file = "lxml-4.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ca34efc80a29351897e18888c71c6aca4a359247c87e0b1c7ada14f0ab0c0fb2"}, - {file = "lxml-4.9.2-cp39-cp39-win32.whl", hash = "sha256:6b418afe5df18233fc6b6093deb82a32895b6bb0b1155c2cdb05203f583053f1"}, - {file = "lxml-4.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:f1496ea22ca2c830cbcbd473de8f114a320da308438ae65abad6bab7867fe38f"}, - {file = "lxml-4.9.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:b264171e3143d842ded311b7dccd46ff9ef34247129ff5bf5066123c55c2431c"}, - {file = "lxml-4.9.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0dc313ef231edf866912e9d8f5a042ddab56c752619e92dfd3a2c277e6a7299a"}, - {file = "lxml-4.9.2-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:16efd54337136e8cd72fb9485c368d91d77a47ee2d42b057564aae201257d419"}, - {file = "lxml-4.9.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:0f2b1e0d79180f344ff9f321327b005ca043a50ece8713de61d1cb383fb8ac05"}, - {file = "lxml-4.9.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:7b770ed79542ed52c519119473898198761d78beb24b107acf3ad65deae61f1f"}, - {file = "lxml-4.9.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:efa29c2fe6b4fdd32e8ef81c1528506895eca86e1d8c4657fda04c9b3786ddf9"}, - {file = "lxml-4.9.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7e91ee82f4199af8c43d8158024cbdff3d931df350252288f0d4ce656df7f3b5"}, - {file = "lxml-4.9.2-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:b23e19989c355ca854276178a0463951a653309fb8e57ce674497f2d9f208746"}, - {file = "lxml-4.9.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:01d36c05f4afb8f7c20fd9ed5badca32a2029b93b1750f571ccc0b142531caf7"}, - {file = "lxml-4.9.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7b515674acfdcadb0eb5d00d8a709868173acece5cb0be3dd165950cbfdf5409"}, - {file = "lxml-4.9.2.tar.gz", hash = "sha256:2455cfaeb7ac70338b3257f41e21f0724f4b5b0c0e7702da67ee6c3640835b67"}, -] - -[package.extras] -cssselect = ["cssselect (>=0.7)"] -html5 = ["html5lib"] -htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=0.29.7)"] - -[[package]] -name = "markdown" -version = "3.3.4" -description = "Python implementation of Markdown." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"}, - {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"}, -] - -[package.extras] -testing = ["coverage", "pyyaml"] - -[[package]] -name = "mt-940" -version = "4.23.0" -description = "A library to parse MT940 files and returns smart Python collections for statistics and manipulation." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "mt-940-4.23.0.tar.gz", hash = "sha256:9274bc8298b2d4b69cb3936bdcda315b50e45975789f519a237bdec58346b8d7"}, - {file = "mt_940-4.23.0-py2.py3-none-any.whl", hash = "sha256:1f727184a12a8f289310620d9a7fbdda2737c396fb5abb356838a7d2823359b0"}, -] - -[package.extras] -docs = ["GitPython (>=2.1.9)", "sphinx (>=1.7.2)", "sphinx2rst"] -tests = ["flake8", "pytest", "pytest-cache", "pytest-cover", "pytest-flakes", "pytest-pep8", "pyyaml"] - -[[package]] -name = "oauthlib" -version = "3.2.2" -description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, - {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, -] - -[package.extras] -rsa = ["cryptography (>=3.0.0)"] -signals = ["blinker (>=1.4.0)"] -signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] - -[[package]] -name = "openpyxl" -version = "3.1.2" -description = "A Python library to read/write Excel 2010 xlsx/xlsm files" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "openpyxl-3.1.2-py2.py3-none-any.whl", hash = "sha256:f91456ead12ab3c6c2e9491cf33ba6d08357d802192379bb482f1033ade496f5"}, - {file = "openpyxl-3.1.2.tar.gz", hash = "sha256:a6f5977418eff3b2d5500d54d9db50c8277a368436f4e4f8ddb1be3422870184"}, -] - -[package.dependencies] -et-xmlfile = "*" - -[[package]] -name = "packaging" -version = "23.0" -description = "Core utilities for Python packages" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"}, - {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, -] - -[[package]] -name = "paypal-checkout-serversdk" -version = "1.0.1" -description = "Python Rest SDK for PayPal Checkout" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "paypal-checkout-serversdk-1.0.1.tar.gz", hash = "sha256:80f62ba2d9fe22b58c2ce1f310146acf6037088493398dba8b1bb67b493aee5e"}, - {file = "paypal_checkout_serversdk-1.0.1-py2-none-any.whl", hash = "sha256:e82bf50c249d7383cb4f68d8562a68dde3e7089389f286c111b6689bd3fdad36"}, -] - -[package.dependencies] -paypalhttp = "*" - -[[package]] -name = "paypalhttp" -version = "1.0.1" -description = "" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "paypalhttp-1.0.1-py3-none-any.whl", hash = "sha256:251a6e72e2c5140c5372ee6351b64f7af61b5aad9c554618db5782a06205989a"}, - {file = "paypalhttp-1.0.1.tar.gz", hash = "sha256:20e00f95ea052f59145b65bc2baf3b8720f449460c96bf7d32f191c8e293d16d"}, -] - -[package.dependencies] -pyopenssl = ">=0.15" -requests = ">=2.0.0" -six = ">=1.0.0" - -[[package]] -name = "paypalrestsdk" -version = "1.13.1" -description = "The PayPal REST SDK provides Python APIs to create, process and manage payments." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "paypalrestsdk-1.13.1.tar.gz", hash = "sha256:238713208031e8981bf70b3350b3d7f85ed64d34e0f21e4c1184444a546fee7f"}, -] - -[package.dependencies] -pyopenssl = ">=0.15" -requests = ">=1.0.0" -six = ">=1.0.0" - -[[package]] -name = "phonenumberslite" -version = "8.13.7" -description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "phonenumberslite-8.13.7-py2.py3-none-any.whl", hash = "sha256:12457107a037c8918c8a181a2bfaae563452b73c619fefb2fdd2b4c7a162200e"}, - {file = "phonenumberslite-8.13.7.tar.gz", hash = "sha256:286865b8e4761e0b9904b9b925f881111c52b0b75db10a504a3de0be698b7f5b"}, -] - -[[package]] -name = "pillow" -version = "9.4.0" -description = "Python Imaging Library (Fork)" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Pillow-9.4.0-1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b4b4e9dda4f4e4c4e6896f93e84a8f0bcca3b059de9ddf67dac3c334b1195e1"}, - {file = "Pillow-9.4.0-1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:fb5c1ad6bad98c57482236a21bf985ab0ef42bd51f7ad4e4538e89a997624e12"}, - {file = "Pillow-9.4.0-1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:f0caf4a5dcf610d96c3bd32932bfac8aee61c96e60481c2a0ea58da435e25acd"}, - {file = "Pillow-9.4.0-1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:3f4cc516e0b264c8d4ccd6b6cbc69a07c6d582d8337df79be1e15a5056b258c9"}, - {file = "Pillow-9.4.0-1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b8c2f6eb0df979ee99433d8b3f6d193d9590f735cf12274c108bd954e30ca858"}, - {file = "Pillow-9.4.0-1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b70756ec9417c34e097f987b4d8c510975216ad26ba6e57ccb53bc758f490dab"}, - {file = "Pillow-9.4.0-1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:43521ce2c4b865d385e78579a082b6ad1166ebed2b1a2293c3be1d68dd7ca3b9"}, - {file = "Pillow-9.4.0-2-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:9d9a62576b68cd90f7075876f4e8444487db5eeea0e4df3ba298ee38a8d067b0"}, - {file = "Pillow-9.4.0-2-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:87708d78a14d56a990fbf4f9cb350b7d89ee8988705e58e39bdf4d82c149210f"}, - {file = "Pillow-9.4.0-2-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8a2b5874d17e72dfb80d917213abd55d7e1ed2479f38f001f264f7ce7bae757c"}, - {file = "Pillow-9.4.0-2-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:83125753a60cfc8c412de5896d10a0a405e0bd88d0470ad82e0869ddf0cb3848"}, - {file = "Pillow-9.4.0-2-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:9e5f94742033898bfe84c93c831a6f552bb629448d4072dd312306bab3bd96f1"}, - {file = "Pillow-9.4.0-2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:013016af6b3a12a2f40b704677f8b51f72cb007dac785a9933d5c86a72a7fe33"}, - {file = "Pillow-9.4.0-2-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:99d92d148dd03fd19d16175b6d355cc1b01faf80dae93c6c3eb4163709edc0a9"}, - {file = "Pillow-9.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:2968c58feca624bb6c8502f9564dd187d0e1389964898f5e9e1fbc8533169157"}, - {file = "Pillow-9.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c5c1362c14aee73f50143d74389b2c158707b4abce2cb055b7ad37ce60738d47"}, - {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd752c5ff1b4a870b7661234694f24b1d2b9076b8bf337321a814c612665f343"}, - {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a3049a10261d7f2b6514d35bbb7a4dfc3ece4c4de14ef5876c4b7a23a0e566d"}, - {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16a8df99701f9095bea8a6c4b3197da105df6f74e6176c5b410bc2df2fd29a57"}, - {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:94cdff45173b1919350601f82d61365e792895e3c3a3443cf99819e6fbf717a5"}, - {file = "Pillow-9.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ed3e4b4e1e6de75fdc16d3259098de7c6571b1a6cc863b1a49e7d3d53e036070"}, - {file = "Pillow-9.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5b2f8a31bd43e0f18172d8ac82347c8f37ef3e0b414431157718aa234991b28"}, - {file = "Pillow-9.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:09b89ddc95c248ee788328528e6a2996e09eaccddeeb82a5356e92645733be35"}, - {file = "Pillow-9.4.0-cp310-cp310-win32.whl", hash = "sha256:f09598b416ba39a8f489c124447b007fe865f786a89dbfa48bb5cf395693132a"}, - {file = "Pillow-9.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6e78171be3fb7941f9910ea15b4b14ec27725865a73c15277bc39f5ca4f8391"}, - {file = "Pillow-9.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:3fa1284762aacca6dc97474ee9c16f83990b8eeb6697f2ba17140d54b453e133"}, - {file = "Pillow-9.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eaef5d2de3c7e9b21f1e762f289d17b726c2239a42b11e25446abf82b26ac132"}, - {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4dfdae195335abb4e89cc9762b2edc524f3c6e80d647a9a81bf81e17e3fb6f0"}, - {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6abfb51a82e919e3933eb137e17c4ae9c0475a25508ea88993bb59faf82f3b35"}, - {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:451f10ef963918e65b8869e17d67db5e2f4ab40e716ee6ce7129b0cde2876eab"}, - {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6663977496d616b618b6cfa43ec86e479ee62b942e1da76a2c3daa1c75933ef4"}, - {file = "Pillow-9.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:60e7da3a3ad1812c128750fc1bc14a7ceeb8d29f77e0a2356a8fb2aa8925287d"}, - {file = "Pillow-9.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:19005a8e58b7c1796bc0167862b1f54a64d3b44ee5d48152b06bb861458bc0f8"}, - {file = "Pillow-9.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f715c32e774a60a337b2bb8ad9839b4abf75b267a0f18806f6f4f5f1688c4b5a"}, - {file = "Pillow-9.4.0-cp311-cp311-win32.whl", hash = "sha256:b222090c455d6d1a64e6b7bb5f4035c4dff479e22455c9eaa1bdd4c75b52c80c"}, - {file = "Pillow-9.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:ba6612b6548220ff5e9df85261bddc811a057b0b465a1226b39bfb8550616aee"}, - {file = "Pillow-9.4.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:5f532a2ad4d174eb73494e7397988e22bf427f91acc8e6ebf5bb10597b49c493"}, - {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dd5a9c3091a0f414a963d427f920368e2b6a4c2f7527fdd82cde8ef0bc7a327"}, - {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef21af928e807f10bf4141cad4746eee692a0dd3ff56cfb25fce076ec3cc8abe"}, - {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:847b114580c5cc9ebaf216dd8c8dbc6b00a3b7ab0131e173d7120e6deade1f57"}, - {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:653d7fb2df65efefbcbf81ef5fe5e5be931f1ee4332c2893ca638c9b11a409c4"}, - {file = "Pillow-9.4.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:46f39cab8bbf4a384ba7cb0bc8bae7b7062b6a11cfac1ca4bc144dea90d4a9f5"}, - {file = "Pillow-9.4.0-cp37-cp37m-win32.whl", hash = "sha256:7ac7594397698f77bce84382929747130765f66406dc2cd8b4ab4da68ade4c6e"}, - {file = "Pillow-9.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:46c259e87199041583658457372a183636ae8cd56dbf3f0755e0f376a7f9d0e6"}, - {file = "Pillow-9.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:0e51f608da093e5d9038c592b5b575cadc12fd748af1479b5e858045fff955a9"}, - {file = "Pillow-9.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:765cb54c0b8724a7c12c55146ae4647e0274a839fb6de7bcba841e04298e1011"}, - {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:519e14e2c49fcf7616d6d2cfc5c70adae95682ae20f0395e9280db85e8d6c4df"}, - {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d197df5489004db87d90b918033edbeee0bd6df3848a204bca3ff0a903bef837"}, - {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0845adc64fe9886db00f5ab68c4a8cd933ab749a87747555cec1c95acea64b0b"}, - {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:e1339790c083c5a4de48f688b4841f18df839eb3c9584a770cbd818b33e26d5d"}, - {file = "Pillow-9.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:a96e6e23f2b79433390273eaf8cc94fec9c6370842e577ab10dabdcc7ea0a66b"}, - {file = "Pillow-9.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7cfc287da09f9d2a7ec146ee4d72d6ea1342e770d975e49a8621bf54eaa8f30f"}, - {file = "Pillow-9.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d7081c084ceb58278dd3cf81f836bc818978c0ccc770cbbb202125ddabec6628"}, - {file = "Pillow-9.4.0-cp38-cp38-win32.whl", hash = "sha256:df41112ccce5d47770a0c13651479fbcd8793f34232a2dd9faeccb75eb5d0d0d"}, - {file = "Pillow-9.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:7a21222644ab69ddd9967cfe6f2bb420b460dae4289c9d40ff9a4896e7c35c9a"}, - {file = "Pillow-9.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0f3269304c1a7ce82f1759c12ce731ef9b6e95b6df829dccd9fe42912cc48569"}, - {file = "Pillow-9.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cb362e3b0976dc994857391b776ddaa8c13c28a16f80ac6522c23d5257156bed"}, - {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2e0f87144fcbbe54297cae708c5e7f9da21a4646523456b00cc956bd4c65815"}, - {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:28676836c7796805914b76b1837a40f76827ee0d5398f72f7dcc634bae7c6264"}, - {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0884ba7b515163a1a05440a138adeb722b8a6ae2c2b33aea93ea3118dd3a899e"}, - {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:53dcb50fbdc3fb2c55431a9b30caeb2f7027fcd2aeb501459464f0214200a503"}, - {file = "Pillow-9.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:e8c5cf126889a4de385c02a2c3d3aba4b00f70234bfddae82a5eaa3ee6d5e3e6"}, - {file = "Pillow-9.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6c6b1389ed66cdd174d040105123a5a1bc91d0aa7059c7261d20e583b6d8cbd2"}, - {file = "Pillow-9.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0dd4c681b82214b36273c18ca7ee87065a50e013112eea7d78c7a1b89a739153"}, - {file = "Pillow-9.4.0-cp39-cp39-win32.whl", hash = "sha256:6d9dfb9959a3b0039ee06c1a1a90dc23bac3b430842dcb97908ddde05870601c"}, - {file = "Pillow-9.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:54614444887e0d3043557d9dbc697dbb16cfb5a35d672b7a0fcc1ed0cf1c600b"}, - {file = "Pillow-9.4.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b9b752ab91e78234941e44abdecc07f1f0d8f51fb62941d32995b8161f68cfe5"}, - {file = "Pillow-9.4.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3b56206244dc8711f7e8b7d6cad4663917cd5b2d950799425076681e8766286"}, - {file = "Pillow-9.4.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aabdab8ec1e7ca7f1434d042bf8b1e92056245fb179790dc97ed040361f16bfd"}, - {file = "Pillow-9.4.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:db74f5562c09953b2c5f8ec4b7dfd3f5421f31811e97d1dbc0a7c93d6e3a24df"}, - {file = "Pillow-9.4.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e9d7747847c53a16a729b6ee5e737cf170f7a16611c143d95aa60a109a59c336"}, - {file = "Pillow-9.4.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b52ff4f4e002f828ea6483faf4c4e8deea8d743cf801b74910243c58acc6eda3"}, - {file = "Pillow-9.4.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:575d8912dca808edd9acd6f7795199332696d3469665ef26163cd090fa1f8bfa"}, - {file = "Pillow-9.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c4ed2ff6760e98d262e0cc9c9a7f7b8a9f61aa4d47c58835cdaf7b0b8811bb"}, - {file = "Pillow-9.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e621b0246192d3b9cb1dc62c78cfa4c6f6d2ddc0ec207d43c0dedecb914f152a"}, - {file = "Pillow-9.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8f127e7b028900421cad64f51f75c051b628db17fb00e099eb148761eed598c9"}, - {file = "Pillow-9.4.0.tar.gz", hash = "sha256:a1c2d7780448eb93fbcc3789bf3916aa5720d942e37945f4056680317f1cd23e"}, -] - -[package.extras] -docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinxext-opengraph"] -tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] - -[[package]] -name = "platformdirs" -version = "3.1.1" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "platformdirs-3.1.1-py3-none-any.whl", hash = "sha256:e5986afb596e4bb5bde29a79ac9061aa955b94fca2399b7aaac4090860920dd8"}, - {file = "platformdirs-3.1.1.tar.gz", hash = "sha256:024996549ee88ec1a9aa99ff7f8fc819bb59e2c3477b410d90a16d32d6e707aa"}, -] - -[package.extras] -docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] - -[[package]] -name = "ply" -version = "3.11" -description = "Python Lex & Yacc" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce"}, - {file = "ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3"}, -] - -[[package]] -name = "pretix" -version = "4.17.1" -description = "Reinventing presales, one ticket at a time" -category = "main" -optional = false -python-versions = ">=3.9" -files = [ - {file = "pretix-4.17.1-py3-none-any.whl", hash = "sha256:0cdf386af30d1d9bcaf097a3245f6cc7212371abe0257854939dc7b37a7daeb5"}, - {file = "pretix-4.17.1.tar.gz", hash = "sha256:a10d0ca3cd6b9ad18b8fd502643bda3325d9d638949100b18858054a18d1ecb7"}, -] - -[package.dependencies] -arabic-reshaper = "3.0.0" -babel = "*" -BeautifulSoup4 = ">=4.11.0,<4.12.0" -bleach = ">=5.0.0,<5.1.0" -celery = ">=5.2.0,<5.3.0" -chardet = ">=5.1.0,<5.2.0" -cryptography = ">=3.4.2" -css-inline = ">=0.8.0,<0.9.0" -defusedcsv = ">=1.1.0" -dj-static = "*" -Django = ">=3.2.18,<3.3.0" -django-bootstrap3 = ">=22.2.0,<22.3.0" -django-compressor = ">=4.3.0,<4.4.0" -django-countries = ">=7.5.0,<7.6.0" -django-filter = "22.1" -django-formset-js-improved = "0.5.0.3" -django-formtools = "2.4" -django-hierarkey = ">=1.1.0,<1.2.0" -django-hijack = ">=3.2.0,<3.3.0" -django-i18nfield = ">=1.9.4,<1.10.0" -django-libsass = "0.9" -django-localflavor = "3.1" -django-markup = "*" -django-mysql = "*" -django-oauth-toolkit = ">=2.2.0,<2.3.0" -django-otp = ">=1.1.0,<1.2.0" -django-phonenumber-field = ">=7.0.0,<7.1.0" -django-redis = ">=5.2.0,<5.3.0" -django-scopes = ">=1.2.0,<1.3.0" -django-statici18n = ">=2.3.0,<2.4.0" -djangorestframework = ">=3.14.0,<3.15.0" -dnspython = ">=2.2.0,<2.3.0" -drf-ujson2 = ">=1.7.0,<1.8.0" -isoweek = "*" -jsonschema = "*" -kombu = ">=5.2.0,<5.3.0" -libsass = ">=0.22.0,<0.23.0" -lxml = "*" -markdown = "3.3.4" -mt-940 = ">=4.23.0,<4.24.0" -oauthlib = ">=3.2.0,<3.3.0" -openpyxl = ">=3.1.0,<3.2.0" -packaging = "*" -paypal-checkout-serversdk = ">=1.0.0,<1.1.0" -paypalrestsdk = ">=1.13.0,<1.14.0" -phonenumberslite = ">=8.13.0,<8.14.0" -Pillow = ">=9.4.0,<9.5.0" -protobuf = ">=4.22.0,<4.23.0" -psycopg2-binary = "*" -pycountry = "*" -pycparser = "2.21" -pycryptodome = ">=3.17.0,<3.18.0" -PyJWT = ">=2.6.0,<2.7.0" -pypdf = ">=3.4.0,<3.5.0" -PyPDF2 = ">=2.12.0,<2.13.0" -python-bidi = ">=0.4.0,<0.5.0" -python-dateutil = ">=2.8.0,<2.9.0" -python-u2flib-server = ">=4.0.0,<5.0.0" -pytz = "*" -pyuca = "*" -redis = ">=4.5.0,<4.6.0" -reportlab = ">=3.6.0,<3.7.0" -requests = ">=2.28.0,<2.29.0" -sentry-sdk = ">=1.15.0,<1.16.0" -sepaxml = ">=2.6.0,<2.7.0" -slimit = "*" -static3 = ">=0.7.0,<0.8.0" -stripe = ">=5.1.0,<5.2.0" -text-unidecode = ">=1.0.0,<2.0.0" -tlds = ">=2020041600" -tqdm = ">=4.0.0,<5.0.0" -vat-moss-forked = "2020.3.20.0.11.0" -vobject = ">=0.9.0,<0.10.0" -webauthn = ">=0.4.0,<0.5.0" -zeep = ">=4.2.0,<4.3.0" - -[package.extras] -dev = ["coverage", "coveralls", "django-debug-toolbar (>=3.8.0,<3.9.0)", "flake8 (>=6.0.0,<6.1.0)", "freezegun", "isort (>=5.12.0,<5.13.0)", "pep8-naming (>=0.12.0,<0.13.0)", "potypo", "pycodestyle (>=2.10.0,<2.11.0)", "pyflakes (>=3.0.0,<3.1.0)", "pytest (>=7.2.0,<7.3.0)", "pytest-cache", "pytest-cov", "pytest-django (>=4.0.0,<5.0.0)", "pytest-mock (>=3.10.0,<3.11.0)", "pytest-rerunfailures (>=11.0.0,<12.0.0)", "pytest-sugar", "pytest-xdist (>=3.2.0,<3.3.0)", "responses"] -memcached = ["pylibmc"] -mysql = ["mysqlclient"] - -[[package]] -name = "prompt-toolkit" -version = "3.0.38" -description = "Library for building powerful interactive command lines in Python" -category = "main" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "prompt_toolkit-3.0.38-py3-none-any.whl", hash = "sha256:45ea77a2f7c60418850331366c81cf6b5b9cf4c7fd34616f733c5427e6abbb1f"}, - {file = "prompt_toolkit-3.0.38.tar.gz", hash = "sha256:23ac5d50538a9a38c8bde05fecb47d0b403ecd0662857a86f886f798563d5b9b"}, -] - -[package.dependencies] -wcwidth = "*" - -[[package]] -name = "protobuf" -version = "4.22.1" -description = "" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "protobuf-4.22.1-cp310-abi3-win32.whl", hash = "sha256:85aa9acc5a777adc0c21b449dafbc40d9a0b6413ff3a4f77ef9df194be7f975b"}, - {file = "protobuf-4.22.1-cp310-abi3-win_amd64.whl", hash = "sha256:8bc971d76c03f1dd49f18115b002254f2ddb2d4b143c583bb860b796bb0d399e"}, - {file = "protobuf-4.22.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:5917412347e1da08ce2939eb5cd60650dfb1a9ab4606a415b9278a1041fb4d19"}, - {file = "protobuf-4.22.1-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:9e12e2810e7d297dbce3c129ae5e912ffd94240b050d33f9ecf023f35563b14f"}, - {file = "protobuf-4.22.1-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:953fc7904ef46900262a26374b28c2864610b60cdc8b272f864e22143f8373c4"}, - {file = "protobuf-4.22.1-cp37-cp37m-win32.whl", hash = "sha256:6e100f7bc787cd0a0ae58dbf0ab8bbf1ee7953f862b89148b6cf5436d5e9eaa1"}, - {file = "protobuf-4.22.1-cp37-cp37m-win_amd64.whl", hash = "sha256:87a6393fa634f294bf24d1cfe9fdd6bb605cbc247af81b9b10c4c0f12dfce4b3"}, - {file = "protobuf-4.22.1-cp38-cp38-win32.whl", hash = "sha256:e3fb58076bdb550e75db06ace2a8b3879d4c4f7ec9dd86e4254656118f4a78d7"}, - {file = "protobuf-4.22.1-cp38-cp38-win_amd64.whl", hash = "sha256:651113695bc2e5678b799ee5d906b5d3613f4ccfa61b12252cfceb6404558af0"}, - {file = "protobuf-4.22.1-cp39-cp39-win32.whl", hash = "sha256:67b7d19da0fda2733702c2299fd1ef6cb4b3d99f09263eacaf1aa151d9d05f02"}, - {file = "protobuf-4.22.1-cp39-cp39-win_amd64.whl", hash = "sha256:b8700792f88e59ccecfa246fa48f689d6eee6900eddd486cdae908ff706c482b"}, - {file = "protobuf-4.22.1-py3-none-any.whl", hash = "sha256:3e19dcf4adbf608924d3486ece469dd4f4f2cf7d2649900f0efcd1a84e8fd3ba"}, - {file = "protobuf-4.22.1.tar.gz", hash = "sha256:dce7a55d501c31ecf688adb2f6c3f763cf11bc0be815d1946a84d74772ab07a7"}, -] - -[[package]] -name = "psycopg2-binary" -version = "2.9.5" -description = "psycopg2 - Python-PostgreSQL Database Adapter" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "psycopg2-binary-2.9.5.tar.gz", hash = "sha256:33e632d0885b95a8b97165899006c40e9ecdc634a529dca7b991eb7de4ece41c"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-macosx_10_15_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:0775d6252ccb22b15da3b5d7adbbf8cfe284916b14b6dc0ff503a23edb01ee85"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ec46ed947801652c9643e0b1dc334cfb2781232e375ba97312c2fc256597632"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3520d7af1ebc838cc6084a3281145d5cd5bdd43fdef139e6db5af01b92596cb7"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cbc554ba47ecca8cd3396ddaca85e1ecfe3e48dd57dc5e415e59551affe568e"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:5d28ecdf191db558d0c07d0f16524ee9d67896edf2b7990eea800abeb23ebd61"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:b9c33d4aef08dfecbd1736ceab8b7b3c4358bf10a0121483e5cd60d3d308cc64"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:05b3d479425e047c848b9782cd7aac9c6727ce23181eb9647baf64ffdfc3da41"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1e491e6489a6cb1d079df8eaa15957c277fdedb102b6a68cfbf40c4994412fd0"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:9e32cedc389bcb76d9f24ea8a012b3cb8385ee362ea437e1d012ffaed106c17d"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:46850a640df62ae940e34a163f72e26aca1f88e2da79148e1862faaac985c302"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-win32.whl", hash = "sha256:3d790f84201c3698d1bfb404c917f36e40531577a6dda02e45ba29b64d539867"}, - {file = "psycopg2_binary-2.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:1764546ffeaed4f9428707be61d68972eb5ede81239b46a45843e0071104d0dd"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-macosx_10_9_universal2.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:426c2ae999135d64e6a18849a7d1ad0e1bd007277e4a8f4752eaa40a96b550ff"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cf1d44e710ca3a9ce952bda2855830fe9f9017ed6259e01fcd71ea6287565f5"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:024030b13bdcbd53d8a93891a2cf07719715724fc9fee40243f3bd78b4264b8f"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcda1c84a1c533c528356da5490d464a139b6e84eb77cc0b432e38c5c6dd7882"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-manylinux_2_24_aarch64.whl", hash = "sha256:2ef892cabdccefe577088a79580301f09f2a713eb239f4f9f62b2b29cafb0577"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-manylinux_2_24_ppc64le.whl", hash = "sha256:af0516e1711995cb08dc19bbd05bec7dbdebf4185f68870595156718d237df3e"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e72c91bda9880f097c8aa3601a2c0de6c708763ba8128006151f496ca9065935"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e67b3c26e9b6d37b370c83aa790bbc121775c57bfb096c2e77eacca25fd0233b"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:5fc447058d083b8c6ac076fc26b446d44f0145308465d745fba93a28c14c9e32"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d892bfa1d023c3781a3cab8dd5af76b626c483484d782e8bd047c180db590e4c"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-win32.whl", hash = "sha256:2abccab84d057723d2ca8f99ff7b619285d40da6814d50366f61f0fc385c3903"}, - {file = "psycopg2_binary-2.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:bef7e3f9dc6f0c13afdd671008534be5744e0e682fb851584c8c3a025ec09720"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:6e63814ec71db9bdb42905c925639f319c80e7909fb76c3b84edc79dadef8d60"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:212757ffcecb3e1a5338d4e6761bf9c04f750e7d027117e74aa3cd8a75bb6fbd"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f8a9bcab7b6db2e3dbf65b214dfc795b4c6b3bb3af922901b6a67f7cb47d5f8"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:56b2957a145f816726b109ee3d4e6822c23f919a7d91af5a94593723ed667835"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:f95b8aca2703d6a30249f83f4fe6a9abf2e627aa892a5caaab2267d56be7ab69"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:70831e03bd53702c941da1a1ad36c17d825a24fbb26857b40913d58df82ec18b"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:dbc332beaf8492b5731229a881807cd7b91b50dbbbaf7fe2faf46942eda64a24"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:2d964eb24c8b021623df1c93c626671420c6efadbdb8655cb2bd5e0c6fa422ba"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:95076399ec3b27a8f7fa1cc9a83417b1c920d55cf7a97f718a94efbb96c7f503"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-win32.whl", hash = "sha256:3fc33295cfccad697a97a76dec3f1e94ad848b7b163c3228c1636977966b51e2"}, - {file = "psycopg2_binary-2.9.5-cp36-cp36m-win_amd64.whl", hash = "sha256:02551647542f2bf89073d129c73c05a25c372fc0a49aa50e0de65c3c143d8bd0"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-macosx_10_15_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:63e318dbe52709ed10d516a356f22a635e07a2e34c68145484ed96a19b0c4c68"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7e518a0911c50f60313cb9e74a169a65b5d293770db4770ebf004245f24b5c5"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9d38a4656e4e715d637abdf7296e98d6267df0cc0a8e9a016f8ba07e4aa3eeb"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:68d81a2fe184030aa0c5c11e518292e15d342a667184d91e30644c9d533e53e1"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:7ee3095d02d6f38bd7d9a5358fcc9ea78fcdb7176921528dd709cc63f40184f5"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:46512486be6fbceef51d7660dec017394ba3e170299d1dc30928cbedebbf103a"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b911dfb727e247340d36ae20c4b9259e4a64013ab9888ccb3cbba69b77fd9636"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:422e3d43b47ac20141bc84b3d342eead8d8099a62881a501e97d15f6addabfe9"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c5682a45df7d9642eff590abc73157c887a68f016df0a8ad722dcc0f888f56d7"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-win32.whl", hash = "sha256:b8104f709590fff72af801e916817560dbe1698028cd0afe5a52d75ceb1fce5f"}, - {file = "psycopg2_binary-2.9.5-cp37-cp37m-win_amd64.whl", hash = "sha256:7b3751857da3e224f5629400736a7b11e940b5da5f95fa631d86219a1beaafec"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-macosx_10_15_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:043a9fd45a03858ff72364b4b75090679bd875ee44df9c0613dc862ca6b98460"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9ffdc51001136b699f9563b1c74cc1f8c07f66ef7219beb6417a4c8aaa896c28"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c15ba5982c177bc4b23a7940c7e4394197e2d6a424a2d282e7c236b66da6d896"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc85b3777068ed30aff8242be2813038a929f2084f69e43ef869daddae50f6ee"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:215d6bf7e66732a514f47614f828d8c0aaac9a648c46a831955cb103473c7147"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:7d07f552d1e412f4b4e64ce386d4c777a41da3b33f7098b6219012ba534fb2c2"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a0adef094c49f242122bb145c3c8af442070dc0e4312db17e49058c1702606d4"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:00475004e5ed3e3bf5e056d66e5dcdf41a0dc62efcd57997acd9135c40a08a50"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:7d88db096fa19d94f433420eaaf9f3c45382da2dd014b93e4bf3215639047c16"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:902844f9c4fb19b17dfa84d9e2ca053d4a4ba265723d62ea5c9c26b38e0aa1e6"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-win32.whl", hash = "sha256:4e7904d1920c0c89105c0517dc7e3f5c20fb4e56ba9cdef13048db76947f1d79"}, - {file = "psycopg2_binary-2.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:a36a0e791805aa136e9cbd0ffa040d09adec8610453ee8a753f23481a0057af5"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-macosx_10_15_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:25382c7d174c679ce6927c16b6fbb68b10e56ee44b1acb40671e02d29f2fce7c"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9c38d3869238e9d3409239bc05bc27d6b7c99c2a460ea337d2814b35fb4fea1b"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5c6527c8efa5226a9e787507652dd5ba97b62d29b53c371a85cd13f957fe4d42"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e59137cdb970249ae60be2a49774c6dfb015bd0403f05af1fe61862e9626642d"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:d4c7b3a31502184e856df1f7bbb2c3735a05a8ce0ade34c5277e1577738a5c91"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:b9a794cef1d9c1772b94a72eec6da144c18e18041d294a9ab47669bc77a80c1d"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5254cbd4f4855e11cebf678c1a848a3042d455a22a4ce61349c36aafd4c2267"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c5e65c6ac0ae4bf5bef1667029f81010b6017795dcb817ba5c7b8a8d61fab76f"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:74eddec4537ab1f701a1647214734bc52cee2794df748f6ae5908e00771f180a"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:01ad49d68dd8c5362e4bfb4158f2896dc6e0c02e87b8a3770fc003459f1a4425"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-win32.whl", hash = "sha256:937880290775033a743f4836aa253087b85e62784b63fd099ee725d567a48aa1"}, - {file = "psycopg2_binary-2.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:484405b883630f3e74ed32041a87456c5e0e63a8e3429aa93e8714c366d62bd1"}, -] - -[[package]] -name = "pycountry" -version = "22.3.5" -description = "ISO country, subdivision, language, currency and script definitions and their translations" -category = "main" -optional = false -python-versions = ">=3.6, <4" -files = [ - {file = "pycountry-22.3.5.tar.gz", hash = "sha256:b2163a246c585894d808f18783e19137cb70a0c18fb36748dc01fc6f109c1646"}, -] - -[package.dependencies] -setuptools = "*" - -[[package]] -name = "pycparser" -version = "2.21" -description = "C parser in Python" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, -] - -[[package]] -name = "pycryptodome" -version = "3.17" -description = "Cryptographic library for Python" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "pycryptodome-3.17-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:2c5631204ebcc7ae33d11c43037b2dafe25e2ab9c1de6448eb6502ac69c19a56"}, - {file = "pycryptodome-3.17-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:04779cc588ad8f13c80a060b0b1c9d1c203d051d8a43879117fe6b8aaf1cd3fa"}, - {file = "pycryptodome-3.17-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:f812d58c5af06d939b2baccdda614a3ffd80531a26e5faca2c9f8b1770b2b7af"}, - {file = "pycryptodome-3.17-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:9453b4e21e752df8737fdffac619e93c9f0ec55ead9a45df782055eb95ef37d9"}, - {file = "pycryptodome-3.17-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:121d61663267f73692e8bde5ec0d23c9146465a0d75cad75c34f75c752527b01"}, - {file = "pycryptodome-3.17-cp27-cp27m-win32.whl", hash = "sha256:ba2d4fcb844c6ba5df4bbfee9352ad5352c5ae939ac450e06cdceff653280450"}, - {file = "pycryptodome-3.17-cp27-cp27m-win_amd64.whl", hash = "sha256:87e2ca3aa557781447428c4b6c8c937f10ff215202ab40ece5c13a82555c10d6"}, - {file = "pycryptodome-3.17-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:f44c0d28716d950135ff21505f2c764498eda9d8806b7c78764165848aa419bc"}, - {file = "pycryptodome-3.17-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:5a790bc045003d89d42e3b9cb3cc938c8561a57a88aaa5691512e8540d1ae79c"}, - {file = "pycryptodome-3.17-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:d086d46774e27b280e4cece8ab3d87299cf0d39063f00f1e9290d096adc5662a"}, - {file = "pycryptodome-3.17-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:5587803d5b66dfd99e7caa31ed91fba0fdee3661c5d93684028ad6653fce725f"}, - {file = "pycryptodome-3.17-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:e7debd9c439e7b84f53be3cf4ba8b75b3d0b6e6015212355d6daf44ac672e210"}, - {file = "pycryptodome-3.17-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ca1ceb6303be1282148f04ac21cebeebdb4152590842159877778f9cf1634f09"}, - {file = "pycryptodome-3.17-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:dc22cc00f804485a3c2a7e2010d9f14a705555f67020eb083e833cabd5bd82e4"}, - {file = "pycryptodome-3.17-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80ea8333b6a5f2d9e856ff2293dba2e3e661197f90bf0f4d5a82a0a6bc83a626"}, - {file = "pycryptodome-3.17-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c133f6721fba313722a018392a91e3c69d3706ae723484841752559e71d69dc6"}, - {file = "pycryptodome-3.17-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:333306eaea01fde50a73c4619e25631e56c4c61bd0fb0a2346479e67e3d3a820"}, - {file = "pycryptodome-3.17-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:1a30f51b990994491cec2d7d237924e5b6bd0d445da9337d77de384ad7f254f9"}, - {file = "pycryptodome-3.17-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:909e36a43fe4a8a3163e9c7fc103867825d14a2ecb852a63d3905250b308a4e5"}, - {file = "pycryptodome-3.17-cp35-abi3-win32.whl", hash = "sha256:a3228728a3808bc9f18c1797ec1179a0efb5068c817b2ffcf6bcd012494dffb2"}, - {file = "pycryptodome-3.17-cp35-abi3-win_amd64.whl", hash = "sha256:9ec565e89a6b400eca814f28d78a9ef3f15aea1df74d95b28b7720739b28f37f"}, - {file = "pycryptodome-3.17-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:e1819b67bcf6ca48341e9b03c2e45b1c891fa8eb1a8458482d14c2805c9616f2"}, - {file = "pycryptodome-3.17-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:f8e550caf52472ae9126953415e4fc554ab53049a5691c45b8816895c632e4d7"}, - {file = "pycryptodome-3.17-pp27-pypy_73-win32.whl", hash = "sha256:afbcdb0eda20a0e1d44e3a1ad6d4ec3c959210f4b48cabc0e387a282f4c7deb8"}, - {file = "pycryptodome-3.17-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a74f45aee8c5cc4d533e585e0e596e9f78521e1543a302870a27b0ae2106381e"}, - {file = "pycryptodome-3.17-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38bbd6717eac084408b4094174c0805bdbaba1f57fc250fd0309ae5ec9ed7e09"}, - {file = "pycryptodome-3.17-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f68d6c8ea2974a571cacb7014dbaada21063a0375318d88ac1f9300bc81e93c3"}, - {file = "pycryptodome-3.17-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8198f2b04c39d817b206ebe0db25a6653bb5f463c2319d6f6d9a80d012ac1e37"}, - {file = "pycryptodome-3.17-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3a232474cd89d3f51e4295abe248a8b95d0332d153bf46444e415409070aae1e"}, - {file = "pycryptodome-3.17-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4992ec965606054e8326e83db1c8654f0549cdb26fce1898dc1a20bc7684ec1c"}, - {file = "pycryptodome-3.17-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53068e33c74f3b93a8158dacaa5d0f82d254a81b1002e0cd342be89fcb3433eb"}, - {file = "pycryptodome-3.17-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:74794a2e2896cd0cf56fdc9db61ef755fa812b4a4900fa46c49045663a92b8d0"}, - {file = "pycryptodome-3.17.tar.gz", hash = "sha256:bce2e2d8e82fcf972005652371a3e8731956a0c1fbb719cc897943b3695ad91b"}, -] - -[[package]] -name = "pyjwt" -version = "2.6.0" -description = "JSON Web Token implementation in Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "PyJWT-2.6.0-py3-none-any.whl", hash = "sha256:d83c3d892a77bbb74d3e1a2cfa90afaadb60945205d1095d9221f04466f64c14"}, - {file = "PyJWT-2.6.0.tar.gz", hash = "sha256:69285c7e31fc44f68a1feb309e948e0df53259d579295e6cfe2b1792329f05fd"}, -] - -[package.extras] -crypto = ["cryptography (>=3.4.0)"] -dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] -docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] -tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] - -[[package]] -name = "pyopenssl" -version = "23.0.0" -description = "Python wrapper module around the OpenSSL library" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pyOpenSSL-23.0.0-py3-none-any.whl", hash = "sha256:df5fc28af899e74e19fccb5510df423581047e10ab6f1f4ba1763ff5fde844c0"}, - {file = "pyOpenSSL-23.0.0.tar.gz", hash = "sha256:c1cc5f86bcacefc84dada7d31175cae1b1518d5f60d3d0bb595a67822a868a6f"}, -] - -[package.dependencies] -cryptography = ">=38.0.0,<40" - -[package.extras] -docs = ["sphinx (!=5.2.0,!=5.2.0.post0)", "sphinx-rtd-theme"] -test = ["flaky", "pretend", "pytest (>=3.0.1)"] - -[[package]] -name = "pypdf" -version = "3.4.1" -description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pypdf-3.4.1-py3-none-any.whl", hash = "sha256:5e84dc66d17f3e74fd6c84ab1e0b0cc6e21aae79e391eac88853cdac47847813"}, - {file = "pypdf-3.4.1.tar.gz", hash = "sha256:8b0badc787062e8fb0d8393eb2a76903717fb24e5cc84e2b03cde5ac3881700f"}, -] - -[package.extras] -crypto = ["PyCryptodome"] -dev = ["black", "flit", "pip-tools", "pre-commit (<2.18.0)", "pytest-cov", "wheel"] -docs = ["myst_parser", "sphinx", "sphinx_rtd_theme"] -full = ["Pillow", "PyCryptodome"] -image = ["Pillow"] - -[[package]] -name = "pypdf2" -version = "2.12.1" -description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyPDF2-2.12.1.tar.gz", hash = "sha256:e03ef18abcc75da741a0acc1a7749253496887be38cd9887bcce1cee393da45e"}, - {file = "pypdf2-2.12.1-py3-none-any.whl", hash = "sha256:41ff16ee122bad9790d57a4235281a838002d7f1cc8d631d91b6f65d709bd825"}, -] - -[package.extras] -crypto = ["PyCryptodome"] -dev = ["black", "flit", "pip-tools", "pre-commit (<2.18.0)", "pytest-cov", "wheel"] -docs = ["myst_parser", "sphinx", "sphinx_rtd_theme"] -full = ["Pillow", "PyCryptodome"] -image = ["Pillow"] - -[[package]] -name = "pyrsistent" -version = "0.19.3" -description = "Persistent/Functional/Immutable data structures" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pyrsistent-0.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a"}, - {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64"}, - {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf"}, - {file = "pyrsistent-0.19.3-cp310-cp310-win32.whl", hash = "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a"}, - {file = "pyrsistent-0.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da"}, - {file = "pyrsistent-0.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9"}, - {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393"}, - {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19"}, - {file = "pyrsistent-0.19.3-cp311-cp311-win32.whl", hash = "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3"}, - {file = "pyrsistent-0.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-win32.whl", hash = "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b"}, - {file = "pyrsistent-0.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8"}, - {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a"}, - {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c"}, - {file = "pyrsistent-0.19.3-cp38-cp38-win32.whl", hash = "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c"}, - {file = "pyrsistent-0.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7"}, - {file = "pyrsistent-0.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc"}, - {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2"}, - {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3"}, - {file = "pyrsistent-0.19.3-cp39-cp39-win32.whl", hash = "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2"}, - {file = "pyrsistent-0.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98"}, - {file = "pyrsistent-0.19.3-py3-none-any.whl", hash = "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64"}, - {file = "pyrsistent-0.19.3.tar.gz", hash = "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440"}, -] - -[[package]] -name = "python-bidi" -version = "0.4.2" -description = "Pure python implementation of the BiDi layout algorithm" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "python-bidi-0.4.2.tar.gz", hash = "sha256:5347f71e82b3e9976dc657f09ded2bfe39ba8d6777ca81a5b2c56c30121c496e"}, - {file = "python_bidi-0.4.2-py2.py3-none-any.whl", hash = "sha256:50eef6f6a0bbdd685f9e8c207f3c9050f5b578d0a46e37c76a9c4baea2cc2e13"}, -] - -[package.dependencies] -six = "*" - -[[package]] -name = "python-dateutil" -version = "2.8.2" -description = "Extensions to the standard Python datetime module" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -files = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "python-stdnum" -version = "1.18" -description = "Python module to handle standardized numbers and codes" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "python-stdnum-1.18.tar.gz", hash = "sha256:bcc763d9c49ae23da5d2b7a686d5fd1deec9d9051341160a10d1ac723a26bec0"}, - {file = "python_stdnum-1.18-py2.py3-none-any.whl", hash = "sha256:d7f2a3c7ef4635c957b9cbdd9b1993d1f6ee3a2959f03e172c45440d99f296eb"}, -] - -[package.extras] -soap = ["zeep"] -soap-alt = ["suds"] -soap-fallback = ["PySimpleSOAP"] - -[[package]] -name = "python-u2flib-server" -version = "4.0.1" -description = "Python based U2F server library" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "python-u2flib-server-4.0.1.tar.gz", hash = "sha256:160425fe00407b06ce261a7d3c455a6a529ed73f71cfea1b436b573e1dff000b"}, -] - -[package.dependencies] -cryptography = ">=1.2" -enum34 = "*" - -[package.extras] -u2f-server = ["WebOb", "argparse"] -yubiauth-server = ["WebOb", "yubiauth"] - -[[package]] -name = "pytz" -version = "2022.7.1" -description = "World timezone definitions, modern and historical" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "pytz-2022.7.1-py2.py3-none-any.whl", hash = "sha256:78f4f37d8198e0627c5f1143240bb0206b8691d8d7ac6d78fee88b78733f8c4a"}, - {file = "pytz-2022.7.1.tar.gz", hash = "sha256:01a0681c4b9684a28304615eba55d1ab31ae00bf68ec157ec3708a8182dbbcd0"}, -] - -[[package]] -name = "pyuca" -version = "1.2" -description = "a Python implementation of the Unicode Collation Algorithm" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "pyuca-1.2-py2.py3-none-any.whl", hash = "sha256:abaa12e1bd2c7c68ca8396ff8383bc0654a739cef3ae68fd7af58bf29af0a91e"}, - {file = "pyuca-1.2.tar.gz", hash = "sha256:8a382fe74627f08c0d18908c0713ca4a20aad5385f077579e56208beea2893b2"}, -] - -[[package]] -name = "rcssmin" -version = "1.1.1" -description = "CSS Minifier" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "rcssmin-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:d4e263fa9428704fd94c2cb565c7519ca1d225217943f71caffe6741ab5b9df1"}, - {file = "rcssmin-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c7278c1c25bb90d8e554df92cfb3b6a1195004ead50f764653d3093933ee0877"}, - {file = "rcssmin-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f15673e97f0a68b4c378c4d15b088fe96d60bc106d278c88829923118833c20f"}, - {file = "rcssmin-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d0afc6e7b64ef30d6dcde88830ec1a237b9f16a39f920a8fd159928684ccf8db"}, - {file = "rcssmin-1.1.1-cp310-cp310-manylinux1_i686.whl", hash = "sha256:705c9112d0ed54ea40aecf97e7fd29bdf0f1c46d278a32d8f957f31dde90778a"}, - {file = "rcssmin-1.1.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:f7a1fcdbafaacac0530da04edca4a44303baab430ea42e7d59aece4b3f3e9a51"}, - {file = "rcssmin-1.1.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:cf74d7ea5e191f0f344b354eed8b7c83eeafbd9a97bec3a579c3d26edf11b005"}, - {file = "rcssmin-1.1.1-cp311-cp311-manylinux1_i686.whl", hash = "sha256:908fe072efd2432fb0975a61124609a8e05021367f6a3463d45f5e3e74c4fdda"}, - {file = "rcssmin-1.1.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:35da6a6999e9e2c5b0e691b42ed56cc479373e0ecab33ef5277dfecce625e44a"}, - {file = "rcssmin-1.1.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:e923c105100ab70abde1c01d3196ddd6b07255e32073685542be4e3a60870c8e"}, - {file = "rcssmin-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:868215e1fd0e92a6122e0ed5973dfc7bb8330fe1e92274d05b2585253b38c0ca"}, - {file = "rcssmin-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c7728e3b546b1b6ea08cab721e8e21409dbcc11b881d0b87d10b0be8930af2a2"}, - {file = "rcssmin-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:271e3d2f8614a6d4637ed8fff3d90007f03e2a654cd9444f37d888797662ba72"}, - {file = "rcssmin-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:42576d95dfad53d77df2e68dfdec95b89b10fad320f241f1af3ca1438578254a"}, - {file = "rcssmin-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:79421230dd67c37ec61ed9892813d2b839b68f2f48ef55c75f976e81701d60b4"}, - {file = "rcssmin-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:8fcfd10ae2a1c4ce231a33013f2539e07c3836bf17cc945cc25cc30bf8e68e45"}, - {file = "rcssmin-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c30f8bc839747b6da59274e0c6e4361915d66532e26448d589cb2b1846d7bf11"}, - {file = "rcssmin-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ee386bec6d62f8c814d65c011d604a7c82d24aa3f718facd66e850eea8d6a5a1"}, - {file = "rcssmin-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8a26fec3c1e6b7a3765ccbaccc20fbb5c0ed3422cc381e01a2607f08d7621c44"}, - {file = "rcssmin-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:a04d58a2a21e9a089306d3f99c4b12bf5b656a79c198ef2321e80f8fd9afab06"}, - {file = "rcssmin-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:914e589f40573035006913861ed2adc28fbe70082a8b6bff5be7ee430b7b5c2e"}, - {file = "rcssmin-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a417735d4023d47d048a6288c88dbceadd20abaaf65a11bb4fda1e8458057019"}, - {file = "rcssmin-1.1.1.tar.gz", hash = "sha256:4f9400b4366d29f5f5446f58e78549afa8338e6a59740c73115e9f6ac413dc64"}, -] - -[[package]] -name = "redis" -version = "4.5.1" -description = "Python client for Redis database and key-value store" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "redis-4.5.1-py3-none-any.whl", hash = "sha256:5deb072d26e67d2be1712603bfb7947ec3431fb0eec9c578994052e33035af6d"}, - {file = "redis-4.5.1.tar.gz", hash = "sha256:1eec3741cda408d3a5f84b78d089c8b8d895f21b3b050988351e925faf202864"}, -] - -[package.dependencies] -async-timeout = ">=4.0.2" - -[package.extras] -hiredis = ["hiredis (>=1.0.0)"] -ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] - -[[package]] -name = "reportlab" -version = "3.6.12" -description = "The Reportlab Toolkit" -category = "main" -optional = false -python-versions = ">=3.7,<4" -files = [ - {file = "reportlab-3.6.12-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dfcf7bd6db5d80711cbbd0996b6e7a79cc414ca81457960367df11d2860f92a"}, - {file = "reportlab-3.6.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a0bc7a1d64fe754b62e175ba0cf47a630b529c0488ec9ac4e4c7655e295ea4d"}, - {file = "reportlab-3.6.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:adf78ccb2defad5b6ecb2e2e9f2a672719b0a8e2278592a7d77f6c220a042388"}, - {file = "reportlab-3.6.12-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c84afd5bef6e407c80ba9f99b6abbe3ea78e8243b0f19897a871a7bcad1f749d"}, - {file = "reportlab-3.6.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4fa3cdf490f3828b055381e8c7dc7819b3e5f7a442d7af7a8f90e9806a7fff51"}, - {file = "reportlab-3.6.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:07fdd968df7941c2bfb67b9bb4532f424992dfafc71b72a4e4b291ff707e6b0e"}, - {file = "reportlab-3.6.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce85a204f46c871c8af6fa64b9bbed165456935c1d0bfb2f570a3194f6723ddb"}, - {file = "reportlab-3.6.12-cp310-cp310-win32.whl", hash = "sha256:090ea99ff829d918f7b6140594373b1340a34e1e6876eddae5aa06662ec10d64"}, - {file = "reportlab-3.6.12-cp310-cp310-win_amd64.whl", hash = "sha256:4c599645af9b5b2241a23e977a82c965a59c24cd94b2600b8d34373c66cad763"}, - {file = "reportlab-3.6.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:236a6483210049205f6180d7a7595d0ca2e4ce343d83cc94ca719a4145809c6f"}, - {file = "reportlab-3.6.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:69f41295d696c822224334f0994f1f107df7efed72211d45a1118696f1427c84"}, - {file = "reportlab-3.6.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f51dcb39e910a853749250c0f82aced80bca3f7315e9c4ee14349eb7cab6a3f8"}, - {file = "reportlab-3.6.12-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8dddc52e0e486291be0ad39184da0607fae9cc665fdba1881211de9cfc0b332"}, - {file = "reportlab-3.6.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4863c49602722237e35cbce5aa91af4539cc63a671f59504d2b3f3767d898cf"}, - {file = "reportlab-3.6.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8b1215facead57cc5325aef4229ef886e85d270b2ba02080fb5809ce9d2b81b4"}, - {file = "reportlab-3.6.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12049314497d872f6788f811e2b331654db207937f8a2fb34ff3e3cd9897faa"}, - {file = "reportlab-3.6.12-cp311-cp311-win32.whl", hash = "sha256:759495c2b8c15cb0d6b539c246896029e4cde42a896c3956f77e311c5f6b0807"}, - {file = "reportlab-3.6.12-cp311-cp311-win_amd64.whl", hash = "sha256:666bdba4958b348460a765c48b8c0640e7085540846ed9494f47d8651604b33c"}, - {file = "reportlab-3.6.12-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7a7c3369fa618eca79f9554ce06c618a5e738e592d61d96aa09b2457ca3ea410"}, - {file = "reportlab-3.6.12-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c9b0861d8f40d7a24b094b8834f6a489b9e8c70bceaa7fa98237eed229671ce"}, - {file = "reportlab-3.6.12-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26c25ea4afa8b92a2c14f4edc41c8fc30505745ce84cae86538e80cacadd7ae2"}, - {file = "reportlab-3.6.12-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55a070206580e161b6bbe1a96abf816c18d4c2c225d49916654714c93d842835"}, - {file = "reportlab-3.6.12-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c40e108072379ff83dd7442159ebc249d12eb8eec15b70614953fecd2c403792"}, - {file = "reportlab-3.6.12-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39e92fa4ab2a8f0f2cc051d9c1e3acb881340c07ef59c0c8b627861343d653c0"}, - {file = "reportlab-3.6.12-cp37-cp37m-win32.whl", hash = "sha256:3fd1ffdd5204301eb4c290a5752ac62f44d2d0b262e02e35a1e5234c13e14662"}, - {file = "reportlab-3.6.12-cp37-cp37m-win_amd64.whl", hash = "sha256:d4cecfb48a6cfbfe2caf0fc280cecea999699e63bc98cb02254bd87b39eff677"}, - {file = "reportlab-3.6.12-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b6a1b685da0b9a8000bb980e02d9d5be202d0cc539af113b661c76c051fca6f1"}, - {file = "reportlab-3.6.12-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f5808e1dac6b66c109d6205ce2aebf84bb89e1a1493b7e6df38932df5ebfb9cf"}, - {file = "reportlab-3.6.12-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb83df8f7840321d34cb5b24c972c617a8c1716c8a36e5050fff56adf5891b8c"}, - {file = "reportlab-3.6.12-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72ec333f089b4fce5a6d740ed0a1963a3994146be195722da0d8e14d4a7e1600"}, - {file = "reportlab-3.6.12-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71cf73f9907c444ef663ea653dbac24af07c307079572c3ff8f20ad1463af3b7"}, - {file = "reportlab-3.6.12-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cee3b6ebef5e4a8654ec5f0effeb1a2bb157ad87b0ac856871d25a805c0f2f90"}, - {file = "reportlab-3.6.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db62bed0774778fdf82c609cb9efd0062f2fdcd285be527d01f6be9fd9755888"}, - {file = "reportlab-3.6.12-cp38-cp38-win32.whl", hash = "sha256:b777ddc57b2d3366cbc540616034cdc1089ca0a31fefc907028e1dd62a6bf16c"}, - {file = "reportlab-3.6.12-cp38-cp38-win_amd64.whl", hash = "sha256:c07ec796a2a5d44bf787f2b623b6e668a389b0cafb78af34cf74554ff3bc532b"}, - {file = "reportlab-3.6.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cdd206883e999278d2af656f988dfcc89eb0c175ce6d75e87b713cf1e792c0c4"}, - {file = "reportlab-3.6.12-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3a62e51a4a47616896bd0f1e9cc3fbfb174b713794a5031a34b84f69dbe01775"}, - {file = "reportlab-3.6.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1dd0307b2b13b0482ac8314fd793fbbce263a428b189371addf0466784e1d597"}, - {file = "reportlab-3.6.12-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c56d701f7dc662e1d3d7fe364e66fa1339eafce54a488c2d16ec0ea49dc213c2"}, - {file = "reportlab-3.6.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:109009b02fc225882ea766a5ed8be0ef473fa1356e252a3f651a6aa89b4a195f"}, - {file = "reportlab-3.6.12-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b3648f3c340b6b6aabf9352341478c708cee6f00c5cd5c902311fcf4ce870f3c"}, - {file = "reportlab-3.6.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:907f7cd4832bb295d0c1573de15cc5aab5988282caf2ee7a2b1276fb6cdf502b"}, - {file = "reportlab-3.6.12-cp39-cp39-win32.whl", hash = "sha256:93e229519d046491b798f2c12dbbf2f3e237e89589aa5cbb5e1d8c1a978816db"}, - {file = "reportlab-3.6.12-cp39-cp39-win_amd64.whl", hash = "sha256:498b4ec7e73426de64c6bf6ec03c5b3f10dedf5db8a9e13fdf195f95a3d065aa"}, - {file = "reportlab-3.6.12.tar.gz", hash = "sha256:b13cebf4e397bba14542bcd023338b6ff2c151a3a12aabca89eecbf972cb361a"}, -] - -[package.dependencies] -pillow = ">=9.0.0" - -[package.extras] -fttextpath = ["freetype-py (>=2.3.0,<2.4)"] -rlpycairo = ["rlPyCairo (>=0.1.0)"] - -[[package]] -name = "requests" -version = "2.28.2" -description = "Python HTTP for Humans." -category = "main" -optional = false -python-versions = ">=3.7, <4" -files = [ - {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, - {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<1.27" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "requests-file" -version = "1.5.1" -description = "File transport adapter for Requests" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "requests-file-1.5.1.tar.gz", hash = "sha256:07d74208d3389d01c38ab89ef403af0cfec63957d53a0081d8eca738d0247d8e"}, - {file = "requests_file-1.5.1-py2.py3-none-any.whl", hash = "sha256:dfe5dae75c12481f68ba353183c53a65e6044c923e64c24b2209f6c7570ca953"}, -] - -[package.dependencies] -requests = ">=1.0.0" -six = "*" - -[[package]] -name = "requests-toolbelt" -version = "0.10.1" -description = "A utility belt for advanced users of python-requests" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "requests-toolbelt-0.10.1.tar.gz", hash = "sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d"}, - {file = "requests_toolbelt-0.10.1-py2.py3-none-any.whl", hash = "sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7"}, -] - -[package.dependencies] -requests = ">=2.0.1,<3.0.0" - -[[package]] -name = "rjsmin" -version = "1.2.1" -description = "Javascript Minifier" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "rjsmin-1.2.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:35827844d2085bd59d34214dfba6f1fc42a215c455887437b07dbf9c73019cc1"}, - {file = "rjsmin-1.2.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:812af25c08d6a5ae98019a2e1b47ebb47f7469abd351670c353d619eaeae4064"}, - {file = "rjsmin-1.2.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:b8464629a18fe69f70677854c93a3707976024b226a0ce62707c618f923e1346"}, - {file = "rjsmin-1.2.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bd1faedc425006d9e86b23837d164f01d105b7a8b66b767a9766d0014773db2a"}, - {file = "rjsmin-1.2.1-cp310-cp310-manylinux1_i686.whl", hash = "sha256:99c074cd6a8302ff47118a9c3d086f89328dc8e5c4b105aa1f348fb85c765a30"}, - {file = "rjsmin-1.2.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:bc5bc2f94e59bc81562c572b7f1bdd6bcec4f61168dc68a2993bad2d355b6e19"}, - {file = "rjsmin-1.2.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:35f21046504544e2941e04190ce24161255479133751550e36ddb3f4af0ecdca"}, - {file = "rjsmin-1.2.1-cp311-cp311-manylinux1_i686.whl", hash = "sha256:ca90630b84fe94bb07739c3e3793e87d30c6ee450dde08653121f0d9153c8d0d"}, - {file = "rjsmin-1.2.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:7dd58b5ed88233bc61dc80b0ed87b93a1786031d9977c70d335221ef1ac5581a"}, - {file = "rjsmin-1.2.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:f0895b360dccf7e2d6af8762a52985e3fbaa56778de1bf6b20dbc96134253807"}, - {file = "rjsmin-1.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:747bc9d3bc8a220f40858e6aad50b2ae2eb7f69c924d4fa3803b81be1c1ddd02"}, - {file = "rjsmin-1.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:f7cd33602ec0f393a0058e883284496bb4dbbdd34e0bbe23b594c8933ddf9b65"}, - {file = "rjsmin-1.2.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:3453ee6d5e7a2723ec45c2909e2382371783400e8d51952b692884c6d850a3d0"}, - {file = "rjsmin-1.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:8c340e251619c97571a5ade20f147f1f7e8664f66a2d6d7319e05e3ef6a4423c"}, - {file = "rjsmin-1.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:145c6af8df42d8af102d0d39a6de2e5fa66aef9e38947cfb9d65377d1b9940b2"}, - {file = "rjsmin-1.2.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:bbd7a0abaa394afd951f5d4e05249d306fec1c9674bfee179787674dddd0bdb7"}, - {file = "rjsmin-1.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:eb770aaf637919b0011c4eb87b9ac6317079fb9800eb17c90dda05fc9de4ebc3"}, - {file = "rjsmin-1.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5d67ec09da46a492186e35cabca02a0d092eda5ef5b408a419b99ee4acf28d5c"}, - {file = "rjsmin-1.2.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:d332e44a1b21ad63401cc7eebc81157e3d982d5fb503bb4faaea5028068d71e9"}, - {file = "rjsmin-1.2.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:113132a40ce7d03b2ced4fac215f0297338ed1c207394b739266efab7831988b"}, - {file = "rjsmin-1.2.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:122aa52bcf7ad9f12728d309012d1308c6ecfe4d6b09ea867a110dcad7b7728c"}, - {file = "rjsmin-1.2.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:8a6710e358c661dcdcfd027e67de3afd72a6af4c88101dcf110de39e9bbded39"}, - {file = "rjsmin-1.2.1.tar.gz", hash = "sha256:1f982be8e011438777a94307279b40134a3935fc0f079312ee299725b8af5411"}, -] - -[[package]] -name = "sentry-sdk" -version = "1.15.0" -description = "Python client for Sentry (https://sentry.io)" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "sentry-sdk-1.15.0.tar.gz", hash = "sha256:69ecbb2e1ff4db02a06c4f20f6f69cb5dfe3ebfbc06d023e40d77cf78e9c37e7"}, - {file = "sentry_sdk-1.15.0-py2.py3-none-any.whl", hash = "sha256:7ad4d37dd093f4a7cb5ad804c6efe9e8fab8873f7ffc06042dc3f3fd700a93ec"}, -] - -[package.dependencies] -certifi = "*" -urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} - -[package.extras] -aiohttp = ["aiohttp (>=3.5)"] -beam = ["apache-beam (>=2.12)"] -bottle = ["bottle (>=0.12.13)"] -celery = ["celery (>=3)"] -chalice = ["chalice (>=1.16.0)"] -django = ["django (>=1.8)"] -falcon = ["falcon (>=1.4)"] -fastapi = ["fastapi (>=0.79.0)"] -flask = ["blinker (>=1.1)", "flask (>=0.11)"] -httpx = ["httpx (>=0.16.0)"] -huey = ["huey (>=2)"] -opentelemetry = ["opentelemetry-distro (>=0.35b0)"] -pure-eval = ["asttokens", "executing", "pure-eval"] -pymongo = ["pymongo (>=3.1)"] -pyspark = ["pyspark (>=2.4.4)"] -quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] -rq = ["rq (>=0.6)"] -sanic = ["sanic (>=0.8)"] -sqlalchemy = ["sqlalchemy (>=1.2)"] -starlette = ["starlette (>=0.19.1)"] -starlite = ["starlite (>=1.48)"] -tornado = ["tornado (>=5)"] - -[[package]] -name = "sepaxml" -version = "2.6.1" -description = "Python SEPA XML implementations" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "sepaxml-2.6.1-py3-none-any.whl", hash = "sha256:f110e4a11322c1ea46f86550d703468b109343b4cece1d5b792d394f03d86110"}, - {file = "sepaxml-2.6.1.tar.gz", hash = "sha256:939c12236779e6a3d7221d3557921abd6e30b0bfd369e67815ea55bccaacd688"}, -] - -[package.dependencies] -text-unidecode = "*" -xmlschema = "*" - -[[package]] -name = "setuptools" -version = "67.6.0" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "setuptools-67.6.0-py3-none-any.whl", hash = "sha256:b78aaa36f6b90a074c1fa651168723acbf45d14cb1196b6f02c0fd07f17623b2"}, - {file = "setuptools-67.6.0.tar.gz", hash = "sha256:2ee892cd5f29f3373097f5a814697e397cf3ce313616df0af11231e2ad118077"}, -] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "slimit" -version = "0.8.1" -description = "SlimIt - JavaScript minifier" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "slimit-0.8.1.zip", hash = "sha256:f433dcef899f166b207b67d91d3f7344659cb33b8259818f084167244e17720b"}, -] - -[package.dependencies] -ply = ">=3.4" - -[[package]] -name = "soupsieve" -version = "2.4" -description = "A modern CSS selector implementation for Beautiful Soup." -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "soupsieve-2.4-py3-none-any.whl", hash = "sha256:49e5368c2cda80ee7e84da9dbe3e110b70a4575f196efb74e51b94549d921955"}, - {file = "soupsieve-2.4.tar.gz", hash = "sha256:e28dba9ca6c7c00173e34e4ba57448f0688bb681b7c5e8bf4971daafc093d69a"}, -] - -[[package]] -name = "sqlparse" -version = "0.4.3" -description = "A non-validating SQL parser." -category = "main" -optional = false -python-versions = ">=3.5" -files = [ - {file = "sqlparse-0.4.3-py3-none-any.whl", hash = "sha256:0323c0ec29cd52bceabc1b4d9d579e311f3e4961b98d174201d5622a23b85e34"}, - {file = "sqlparse-0.4.3.tar.gz", hash = "sha256:69ca804846bb114d2ec380e4360a8a340db83f0ccf3afceeb1404df028f57268"}, -] - -[[package]] -name = "static3" -version = "0.7.0" -description = "A really simple WSGI way to serve static (or mixed) content." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "static3-0.7.0.tar.gz", hash = "sha256:674641c64bc75507af2eb20bef7e7e3593dca993dec6674be108fa15b42f47c8"}, -] - -[package.extras] -genshimagic = ["Genshi"] -kidmagic = ["kid"] - -[[package]] -name = "stripe" -version = "5.1.1" -description = "Python bindings for the Stripe API" -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "stripe-5.1.1-py2.py3-none-any.whl", hash = "sha256:277e156d3d5c82e1795b07c9e07b342c5ec42558ff17455e1e3d20697277dbdf"}, - {file = "stripe-5.1.1.tar.gz", hash = "sha256:c008dd08c59986dcf059fbb776486e70b82d4fa46a63ca1085d94b2688c28e19"}, -] - -[package.dependencies] -requests = {version = ">=2.20", markers = "python_version >= \"3.0\""} - -[[package]] -name = "text-unidecode" -version = "1.3" -description = "The most basic Text::Unidecode port" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, - {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, -] - -[[package]] -name = "tlds" -version = "2022070701" -description = "Automatically updated list of valid TLDs taken directly from IANA" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "tlds-2022070701-py2.py3-none-any.whl", hash = "sha256:7dbc90f0ebfedf6d418a6430cd84ce98103eca0b699fc316e464a97ec811a061"}, - {file = "tlds-2022070701.tar.gz", hash = "sha256:8049061c2844dd35adda622b3c509defc79dd9f2bb7b828580405f9c33ecf8aa"}, -] - -[[package]] -name = "tqdm" -version = "4.65.0" -description = "Fast, Extensible Progress Meter" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tqdm-4.65.0-py3-none-any.whl", hash = "sha256:c4f53a17fe37e132815abceec022631be8ffe1b9381c2e6e30aa70edc99e9671"}, - {file = "tqdm-4.65.0.tar.gz", hash = "sha256:1871fb68a86b8fb3b59ca4cdd3dcccbc7e6d613eeed31f4c332531977b89beb5"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -dev = ["py-make (>=0.1.0)", "twine", "wheel"] -notebook = ["ipywidgets (>=6)"] -slack = ["slack-sdk"] -telegram = ["requests"] - -[[package]] -name = "typing-extensions" -version = "4.5.0" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, - {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, -] - -[[package]] -name = "ujson" -version = "5.7.0" -description = "Ultra fast JSON encoder and decoder for Python" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "ujson-5.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5eba5e69e4361ac3a311cf44fa71bc619361b6e0626768a494771aacd1c2f09b"}, - {file = "ujson-5.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aae4d9e1b4c7b61780f0a006c897a4a1904f862fdab1abb3ea8f45bd11aa58f3"}, - {file = "ujson-5.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2e43ccdba1cb5c6d3448eadf6fc0dae7be6c77e357a3abc968d1b44e265866d"}, - {file = "ujson-5.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54384ce4920a6d35fa9ea8e580bc6d359e3eb961fa7e43f46c78e3ed162d56ff"}, - {file = "ujson-5.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24ad1aa7fc4e4caa41d3d343512ce68e41411fb92adf7f434a4d4b3749dc8f58"}, - {file = "ujson-5.7.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:afff311e9f065a8f03c3753db7011bae7beb73a66189c7ea5fcb0456b7041ea4"}, - {file = "ujson-5.7.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e80f0d03e7e8646fc3d79ed2d875cebd4c83846e129737fdc4c2532dbd43d9e"}, - {file = "ujson-5.7.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:137831d8a0db302fb6828ee21c67ad63ac537bddc4376e1aab1c8573756ee21c"}, - {file = "ujson-5.7.0-cp310-cp310-win32.whl", hash = "sha256:7df3fd35ebc14dafeea031038a99232b32f53fa4c3ecddb8bed132a43eefb8ad"}, - {file = "ujson-5.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:af4639f684f425177d09ae409c07602c4096a6287027469157bfb6f83e01448b"}, - {file = "ujson-5.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b0f2680ce8a70f77f5d70aaf3f013d53e6af6d7058727a35d8ceb4a71cdd4e9"}, - {file = "ujson-5.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a19fd8e7d8cc58a169bea99fed5666023adf707a536d8f7b0a3c51dd498abf"}, - {file = "ujson-5.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6abb8e6d8f1ae72f0ed18287245f5b6d40094e2656d1eab6d99d666361514074"}, - {file = "ujson-5.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8cd622c069368d5074bd93817b31bdb02f8d818e57c29e206f10a1f9c6337dd"}, - {file = "ujson-5.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14f9082669f90e18e64792b3fd0bf19f2b15e7fe467534a35ea4b53f3bf4b755"}, - {file = "ujson-5.7.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d7ff6ebb43bc81b057724e89550b13c9a30eda0f29c2f506f8b009895438f5a6"}, - {file = "ujson-5.7.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f7f241488879d91a136b299e0c4ce091996c684a53775e63bb442d1a8e9ae22a"}, - {file = "ujson-5.7.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5593263a7fcfb934107444bcfba9dde8145b282de0ee9f61e285e59a916dda0f"}, - {file = "ujson-5.7.0-cp311-cp311-win32.whl", hash = "sha256:26c2b32b489c393106e9cb68d0a02e1a7b9d05a07429d875c46b94ee8405bdb7"}, - {file = "ujson-5.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:ed24406454bb5a31df18f0a423ae14beb27b28cdfa34f6268e7ebddf23da807e"}, - {file = "ujson-5.7.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18679484e3bf9926342b1c43a3bd640f93a9eeeba19ef3d21993af7b0c44785d"}, - {file = "ujson-5.7.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ee295761e1c6c30400641f0a20d381633d7622633cdf83a194f3c876a0e4b7e"}, - {file = "ujson-5.7.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b738282e12a05f400b291966630a98d622da0938caa4bc93cf65adb5f4281c60"}, - {file = "ujson-5.7.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00343501dbaa5172e78ef0e37f9ebd08040110e11c12420ff7c1f9f0332d939e"}, - {file = "ujson-5.7.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c0d1f7c3908357ee100aa64c4d1cf91edf99c40ac0069422a4fd5fd23b263263"}, - {file = "ujson-5.7.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a5d2f44331cf04689eafac7a6596c71d6657967c07ac700b0ae1c921178645da"}, - {file = "ujson-5.7.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:16b2254a77b310f118717715259a196662baa6b1f63b1a642d12ab1ff998c3d7"}, - {file = "ujson-5.7.0-cp37-cp37m-win32.whl", hash = "sha256:6faf46fa100b2b89e4db47206cf8a1ffb41542cdd34dde615b2fc2288954f194"}, - {file = "ujson-5.7.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ff0004c3f5a9a6574689a553d1b7819d1a496b4f005a7451f339dc2d9f4cf98c"}, - {file = "ujson-5.7.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:75204a1dd7ec6158c8db85a2f14a68d2143503f4bafb9a00b63fe09d35762a5e"}, - {file = "ujson-5.7.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7312731c7826e6c99cdd3ac503cd9acd300598e7a80bcf41f604fee5f49f566c"}, - {file = "ujson-5.7.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b9dc5a90e2149643df7f23634fe202fed5ebc787a2a1be95cf23632b4d90651"}, - {file = "ujson-5.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6a6961fc48821d84b1198a09516e396d56551e910d489692126e90bf4887d29"}, - {file = "ujson-5.7.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b01a9af52a0d5c46b2c68e3f258fdef2eacaa0ce6ae3e9eb97983f5b1166edb6"}, - {file = "ujson-5.7.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7316d3edeba8a403686cdcad4af737b8415493101e7462a70ff73dd0609eafc"}, - {file = "ujson-5.7.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4ee997799a23227e2319a3f8817ce0b058923dbd31904761b788dc8f53bd3e30"}, - {file = "ujson-5.7.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dda9aa4c33435147262cd2ea87c6b7a1ca83ba9b3933ff7df34e69fee9fced0c"}, - {file = "ujson-5.7.0-cp38-cp38-win32.whl", hash = "sha256:bea8d30e362180aafecabbdcbe0e1f0b32c9fa9e39c38e4af037b9d3ca36f50c"}, - {file = "ujson-5.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:c96e3b872bf883090ddf32cc41957edf819c5336ab0007d0cf3854e61841726d"}, - {file = "ujson-5.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6411aea4c94a8e93c2baac096fbf697af35ba2b2ed410b8b360b3c0957a952d3"}, - {file = "ujson-5.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3d3b3499c55911f70d4e074c626acdb79a56f54262c3c83325ffb210fb03e44d"}, - {file = "ujson-5.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:341f891d45dd3814d31764626c55d7ab3fd21af61fbc99d070e9c10c1190680b"}, - {file = "ujson-5.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f242eec917bafdc3f73a1021617db85f9958df80f267db69c76d766058f7b19"}, - {file = "ujson-5.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3af9f9f22a67a8c9466a32115d9073c72a33ae627b11de6f592df0ee09b98b6"}, - {file = "ujson-5.7.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4a3d794afbf134df3056a813e5c8a935208cddeae975bd4bc0ef7e89c52f0ce0"}, - {file = "ujson-5.7.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:800bf998e78dae655008dd10b22ca8dc93bdcfcc82f620d754a411592da4bbf2"}, - {file = "ujson-5.7.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b5ac3d5c5825e30b438ea92845380e812a476d6c2a1872b76026f2e9d8060fc2"}, - {file = "ujson-5.7.0-cp39-cp39-win32.whl", hash = "sha256:cd90027e6d93e8982f7d0d23acf88c896d18deff1903dd96140613389b25c0dd"}, - {file = "ujson-5.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:523ee146cdb2122bbd827f4dcc2a8e66607b3f665186bce9e4f78c9710b6d8ab"}, - {file = "ujson-5.7.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e87cec407ec004cf1b04c0ed7219a68c12860123dfb8902ef880d3d87a71c172"}, - {file = "ujson-5.7.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bab10165db6a7994e67001733f7f2caf3400b3e11538409d8756bc9b1c64f7e8"}, - {file = "ujson-5.7.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b522be14a28e6ac1cf818599aeff1004a28b42df4ed4d7bc819887b9dac915fc"}, - {file = "ujson-5.7.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7592f40175c723c032cdbe9fe5165b3b5903604f774ab0849363386e99e1f253"}, - {file = "ujson-5.7.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ed22f9665327a981f288a4f758a432824dc0314e4195a0eaeb0da56a477da94d"}, - {file = "ujson-5.7.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:adf445a49d9a97a5a4c9bb1d652a1528de09dd1c48b29f79f3d66cea9f826bf6"}, - {file = "ujson-5.7.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64772a53f3c4b6122ed930ae145184ebaed38534c60f3d859d8c3f00911eb122"}, - {file = "ujson-5.7.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35209cb2c13fcb9d76d249286105b4897b75a5e7f0efb0c0f4b90f222ce48910"}, - {file = "ujson-5.7.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:90712dfc775b2c7a07d4d8e059dd58636bd6ff1776d79857776152e693bddea6"}, - {file = "ujson-5.7.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:0e4e8981c6e7e9e637e637ad8ffe948a09e5434bc5f52ecbb82b4b4cfc092bfb"}, - {file = "ujson-5.7.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:581c945b811a3d67c27566539bfcb9705ea09cb27c4be0002f7a553c8886b817"}, - {file = "ujson-5.7.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d36a807a24c7d44f71686685ae6fbc8793d784bca1adf4c89f5f780b835b6243"}, - {file = "ujson-5.7.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b4257307e3662aa65e2644a277ca68783c5d51190ed9c49efebdd3cbfd5fa44"}, - {file = "ujson-5.7.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea7423d8a2f9e160c5e011119741682414c5b8dce4ae56590a966316a07a4618"}, - {file = "ujson-5.7.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4c592eb91a5968058a561d358d0fef59099ed152cfb3e1cd14eee51a7a93879e"}, - {file = "ujson-5.7.0.tar.gz", hash = "sha256:e788e5d5dcae8f6118ac9b45d0b891a0d55f7ac480eddcb7f07263f2bcf37b23"}, -] - -[[package]] -name = "urllib3" -version = "1.26.15" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -files = [ - {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, - {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[[package]] -name = "vat-moss-forked" -version = "2020.3.20.0.11.0" -description = "Tools for VAT MOSS and Norway VAT on digital services." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "vat_moss_forked-2020.3.20.0.11.0-py3-none-any.whl", hash = "sha256:80f489da56836f5d3ad8e5b29621238a18888fa8669ebf9dd975c3666174471f"}, - {file = "vat_moss_forked-2020.3.20.0.11.0.tar.gz", hash = "sha256:d46836079687a20375cae0336946015060c71a30faa30642c8ea726daf638095"}, -] - -[[package]] -name = "vine" -version = "5.0.0" -description = "Promises, promises, promises." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "vine-5.0.0-py2.py3-none-any.whl", hash = "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30"}, - {file = "vine-5.0.0.tar.gz", hash = "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"}, -] - -[[package]] -name = "vobject" -version = "0.9.6.1" -description = "A full-featured Python package for parsing and creating iCalendar and vCard files" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "vobject-0.9.6.1.tar.gz", hash = "sha256:96512aec74b90abb71f6b53898dd7fe47300cc940104c4f79148f0671f790101"}, -] - -[package.dependencies] -python-dateutil = ">=2.4.0" - -[[package]] -name = "wcwidth" -version = "0.2.6" -description = "Measures the displayed width of unicode strings in a terminal" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, - {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, -] - -[[package]] -name = "webauthn" -version = "0.4.7" -description = "A WebAuthn Python module." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "webauthn-0.4.7-py2.py3-none-any.whl", hash = "sha256:238391b2e2cc60fb51a2cd2d2d6be149920b9af6184651353d9f95856617a9e7"}, - {file = "webauthn-0.4.7.tar.gz", hash = "sha256:8ad9072ff1d6169f3be30d4dc8733ea563dd266962397bc58b40f674a6af74ac"}, -] - -[package.dependencies] -cbor2 = ">=4.0.1" -cryptography = ">=2.3.1" -future = ">=0.17.1" -pyOpenSSL = ">=16.0.0" -six = ">=1.11.0" - -[[package]] -name = "webencodings" -version = "0.5.1" -description = "Character encoding aliases for legacy web content" -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, - {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, -] - -[[package]] -name = "wrapt" -version = "1.15.0" -description = "Module for decorators, wrappers and monkey patching." -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -files = [ - {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, - {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, - {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, - {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, - {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, - {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, - {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, - {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, - {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, - {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, - {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, - {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, - {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, - {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, - {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, - {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, - {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, - {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, - {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, -] - -[[package]] -name = "xmlschema" -version = "2.2.2" -description = "An XML Schema validator and decoder" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "xmlschema-2.2.2-py3-none-any.whl", hash = "sha256:557f3632b54b6ff10576736bba62e43db84eb60f6465a83818576cd9ffcc1799"}, - {file = "xmlschema-2.2.2.tar.gz", hash = "sha256:0caa96668807b4b51c42a0fe2b6610752bc59f069615df3e34dcfffb962973fd"}, -] - -[package.dependencies] -elementpath = ">=4.0.0,<5.0.0" - -[package.extras] -codegen = ["elementpath (>=4.0.0,<5.0.0)", "jinja2"] -dev = ["Sphinx", "coverage", "elementpath (>=4.0.0,<5.0.0)", "flake8", "jinja2", "lxml", "lxml-stubs", "memory-profiler", "mypy", "sphinx-rtd-theme", "tox"] -docs = ["Sphinx", "elementpath (>=4.0.0,<5.0.0)", "jinja2", "sphinx-rtd-theme"] - -[[package]] -name = "zeep" -version = "4.2.1" -description = "A Python SOAP client" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "zeep-4.2.1-py3-none-any.whl", hash = "sha256:6754feb4c34a4b6d65fbc359252bf6654dcce3937bf1d95aae4402a60a8f5939"}, - {file = "zeep-4.2.1.tar.gz", hash = "sha256:72093acfdb1d8360ed400869b73fbf1882b95c4287f798084c42ee0c1ff0e425"}, -] - -[package.dependencies] -attrs = ">=17.2.0" -isodate = ">=0.5.4" -lxml = ">=4.6.0" -platformdirs = ">=1.4.0" -pytz = "*" -requests = ">=2.7.0" -requests-file = ">=1.5.1" -requests-toolbelt = ">=0.7.1" - -[package.extras] -async = ["httpx (>=0.15.0)"] -docs = ["sphinx (>=1.4.0)"] -test = ["coverage[toml] (==5.2.1)", "flake8 (==3.8.3)", "flake8-blind-except (==0.1.1)", "flake8-debugger (==3.2.1)", "flake8-imports (==0.1.1)", "freezegun (==0.3.15)", "isort (==5.3.2)", "pretend (==1.0.9)", "pytest (==6.2.5)", "pytest-asyncio", "pytest-cov (==2.8.1)", "pytest-httpx", "requests-mock (>=0.7.0)"] -xmlsec = ["xmlsec (>=0.6.1)"] - -[metadata] -lock-version = "2.0" -python-versions = "^3.10" -content-hash = "e19638ee5310d6fbaa36fdebf1349c5b64f744374d5cac0051aedb37c08c46f5" diff --git a/pkgs/pretix/pretix-banktool-requirements.patch b/pkgs/pretix/pretix-banktool-requirements.patch deleted file mode 100644 index 72ca79d..0000000 --- a/pkgs/pretix/pretix-banktool-requirements.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/setup.py b/setup.py -index 2eba88a..7041acd 100644 ---- a/setup.py -+++ b/setup.py -@@ -19,7 +19,7 @@ setup( - author_email='mail@raphaelmichel.de', - - install_requires=[ -- 'click==6.*', -+ 'click>=6.*', - 'fints>=3.0.*', - 'requests', - 'mt-940>=4.12*', --- -2.38.3 diff --git a/pkgs/pretix/pretix-banktool.nix b/pkgs/pretix/pretix-banktool.nix deleted file mode 100644 index 74d332d..0000000 --- a/pkgs/pretix/pretix-banktool.nix +++ /dev/null @@ -1,23 +0,0 @@ -{ python3Packages, fetchFromGitHub }: -python3Packages.buildPythonApplication rec { - name = "pretix-banktool"; - version = "1.0.0"; - - src = fetchFromGitHub { - owner = "pretix"; - repo = "pretix-banktool"; - rev = "v${version}"; - sha256 = "vYHjotx1RujPV53Ei7bXAc3kL/3cwbWQB1T3sQ15MFA="; - }; - - patches = [ - ./pretix-banktool-requirements.patch - ]; - - propagatedBuildInputs = with python3Packages; [ - click - fints - mt-940 - requests - ]; -} diff --git a/pkgs/pretix/pretix-static.nix b/pkgs/pretix/pretix-static.nix deleted file mode 100644 index b7be449..0000000 --- a/pkgs/pretix/pretix-static.nix +++ /dev/null @@ -1,48 +0,0 @@ -{ stdenvNoCC -, pretix -, buildNpmPackage -, makeWrapper -}: - -let - nodeEnv = buildNpmPackage rec { - name = "pretix-nodejs"; - src = "${pretix.passthru.pythonModule.pkgs.pretix}/lib/python3.10/site-packages/pretix/static/npm_dir"; - npmDepsHash = "sha256-fAs+e1P0/uFrmjhAKd/8OBoqECuMjQ2jvVsGv8KWVec="; - dontNpmBuild = true; - installPhase = '' - mkdir -p $out - cp -r node_modules $out/ - mkdir -p $out/bin - ln -s $out/node_modules/rollup/dist/bin/rollup $out/bin/rollup - ''; - postFixup = '' - wrapProgram $out/bin/rollup --prefix NODE_PATH : $out - ''; - nativeBuildInputs = [ - makeWrapper - ]; - }; -in -stdenvNoCC.mkDerivation { - name = "pretix-static"; - src = ./.; - buildPhase = '' - mkdir $out - export PRETIX_STATIC_ROOT=$out - export DJANGO_SETTINGS_MODULE=pretix_wrapper.settings - ${pretix}/bin/pretix collectstatic --noinput - mkdir -p $PRETIX_STATIC_ROOT/node_prefix - ln -s ${nodeEnv}/node_modules $PRETIX_STATIC_ROOT/node_prefix/node_modules - echo ${nodeEnv}/bin/rollup - ${pretix}/bin/pretix compress - ''; - installPhase = '' - runHook preInstall - runHook postInstall - ''; - nativeBuildInputs = [ - nodeEnv - ]; -} - diff --git a/pkgs/pretix/pretix.nix b/pkgs/pretix/pretix.nix deleted file mode 100644 index 6c5337c..0000000 --- a/pkgs/pretix/pretix.nix +++ /dev/null @@ -1,64 +0,0 @@ -{ lib -, poetry2nix -, pkgs -, gettext -, tlds-alpha-by-domain ? ./tlds-alpha-by-domain.txt -}: - -let - tlds = pkgs.fetchurl { - url = "https://data.iana.org/TLD/tlds-alpha-by-domain.txt"; - sha256 = "0153py77ll759jacq41dp2z2ksr08pdcfic0rwjd6pr84dk89y9v"; - }; - pkgsRequiringSetuptools = [ - "dj-static" - "django-jquery-js" - "paypal-checkout-serversdk" - "python-u2flib-server" - "slimit" - "static3" - ]; -in -poetry2nix.mkPoetryApplication rec { - projectDir = ./.; - #python = pkgs.python310; - preferWheels = true; - overrides = poetry2nix.defaultPoetryOverrides.extend - ( - self: super: lib.attrsets.genAttrs pkgsRequiringSetuptools - ( - pythonPackage: - super."${pythonPackage}".overridePythonAttrs ( - old: { - buildInputs = (old.buildInputs or [ ]) ++ [ super.setuptools ]; - } - ) - ) // { - tlds = super.tlds.overridePythonAttrs ( - old: { - buildInputs = (old.buildInputs or [ ]) ++ [ super.setuptools ]; - patches = [ - ./tlds-read-domains-from-file.patch - ]; - - # https://data.iana.org/TLD/tlds-alpha-by-domain.txt - preBuild = "cp ${tlds-alpha-by-domain} ./tlds-alpha-by-domain.txt"; - } - ); - pretix = super.pretix.overridePythonAttrs ( - old: { - buildInputs = (old.buildInputs or [ ]) ++ [ - gettext - ]; - preFixup = '' - python -m pretix compilemessages - python -m pretix compilejsi18n - ''; - } - ); - } - ); -} -# .overrideAttrs(old: { -# inherit (old.passthru.pythonPackages.pretix) pname name version; -# }) diff --git a/pkgs/pretix/pretix_wrapper/__main__.py b/pkgs/pretix/pretix_wrapper/__main__.py deleted file mode 100644 index 57fdb08..0000000 --- a/pkgs/pretix/pretix_wrapper/__main__.py +++ /dev/null @@ -1,9 +0,0 @@ -import sys -import os - -module_name = "pretix" - - -def main(): - os.environ["PYTHONPATH"] = ":".join(sys.path) - os.execv(sys.executable, [sys.executable, "-m", module_name, *sys.argv[1:]]) diff --git a/pkgs/pretix/pretix_wrapper/settings.py b/pkgs/pretix/pretix_wrapper/settings.py deleted file mode 100644 index e0c5620..0000000 --- a/pkgs/pretix/pretix_wrapper/settings.py +++ /dev/null @@ -1,4 +0,0 @@ -import os -from pretix.settings import * - -STATIC_ROOT = os.getenv("PRETIX_STATIC_ROOT") diff --git a/pkgs/pretix/pyproject.toml b/pkgs/pretix/pyproject.toml deleted file mode 100644 index 26cc3fc..0000000 --- a/pkgs/pretix/pyproject.toml +++ /dev/null @@ -1,19 +0,0 @@ -[tool.poetry] -name = "pretix_wrapper" -version = "1.0.0" -description = "" -authors = ["Jakob Lechner "] -license = "MIT" - -[tool.poetry.dependencies] -python = "^3.10" -pretix = "^4.16" - -[tool.poetry.dev-dependencies] - -[tool.poetry.scripts] -pretix = "pretix_wrapper.__main__:main" - -[build-system] -requires = ["poetry>=0.12"] -build-backend = "poetry.masonry.api" diff --git a/pkgs/pretix/tlds-alpha-by-domain.txt b/pkgs/pretix/tlds-alpha-by-domain.txt deleted file mode 100644 index d3a46e7..0000000 --- a/pkgs/pretix/tlds-alpha-by-domain.txt +++ /dev/null @@ -1,1482 +0,0 @@ -# Version 2023022100, Last Updated Tue Feb 21 07:07:02 2023 UTC -AAA -AARP -ABARTH -ABB -ABBOTT -ABBVIE -ABC -ABLE -ABOGADO -ABUDHABI -AC -ACADEMY -ACCENTURE -ACCOUNTANT -ACCOUNTANTS -ACO -ACTOR -AD -ADS -ADULT -AE -AEG -AERO -AETNA -AF -AFL -AFRICA -AG -AGAKHAN -AGENCY -AI -AIG -AIRBUS -AIRFORCE -AIRTEL -AKDN -AL -ALFAROMEO -ALIBABA -ALIPAY -ALLFINANZ -ALLSTATE -ALLY -ALSACE -ALSTOM -AM -AMAZON -AMERICANEXPRESS -AMERICANFAMILY -AMEX -AMFAM -AMICA -AMSTERDAM -ANALYTICS -ANDROID -ANQUAN -ANZ -AO -AOL -APARTMENTS -APP -APPLE -AQ -AQUARELLE -AR -ARAB -ARAMCO -ARCHI -ARMY -ARPA -ART -ARTE -AS -ASDA -ASIA -ASSOCIATES -AT -ATHLETA -ATTORNEY -AU -AUCTION -AUDI -AUDIBLE -AUDIO -AUSPOST -AUTHOR -AUTO -AUTOS -AVIANCA -AW -AWS -AX -AXA -AZ -AZURE -BA -BABY -BAIDU -BANAMEX -BANANAREPUBLIC -BAND -BANK -BAR -BARCELONA -BARCLAYCARD -BARCLAYS -BAREFOOT -BARGAINS -BASEBALL -BASKETBALL -BAUHAUS -BAYERN -BB -BBC -BBT -BBVA -BCG -BCN -BD -BE -BEATS -BEAUTY -BEER -BENTLEY -BERLIN -BEST -BESTBUY -BET -BF -BG -BH -BHARTI -BI -BIBLE -BID -BIKE -BING -BINGO -BIO -BIZ -BJ -BLACK -BLACKFRIDAY -BLOCKBUSTER -BLOG -BLOOMBERG -BLUE -BM -BMS -BMW -BN -BNPPARIBAS -BO -BOATS -BOEHRINGER -BOFA -BOM -BOND -BOO -BOOK -BOOKING -BOSCH -BOSTIK -BOSTON -BOT -BOUTIQUE -BOX -BR -BRADESCO -BRIDGESTONE -BROADWAY -BROKER -BROTHER -BRUSSELS -BS -BT -BUILD -BUILDERS -BUSINESS -BUY -BUZZ -BV -BW -BY -BZ -BZH -CA -CAB -CAFE -CAL -CALL -CALVINKLEIN -CAM -CAMERA -CAMP -CANON -CAPETOWN -CAPITAL -CAPITALONE -CAR -CARAVAN -CARDS -CARE -CAREER -CAREERS -CARS -CASA -CASE -CASH -CASINO -CAT -CATERING -CATHOLIC -CBA -CBN -CBRE -CBS -CC -CD -CENTER -CEO -CERN -CF -CFA -CFD -CG -CH -CHANEL -CHANNEL -CHARITY -CHASE -CHAT -CHEAP -CHINTAI -CHRISTMAS -CHROME -CHURCH -CI -CIPRIANI -CIRCLE -CISCO -CITADEL -CITI -CITIC -CITY -CITYEATS -CK -CL -CLAIMS -CLEANING -CLICK -CLINIC -CLINIQUE -CLOTHING -CLOUD -CLUB -CLUBMED -CM -CN -CO -COACH -CODES -COFFEE -COLLEGE -COLOGNE -COM -COMCAST -COMMBANK -COMMUNITY -COMPANY -COMPARE -COMPUTER -COMSEC -CONDOS -CONSTRUCTION -CONSULTING -CONTACT -CONTRACTORS -COOKING -COOKINGCHANNEL -COOL -COOP -CORSICA -COUNTRY -COUPON -COUPONS -COURSES -CPA -CR -CREDIT -CREDITCARD -CREDITUNION -CRICKET -CROWN -CRS -CRUISE -CRUISES -CU -CUISINELLA -CV -CW -CX -CY -CYMRU -CYOU -CZ -DABUR -DAD -DANCE -DATA -DATE -DATING -DATSUN -DAY -DCLK -DDS -DE -DEAL -DEALER -DEALS -DEGREE -DELIVERY -DELL -DELOITTE -DELTA -DEMOCRAT -DENTAL -DENTIST -DESI -DESIGN -DEV -DHL -DIAMONDS -DIET -DIGITAL -DIRECT -DIRECTORY -DISCOUNT -DISCOVER -DISH -DIY -DJ -DK -DM -DNP -DO -DOCS -DOCTOR -DOG -DOMAINS -DOT -DOWNLOAD -DRIVE -DTV -DUBAI -DUNLOP -DUPONT -DURBAN -DVAG -DVR -DZ -EARTH -EAT -EC -ECO -EDEKA -EDU -EDUCATION -EE -EG -EMAIL -EMERCK -ENERGY -ENGINEER -ENGINEERING -ENTERPRISES -EPSON -EQUIPMENT -ER -ERICSSON -ERNI -ES -ESQ -ESTATE -ET -ETISALAT -EU -EUROVISION -EUS -EVENTS -EXCHANGE -EXPERT -EXPOSED -EXPRESS -EXTRASPACE -FAGE -FAIL -FAIRWINDS -FAITH -FAMILY -FAN -FANS -FARM -FARMERS -FASHION -FAST -FEDEX -FEEDBACK -FERRARI -FERRERO -FI -FIAT -FIDELITY -FIDO -FILM -FINAL -FINANCE -FINANCIAL -FIRE -FIRESTONE -FIRMDALE -FISH -FISHING -FIT -FITNESS -FJ -FK -FLICKR -FLIGHTS -FLIR -FLORIST -FLOWERS -FLY -FM -FO -FOO -FOOD -FOODNETWORK -FOOTBALL -FORD -FOREX -FORSALE -FORUM -FOUNDATION -FOX -FR -FREE -FRESENIUS -FRL -FROGANS -FRONTDOOR -FRONTIER -FTR -FUJITSU -FUN -FUND -FURNITURE -FUTBOL -FYI -GA -GAL -GALLERY -GALLO -GALLUP -GAME -GAMES -GAP -GARDEN -GAY -GB -GBIZ -GD -GDN -GE -GEA -GENT -GENTING -GEORGE -GF -GG -GGEE -GH -GI -GIFT -GIFTS -GIVES -GIVING -GL -GLASS -GLE -GLOBAL -GLOBO -GM -GMAIL -GMBH -GMO -GMX -GN -GODADDY -GOLD -GOLDPOINT -GOLF -GOO -GOODYEAR -GOOG -GOOGLE -GOP -GOT -GOV -GP -GQ -GR -GRAINGER -GRAPHICS -GRATIS -GREEN -GRIPE -GROCERY -GROUP -GS -GT -GU -GUARDIAN -GUCCI -GUGE -GUIDE -GUITARS -GURU -GW -GY -HAIR -HAMBURG -HANGOUT -HAUS -HBO -HDFC -HDFCBANK -HEALTH -HEALTHCARE -HELP -HELSINKI -HERE -HERMES -HGTV -HIPHOP -HISAMITSU -HITACHI -HIV -HK -HKT -HM -HN -HOCKEY -HOLDINGS -HOLIDAY -HOMEDEPOT -HOMEGOODS -HOMES -HOMESENSE -HONDA -HORSE -HOSPITAL -HOST -HOSTING -HOT -HOTELES -HOTELS -HOTMAIL -HOUSE -HOW -HR -HSBC -HT -HU -HUGHES -HYATT -HYUNDAI -IBM -ICBC -ICE -ICU -ID -IE -IEEE -IFM -IKANO -IL -IM -IMAMAT -IMDB -IMMO -IMMOBILIEN -IN -INC -INDUSTRIES -INFINITI -INFO -ING -INK -INSTITUTE -INSURANCE -INSURE -INT -INTERNATIONAL -INTUIT -INVESTMENTS -IO -IPIRANGA -IQ -IR -IRISH -IS -ISMAILI -IST -ISTANBUL -IT -ITAU -ITV -JAGUAR -JAVA -JCB -JE -JEEP -JETZT -JEWELRY -JIO -JLL -JM -JMP -JNJ -JO -JOBS -JOBURG -JOT -JOY -JP -JPMORGAN -JPRS -JUEGOS -JUNIPER -KAUFEN -KDDI -KE -KERRYHOTELS -KERRYLOGISTICS -KERRYPROPERTIES -KFH -KG -KH -KI -KIA -KIDS -KIM -KINDER -KINDLE -KITCHEN -KIWI -KM -KN -KOELN -KOMATSU -KOSHER -KP -KPMG -KPN -KR -KRD -KRED -KUOKGROUP -KW -KY -KYOTO -KZ -LA -LACAIXA -LAMBORGHINI -LAMER -LANCASTER -LANCIA -LAND -LANDROVER -LANXESS -LASALLE -LAT -LATINO -LATROBE -LAW -LAWYER -LB -LC -LDS -LEASE -LECLERC -LEFRAK -LEGAL -LEGO -LEXUS -LGBT -LI -LIDL -LIFE -LIFEINSURANCE -LIFESTYLE -LIGHTING -LIKE -LILLY -LIMITED -LIMO -LINCOLN -LINDE -LINK -LIPSY -LIVE -LIVING -LK -LLC -LLP -LOAN -LOANS -LOCKER -LOCUS -LOL -LONDON -LOTTE -LOTTO -LOVE -LPL -LPLFINANCIAL -LR -LS -LT -LTD -LTDA -LU -LUNDBECK -LUXE -LUXURY -LV -LY -MA -MACYS -MADRID -MAIF -MAISON -MAKEUP -MAN -MANAGEMENT -MANGO -MAP -MARKET -MARKETING -MARKETS -MARRIOTT -MARSHALLS -MASERATI -MATTEL -MBA -MC -MCKINSEY -MD -ME -MED -MEDIA -MEET -MELBOURNE -MEME -MEMORIAL -MEN -MENU -MERCKMSD -MG -MH -MIAMI -MICROSOFT -MIL -MINI -MINT -MIT -MITSUBISHI -MK -ML -MLB -MLS -MM -MMA -MN -MO -MOBI -MOBILE -MODA -MOE -MOI -MOM -MONASH -MONEY -MONSTER -MORMON -MORTGAGE -MOSCOW -MOTO -MOTORCYCLES -MOV -MOVIE -MP -MQ -MR -MS -MSD -MT -MTN -MTR -MU -MUSEUM -MUSIC -MUTUAL -MV -MW -MX -MY -MZ -NA -NAB -NAGOYA -NAME -NATURA -NAVY -NBA -NC -NE -NEC -NET -NETBANK -NETFLIX -NETWORK -NEUSTAR -NEW -NEWS -NEXT -NEXTDIRECT -NEXUS -NF -NFL -NG -NGO -NHK -NI -NICO -NIKE -NIKON -NINJA -NISSAN -NISSAY -NL -NO -NOKIA -NORTHWESTERNMUTUAL -NORTON -NOW -NOWRUZ -NOWTV -NP -NR -NRA -NRW -NTT -NU -NYC -NZ -OBI -OBSERVER -OFFICE -OKINAWA -OLAYAN -OLAYANGROUP -OLDNAVY -OLLO -OM -OMEGA -ONE -ONG -ONL -ONLINE -OOO -OPEN -ORACLE -ORANGE -ORG -ORGANIC -ORIGINS -OSAKA -OTSUKA -OTT -OVH -PA -PAGE -PANASONIC -PARIS -PARS -PARTNERS -PARTS -PARTY -PASSAGENS -PAY -PCCW -PE -PET -PF -PFIZER -PG -PH -PHARMACY -PHD -PHILIPS -PHONE -PHOTO -PHOTOGRAPHY -PHOTOS -PHYSIO -PICS -PICTET -PICTURES -PID -PIN -PING -PINK -PIONEER -PIZZA -PK -PL -PLACE -PLAY -PLAYSTATION -PLUMBING -PLUS -PM -PN -PNC -POHL -POKER -POLITIE -PORN -POST -PR -PRAMERICA -PRAXI -PRESS -PRIME -PRO -PROD -PRODUCTIONS -PROF -PROGRESSIVE -PROMO -PROPERTIES -PROPERTY -PROTECTION -PRU -PRUDENTIAL -PS -PT -PUB -PW -PWC -PY -QA -QPON -QUEBEC -QUEST -RACING -RADIO -RE -READ -REALESTATE -REALTOR -REALTY -RECIPES -RED -REDSTONE -REDUMBRELLA -REHAB -REISE -REISEN -REIT -RELIANCE -REN -RENT -RENTALS -REPAIR -REPORT -REPUBLICAN -REST -RESTAURANT -REVIEW -REVIEWS -REXROTH -RICH -RICHARDLI -RICOH -RIL -RIO -RIP -RO -ROCHER -ROCKS -RODEO -ROGERS -ROOM -RS -RSVP -RU -RUGBY -RUHR -RUN -RW -RWE -RYUKYU -SA -SAARLAND -SAFE -SAFETY -SAKURA -SALE -SALON -SAMSCLUB -SAMSUNG -SANDVIK -SANDVIKCOROMANT -SANOFI -SAP -SARL -SAS -SAVE -SAXO -SB -SBI -SBS -SC -SCA -SCB -SCHAEFFLER -SCHMIDT -SCHOLARSHIPS -SCHOOL -SCHULE -SCHWARZ -SCIENCE -SCOT -SD -SE -SEARCH -SEAT -SECURE -SECURITY -SEEK -SELECT -SENER -SERVICES -SEVEN -SEW -SEX -SEXY -SFR -SG -SH -SHANGRILA -SHARP -SHAW -SHELL -SHIA -SHIKSHA -SHOES -SHOP -SHOPPING -SHOUJI -SHOW -SHOWTIME -SI -SILK -SINA -SINGLES -SITE -SJ -SK -SKI -SKIN -SKY -SKYPE -SL -SLING -SM -SMART -SMILE -SN -SNCF -SO -SOCCER -SOCIAL -SOFTBANK -SOFTWARE -SOHU -SOLAR -SOLUTIONS -SONG -SONY -SOY -SPA -SPACE -SPORT -SPOT -SR -SRL -SS -ST -STADA -STAPLES -STAR -STATEBANK -STATEFARM -STC -STCGROUP -STOCKHOLM -STORAGE -STORE -STREAM -STUDIO -STUDY -STYLE -SU -SUCKS -SUPPLIES -SUPPLY -SUPPORT -SURF -SURGERY -SUZUKI -SV -SWATCH -SWISS -SX -SY -SYDNEY -SYSTEMS -SZ -TAB -TAIPEI -TALK -TAOBAO -TARGET -TATAMOTORS -TATAR -TATTOO -TAX -TAXI -TC -TCI -TD -TDK -TEAM -TECH -TECHNOLOGY -TEL -TEMASEK -TENNIS -TEVA -TF -TG -TH -THD -THEATER -THEATRE -TIAA -TICKETS -TIENDA -TIFFANY -TIPS -TIRES -TIROL -TJ -TJMAXX -TJX -TK -TKMAXX -TL -TM -TMALL -TN -TO -TODAY -TOKYO -TOOLS -TOP -TORAY -TOSHIBA -TOTAL -TOURS -TOWN -TOYOTA -TOYS -TR -TRADE -TRADING -TRAINING -TRAVEL -TRAVELCHANNEL -TRAVELERS -TRAVELERSINSURANCE -TRUST -TRV -TT -TUBE -TUI -TUNES -TUSHU -TV -TVS -TW -TZ -UA -UBANK -UBS -UG -UK -UNICOM -UNIVERSITY -UNO -UOL -UPS -US -UY -UZ -VA -VACATIONS -VANA -VANGUARD -VC -VE -VEGAS -VENTURES -VERISIGN -VERSICHERUNG -VET -VG -VI -VIAJES -VIDEO -VIG -VIKING -VILLAS -VIN -VIP -VIRGIN -VISA -VISION -VIVA -VIVO -VLAANDEREN -VN -VODKA -VOLKSWAGEN -VOLVO -VOTE -VOTING -VOTO -VOYAGE -VU -VUELOS -WALES -WALMART -WALTER -WANG -WANGGOU -WATCH -WATCHES -WEATHER -WEATHERCHANNEL -WEBCAM -WEBER -WEBSITE -WED -WEDDING -WEIBO -WEIR -WF -WHOSWHO -WIEN -WIKI -WILLIAMHILL -WIN -WINDOWS -WINE -WINNERS -WME -WOLTERSKLUWER -WOODSIDE -WORK -WORKS -WORLD -WOW -WS -WTC -WTF -XBOX -XEROX -XFINITY -XIHUAN -XIN -XN--11B4C3D -XN--1CK2E1B -XN--1QQW23A -XN--2SCRJ9C -XN--30RR7Y -XN--3BST00M -XN--3DS443G -XN--3E0B707E -XN--3HCRJ9C -XN--3PXU8K -XN--42C2D9A -XN--45BR5CYL -XN--45BRJ9C -XN--45Q11C -XN--4DBRK0CE -XN--4GBRIM -XN--54B7FTA0CC -XN--55QW42G -XN--55QX5D -XN--5SU34J936BGSG -XN--5TZM5G -XN--6FRZ82G -XN--6QQ986B3XL -XN--80ADXHKS -XN--80AO21A -XN--80AQECDR1A -XN--80ASEHDB -XN--80ASWG -XN--8Y0A063A -XN--90A3AC -XN--90AE -XN--90AIS -XN--9DBQ2A -XN--9ET52U -XN--9KRT00A -XN--B4W605FERD -XN--BCK1B9A5DRE4C -XN--C1AVG -XN--C2BR7G -XN--CCK2B3B -XN--CCKWCXETD -XN--CG4BKI -XN--CLCHC0EA0B2G2A9GCD -XN--CZR694B -XN--CZRS0T -XN--CZRU2D -XN--D1ACJ3B -XN--D1ALF -XN--E1A4C -XN--ECKVDTC9D -XN--EFVY88H -XN--FCT429K -XN--FHBEI -XN--FIQ228C5HS -XN--FIQ64B -XN--FIQS8S -XN--FIQZ9S -XN--FJQ720A -XN--FLW351E -XN--FPCRJ9C3D -XN--FZC2C9E2C -XN--FZYS8D69UVGM -XN--G2XX48C -XN--GCKR3F0F -XN--GECRJ9C -XN--GK3AT1E -XN--H2BREG3EVE -XN--H2BRJ9C -XN--H2BRJ9C8C -XN--HXT814E -XN--I1B6B1A6A2E -XN--IMR513N -XN--IO0A7I -XN--J1AEF -XN--J1AMH -XN--J6W193G -XN--JLQ480N2RG -XN--JVR189M -XN--KCRX77D1X4A -XN--KPRW13D -XN--KPRY57D -XN--KPUT3I -XN--L1ACC -XN--LGBBAT1AD8J -XN--MGB9AWBF -XN--MGBA3A3EJT -XN--MGBA3A4F16A -XN--MGBA7C0BBN0A -XN--MGBAAKC7DVF -XN--MGBAAM7A8H -XN--MGBAB2BD -XN--MGBAH1A3HJKRD -XN--MGBAI9AZGQP6J -XN--MGBAYH7GPA -XN--MGBBH1A -XN--MGBBH1A71E -XN--MGBC0A9AZCG -XN--MGBCA7DZDO -XN--MGBCPQ6GPA1A -XN--MGBERP4A5D4AR -XN--MGBGU82A -XN--MGBI4ECEXP -XN--MGBPL2FH -XN--MGBT3DHD -XN--MGBTX2B -XN--MGBX4CD0AB -XN--MIX891F -XN--MK1BU44C -XN--MXTQ1M -XN--NGBC5AZD -XN--NGBE9E0A -XN--NGBRX -XN--NODE -XN--NQV7F -XN--NQV7FS00EMA -XN--NYQY26A -XN--O3CW4H -XN--OGBPF8FL -XN--OTU796D -XN--P1ACF -XN--P1AI -XN--PGBS0DH -XN--PSSY2U -XN--Q7CE6A -XN--Q9JYB4C -XN--QCKA1PMC -XN--QXA6A -XN--QXAM -XN--RHQV96G -XN--ROVU88B -XN--RVC1E0AM3E -XN--S9BRJ9C -XN--SES554G -XN--T60B56A -XN--TCKWE -XN--TIQ49XQYJ -XN--UNUP4Y -XN--VERMGENSBERATER-CTB -XN--VERMGENSBERATUNG-PWB -XN--VHQUV -XN--VUQ861B -XN--W4R85EL8FHU5DNRA -XN--W4RS40L -XN--WGBH1C -XN--WGBL6A -XN--XHQ521B -XN--XKC2AL3HYE2A -XN--XKC2DL3A5EE0H -XN--Y9A3AQ -XN--YFRO4I67O -XN--YGBI2AMMX -XN--ZFR164B -XXX -XYZ -YACHTS -YAHOO -YAMAXUN -YANDEX -YE -YODOBASHI -YOGA -YOKOHAMA -YOU -YOUTUBE -YT -YUN -ZA -ZAPPOS -ZARA -ZERO -ZIP -ZM -ZONE -ZUERICH -ZW diff --git a/pkgs/pretix/tlds-read-domains-from-file.patch b/pkgs/pretix/tlds-read-domains-from-file.patch deleted file mode 100644 index ba8f166..0000000 --- a/pkgs/pretix/tlds-read-domains-from-file.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 40544e83b5920c84a4565c2bf8dc29fc381d5796 Mon Sep 17 00:00:00 2001 -From: Jakob Lechner -Date: Tue, 21 Feb 2023 21:25:06 +0000 -Subject: [PATCH] read static file - ---- - setup.py | 13 +++---------- - 1 file changed, 3 insertions(+), 10 deletions(-) - -diff --git a/setup.py b/setup.py -index 6b4653c..13b6ad4 100644 ---- a/setup.py -+++ b/setup.py -@@ -8,16 +8,9 @@ from setuptools import setup, find_packages - if sys.version_info.major >= 3: - import urllib.request - -- r = urllib.request.urlopen('https://data.iana.org/TLD/tlds-alpha-by-domain.txt') -- assert r.status == 200 -- data = r.read().decode('utf-8').split('\n') --else: -- import urllib -- -- r = urllib.urlopen('https://data.iana.org/TLD/tlds-alpha-by-domain.txt') -- assert r.getcode() == 200 -- data = r.read().split('\n') -- -+ with open("tlds-alpha-by-domain.txt", 'r') as f: -+ data = f.read().split('\n') -+ - version = re.match('^# Version (?P[0-9]+).*$', data[0]).group('version') - tlds = [i.lower() for i in data[1:] if i and not i.startswith('#')] - --- -2.38.3 - diff --git a/pkgs/tabbed-box-maker/default.nix b/pkgs/tabbed-box-maker/default.nix index d30e534..dee949e 100644 --- a/pkgs/tabbed-box-maker/default.nix +++ b/pkgs/tabbed-box-maker/default.nix @@ -14,7 +14,7 @@ stdenvNoCC.mkDerivation { sha256 = "8TNNVMSwbvcEwkvMHecHtGLEpiX3F0g0EGsgO1YKBGQ="; }; - dontBild = true; + dontBuild = true; installPhase = '' mkdir $out cp * $out diff --git a/pkgs/tradebyte-attendance/default.nix b/pkgs/tradebyte-attendance/default.nix deleted file mode 100644 index 84d04d4..0000000 --- a/pkgs/tradebyte-attendance/default.nix +++ /dev/null @@ -1,14 +0,0 @@ -{ pkgs, python3Packages }: - -python3Packages.buildPythonApplication rec { - pname = "tradebyte-attendance"; - version = "0.0.1"; - - src = ./.; - - propagatedBuildInputs = [ - python3Packages.slack-sdk - pkgs.timetrap - pkgs.pass - ]; -} diff --git a/pkgs/tradebyte-attendance/setup.py b/pkgs/tradebyte-attendance/setup.py deleted file mode 100644 index bee6866..0000000 --- a/pkgs/tradebyte-attendance/setup.py +++ /dev/null @@ -1,15 +0,0 @@ -from setuptools import setup, find_packages - -setup( - name="tradebyte_attendance", - version="0.0.1", - author="jalr", - author_email="mail@jalr.de", - description="Send attendance messages to slack channel", - packages=find_packages(), - entry_points={ - "console_scripts": [ - "tradebyte-attendance = tradebyte_attendance.__init__:main", - ], - }, -) diff --git a/pkgs/tradebyte-attendance/tradebyte_attendance/__init__.py b/pkgs/tradebyte-attendance/tradebyte_attendance/__init__.py deleted file mode 100644 index 458c627..0000000 --- a/pkgs/tradebyte-attendance/tradebyte_attendance/__init__.py +++ /dev/null @@ -1,94 +0,0 @@ -import argparse -import datetime -import random -import subprocess - -from slack_sdk import WebClient - - -class Anwesenheit: - _slack_client = None - - @property - def slack_client(self): - if self._slack_client is None: - token = get_pass("slack/token/anwesenheit")[0] - self._slack_client = WebClient( - token=token, - ) - return self._slack_client - - def timetrap(self, cmd): - subprocess.call(["timetrap", cmd]) - - def send_anwesenheit_slack(self, channel, message): - self.slack_client.chat_postMessage(channel=channel, text=message) - - def morning(self, test): - messages = ["Good morning"] - self.set_state(messages, "in", test) - - def lunch(self, test): - messages = ["Lunch"] - self.set_state(messages, "out", test) - - def back(self, test): - messages = ["back"] - self.set_state(messages, "in", test) - - def bye(self, test): - if datetime.datetime.today().weekday() == 4: # Friday! - messages = [ - "Bye, have a nice weekend everyone!", - "Bye, enjoy your weekend", - "I'm out, have a nice weekend!", - "Have a nice weekend everyone!", - ] - else: - messages = [ - "See you tomorrow!", - "Bye, have a nice evening!", - "I'm out for today", - "Bye", - "That's it for today, see you tomorrow.", - ] - self.set_state(messages, "out", test) - - def set_state(self, messages, state, test_mode): - self.timetrap(state) - message = random.choice(messages) - channel = "jal-test" if test_mode else "ccs-attendance" - self.send_anwesenheit_slack(channel, message) - - -def get_pass(title): - return subprocess.check_output(["pass", "show", title], text=True).splitlines() - - -def main(): - parser = argparse.ArgumentParser() - command_subparsers = parser.add_subparsers(dest="command", required=True) - command_arguments = { - "--test": { - "action": "store_true", - "help": "publish message to test channel", - }, - } - for command, command_help, arguments in ( - ("morning", "Send greeting message in the morning", ("--test",)), - ("lunch", "Send message at lunch time", ("--test",)), - ("back", "Send message when returning to work", ("--test",)), - ("bye", "Send goodbye message", ("--test",)), - ): - command_parser = command_subparsers.add_parser(command, help=command_help) - for argument in arguments: - command_parser.add_argument(argument, **command_arguments[argument]) - - args = vars(parser.parse_args()) - anwesenheit = Anwesenheit() - fn = getattr(anwesenheit, args.pop("command").replace("-", "_")) - fn(**args) - - -if __name__ == "__main__": - main() diff --git a/pkgs/vesc-tool/firmware.nix b/pkgs/vesc-tool/firmware.nix deleted file mode 100644 index cb16051..0000000 --- a/pkgs/vesc-tool/firmware.nix +++ /dev/null @@ -1,46 +0,0 @@ -{ lib -, stdenv -, fetchFromGitHub -, gcc-arm-embedded-7 -, python311 -, git -}: - -stdenv.mkDerivation rec { - pname = "vesc-firmware"; - version = "master"; - - src = fetchFromGitHub { - owner = "vedderb"; - repo = "bldc"; - rev = "6.00"; - sha256 = "XeIS+3SOKeTkbsx/aCy0FRw/LVXBbLpAqz6upN5HJUY="; - #rev = "5b6cc075d3cfa62bd52a05fad0195fae114d914c"; - #sha256 = "SB/mQEYY2Bi2xl5qniKdShMQ0vdrVuLtiWWH4D1U0tw="; - fetchSubmodules = true; - }; - - nativeBuildInputs = [ - gcc-arm-embedded-7 - python311 - git - # bash - # libsForQt5.qmake - # libsForQt5.qtconnectivity - # libsForQt5.qtgamepad - # libsForQt5.qtlocation - # libsForQt5.qtquickcontrols2 - # libsForQt5.qtserialport - ]; - - buildCommand = '' - cp -r $src bldc - ( - cd bldc - chmod +w . - #make all_fw_package - make fw_410 - ) - cp -r bldc/build/* $out - ''; -} diff --git a/pkgs/vesc-tool/tool.nix b/pkgs/vesc-tool/tool.nix deleted file mode 100644 index 2a23fa3..0000000 --- a/pkgs/vesc-tool/tool.nix +++ /dev/null @@ -1,70 +0,0 @@ -{ lib -, stdenv -, fetchFromGitHub -, libsForQt5 -, vesc-firmware -, writeText -, qt5 -}: - -let - qresource = writeText "res_fw.qrc" - '' - - - 410.bin - - - ''; -in -stdenv.mkDerivation rec { - pname = "vesc_tool"; - version = "master"; - - src = fetchFromGitHub { - owner = "vedderb"; - repo = "vesc_tool"; - rev = "6d9d6d7f17c8756a272c267659849ea9c9404efa"; - sha256 = "EXXWAwX3yPirhe83wz5/vEectieHeUAkIla7ICxzMow="; - fetchSubmodules = true; - }; - - nativeBuildInputs = [ - libsForQt5.qmake - qt5.wrapQtAppsHook - ]; - - buildInputs = [ - vesc-firmware - ]; - - propagatedBuildInputs = with qt5; [ - qtconnectivity - qtgamepad - qtlocation - qtquickcontrols2 - qtserialport - ]; - - buildPhase = '' - tempdir="$(mktemp -d)" - cp -r "$src/." "$tempdir/" - chmod -R +w "$tempdir" - cd "$tempdir" - - mkdir -p res/firmwares/ - cp ${qresource} res/firmwares/res_fw.qrc - cp ${vesc-firmware}/*.bin res/firmwares - - qmake -config release "CONFIG += release_lin build_platinum" - make clean - make -j8 - rm -rf build/lin/obj - mkdir -p $out/bin - cp build/lin/* $out/bin - ''; - - postFixup = '' - wrapQtApp $out/bin/vesc_tool_6.02 - ''; -} diff --git a/pkgs/vim-fluid/default.nix b/pkgs/vim-fluid/default.nix new file mode 100644 index 0000000..35f232f --- /dev/null +++ b/pkgs/vim-fluid/default.nix @@ -0,0 +1,12 @@ +{ buildVimPlugin, fetchFromGitHub }: +buildVimPlugin { + pname = "vim-fluid"; + version = "0.0.1"; + src = fetchFromGitHub { + owner = "mipmip"; + repo = "vim-fluid"; + rev = "cedc4ad871941e8f7134d1d71f9434f1bc3d93d5"; + sha256 = "sha256-LiS2Dqw1K1Fu5VfHQnxIBDxDzEarmSAUUavQcwHRDsQ="; + }; + meta.homepage = "https://github.com/mipmip/vim-fluid"; +} diff --git a/pkgs/vim-typoscript/default.nix b/pkgs/vim-typoscript/default.nix new file mode 100644 index 0000000..ab85d79 --- /dev/null +++ b/pkgs/vim-typoscript/default.nix @@ -0,0 +1,13 @@ +{ buildVimPlugin, fetchFromGitHub }: +buildVimPlugin rec { + pname = "vim-typoscript"; + version = "2.0.0"; + src = fetchFromGitHub { + owner = "DanielSiepmann"; + repo = "mirror-vim.typoscript"; + rev = "v${version}"; + sha256 = "sha256-fCB+ikDmkfEP/W0pFYGrsZiH30vT0g3z6GZpRGk0Rhc="; + }; + meta.homepage = "https://git.daniel-siepmann.de/danielsiepmann/vim-syntax-typoscript"; +} + diff --git a/pkgs/vodafone-station-exporter/default.nix b/pkgs/vodafone-station-exporter/default.nix new file mode 100644 index 0000000..9a80e97 --- /dev/null +++ b/pkgs/vodafone-station-exporter/default.nix @@ -0,0 +1,12 @@ +{ buildGoApplication, fetchgit }: + +buildGoApplication { + pname = "vodafone-station-exporter"; + version = "0.0.1"; + src = fetchgit { + url = "https://git.jalr.de/jalr/vodafone-station-exporter"; + rev = "808564b940c3570e3b32ce60657bf83fda75ec3c"; + hash = "sha256-A3Behy8Q7bhYXoGUsZXzIAQd/dTXH4d4wd+FDYuD7tE="; + }; + modules = ./gomod2nix.toml; +} diff --git a/pkgs/vodafone-station-exporter/gomod2nix.toml b/pkgs/vodafone-station-exporter/gomod2nix.toml new file mode 100644 index 0000000..293cd7e --- /dev/null +++ b/pkgs/vodafone-station-exporter/gomod2nix.toml @@ -0,0 +1,42 @@ +schema = 3 + +[mod] + [mod."github.com/beorn7/perks"] + version = "v1.0.1" + hash = "sha256-h75GUqfwJKngCJQVE5Ao5wnO3cfKD9lSIteoLp/3xJ4=" + [mod."github.com/cespare/xxhash/v2"] + version = "v2.3.0" + hash = "sha256-7hRlwSR+fos1kx4VZmJ/7snR7zHh8ZFKX+qqqqGcQpY=" + [mod."github.com/kr/text"] + version = "v0.2.0" + hash = "sha256-fadcWxZOORv44oak3jTxm6YcITcFxdGt4bpn869HxUE=" + [mod."github.com/munnerz/goautoneg"] + version = "v0.0.0-20191010083416-a7dc8b61c822" + hash = "sha256-79URDDFenmGc9JZu+5AXHToMrtTREHb3BC84b/gym9Q=" + [mod."github.com/prometheus/client_golang"] + version = "v1.23.2" + hash = "sha256-3GD4fBFa1tJu8MS4TNP6r2re2eViUE+kWUaieIOQXCg=" + [mod."github.com/prometheus/client_model"] + version = "v0.6.2" + hash = "sha256-q6Fh6v8iNJN9ypD47LjWmx66YITa3FyRjZMRsuRTFeQ=" + [mod."github.com/prometheus/common"] + version = "v0.66.1" + hash = "sha256-bqHPaV9IV70itx63wqwgy2PtxMN0sn5ThVxDmiD7+Tk=" + [mod."github.com/prometheus/procfs"] + version = "v0.16.1" + hash = "sha256-OBCvKlLW2obct35p0L9Q+1ZrxZjpTmbgHMP2rng9hpo=" + [mod."go.yaml.in/yaml/v2"] + version = "v2.4.2" + hash = "sha256-oC8RWdf1zbMYCtmR0ATy/kCkhIwPR9UqFZSMOKLVF/A=" + [mod."golang.org/x/crypto"] + version = "v0.42.0" + hash = "sha256-qa6cGxZUhVnbkpVzfvLGQQsl/NCqNceJp9SIx5vkyiI=" + [mod."golang.org/x/exp"] + version = "v0.0.0-20250911091902-df9299821621" + hash = "sha256-cSDirFex900mrckzB3fe18hW2Vk4/y4xKvlUWq3yoDA=" + [mod."golang.org/x/sys"] + version = "v0.36.0" + hash = "sha256-9h4SHGnlJzmTENUp6226hC8fQ73QrQC3D85NNMxLuXg=" + [mod."google.golang.org/protobuf"] + version = "v1.36.8" + hash = "sha256-yZN8ZON0b5HjUNUSubHst7zbvnMsOzd81tDPYQRtPgM=" diff --git a/pkgs/wofi-bluetooth/wofi-bluetooth.nix b/pkgs/wofi-bluetooth/wofi-bluetooth.nix new file mode 100644 index 0000000..5f6e408 --- /dev/null +++ b/pkgs/wofi-bluetooth/wofi-bluetooth.nix @@ -0,0 +1,30 @@ +{ lib +, stdenv +, makeWrapper +, bluez +, wofi +, rofi-bluetooth +}: + +stdenv.mkDerivation rec { + pname = "wofi-bluetooth"; + inherit (rofi-bluetooth) version; + inherit (rofi-bluetooth) src; + patches = [ + ./wofi-bluetooth.patch + ]; + buildInputs = [ wofi ]; + nativeBuildInputs = [ makeWrapper ]; + installPhase = '' + runHook preInstall + + install -D --target-directory=$out/bin/ ./rofi-bluetooth + + mv $out/bin/rofi-bluetooth $out/bin/wofi-bluetooth + + wrapProgram $out/bin/wofi-bluetooth \ + --prefix PATH ":" ${lib.makeBinPath [ bluez wofi ] } + + runHook postInstall + ''; +} diff --git a/pkgs/wofi-bluetooth/wofi-bluetooth.patch b/pkgs/wofi-bluetooth/wofi-bluetooth.patch new file mode 100644 index 0000000..3150f7a --- /dev/null +++ b/pkgs/wofi-bluetooth/wofi-bluetooth.patch @@ -0,0 +1,68 @@ +--- a/rofi-bluetooth ++++ b/rofi-bluetooth +@@ -7,14 +7,14 @@ + # + # Author: Nick Clyde (clydedroid) + # +-# A script that generates a rofi menu that uses bluetoothctl to ++# A script that generates a wofi menu that uses bluetoothctl to + # connect to bluetooth devices and display status info. + # + # Inspired by networkmanager-dmenu (https://github.com/firecat53/networkmanager-dmenu) + # Thanks to x70b1 (https://github.com/polybar/polybar-scripts/tree/master/polybar-scripts/system-bluetooth-bluetoothctl) + # + # Depends on: +-# Arch repositories: rofi, bluez-utils (contains bluetoothctl) ++# Arch repositories: wofi, bluez-utils (contains bluetoothctl) + + # Constants + divider="---------" +@@ -231,8 +231,8 @@ + trusted=$(device_trusted "$mac") + options="$connected\n$paired\n$trusted\n$divider\n$goback\nExit" + +- # Open rofi menu, read chosen option +- chosen="$(echo -e "$options" | $rofi_command "$device_name")" ++ # Open wofi menu, read chosen option ++ chosen="$(echo -e "$options" | $wofi_command "$device_name")" + + # Match chosen option to command + case "$chosen" in +@@ -254,7 +254,7 @@ + esac + } + +-# Opens a rofi menu with current bluetooth status and options to connect ++# Opens a wofi menu with current bluetooth status and options to connect + show_menu() { + # Get menu options + if power_on; then +@@ -269,15 +269,16 @@ + pairable=$(pairable_on) + discoverable=$(discoverable_on) + +- # Options passed to rofi ++ # Options passed to wofi + options="$devices\n$divider\n$power\n$scan\n$pairable\n$discoverable\nExit" + else + power="Power: off" + options="$power\nExit" + fi + +- # Open rofi menu, read chosen option +- chosen="$(echo -e "$options" | $rofi_command "Bluetooth")" ++ lines="$(echo -e "$options" | wc -l)" ++ # Open wofi menu, read chosen option ++ chosen="$(echo -e "$options" | $wofi_command "Bluetooth" -L "$lines")" + + # Match chosen option to command + case "$chosen" in +@@ -305,7 +306,7 @@ + } + + # Rofi command to pipe into, can add any options here +-rofi_command="rofi -dmenu $* -p" ++wofi_command="wofi --color=$HOME/.config/wofi/color -d -i -p " + + case "$1" in + --status) diff --git a/users/README.md b/users/README.md new file mode 100644 index 0000000..50675ba --- /dev/null +++ b/users/README.md @@ -0,0 +1,7 @@ +# Home Manager +The user configuration is managed by [Home Manager](https://github.com/nix-community/home-manager) + +For a systematic overview of Home Manager and its available options, please see +- the [Home Manager manual](https://nix-community.github.io/home-manager/index.html) and +- the [Home Manager configuration options](https://nix-community.github.io/home-manager/options.html). + diff --git a/users/jalr/default.nix b/users/jalr/default.nix new file mode 100644 index 0000000..ac85cd2 --- /dev/null +++ b/users/jalr/default.nix @@ -0,0 +1,206 @@ +{ config, pkgs, ... }: + +let + sshKeys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH3l+Yixrsjhze20CSjvUK4Qj/BNqbTNitgk20vuzPej cardno:25_750_479" + ]; +in +{ + users.users.jalr = { + isNormalUser = true; + extraGroups = [ + "adbusers" + "audio" + "dialout" + "docker" + "libvirtd" + "lp" + "networkmanager" + "plugdev" + "scanner" + "video" + "wheel" + "wireshark" + ]; # Enable ‘sudo’ for the user. + shell = pkgs.fish; + openssh.authorizedKeys.keys = sshKeys; + }; + + users.users.root.openssh.authorizedKeys.keys = sshKeys; + + home-manager = { + useUserPackages = true; + useGlobalPkgs = true; + backupFileExtension = "hm.bak"; + users.jalr = { pkgs, ... }: { + imports = [ ./modules ]; + config = { + home.stateVersion = config.system.stateVersion; + + home.packages = with pkgs; [ + cutecom + ghostscript + newsboat + pdftk + platformio + ptouch-print + qrencode + sshfs + tmate + + # common + asciinema + bat + envsubst + gnupg + nmap + psutils + pwgen + ]; + + xdg.mimeApps = { + enable = true; + defaultApplications = { + "application/pdf" = "org.gnome.Evince.desktop"; + + "image/svg+xml" = "org.inkscape.Inkscape.desktop"; + + "x-scheme-handler/http" = "firefox-esr.desktop"; + "x-scheme-handler/https" = "firefox-esr.desktop"; + + "x-scheme-handler/mailto" = "thunderbird.desktop"; + }; + }; + + accounts.email.accounts = { + "jalr" = { + primary = true; + userName = "jalr@jalr.de"; + address = "jalr@jalr.de"; + realName = "Jakob Lechner"; + imap = { + host = "hha.jalr.de"; + port = 143; + tls = { + enable = true; + useStartTls = true; + }; + }; + smtp = { + host = "hha.jalr.de"; + port = 465; + tls = { + enable = true; + useStartTls = false; + }; + }; + thunderbird = { + enable = true; + profiles = [ "default" ]; + }; + }; + "Digitaler Dienst" = { + userName = "j.lechner@digitaler-dienst.gmbh"; + address = "j.lechner@digitaler-dienst.gmbh"; + realName = "Jakob Lechner"; + imap = { + host = "mail.agenturserver.de"; + port = 143; + tls = { + enable = true; + useStartTls = true; + }; + }; + smtp = { + host = "mail.agenturserver.de"; + port = 465; + tls = { + enable = true; + useStartTls = false; + }; + }; + thunderbird = { + enable = true; + profiles = [ "default" ]; + }; + }; + "Digitaler Dienst info" = { + userName = "info@digitaler-dienst.gmbh"; + address = "info@digitaler-dienst.gmbh"; + realName = "Digitaler Dienst"; + imap = { + host = "mail.agenturserver.de"; + port = 143; + tls = { + enable = true; + useStartTls = true; + }; + }; + smtp = { + host = "mail.agenturserver.de"; + port = 587; + tls = { + enable = true; + useStartTls = true; + }; + }; + thunderbird = { + enable = true; + profiles = [ "default" ]; + }; + }; + "FabLab NEA" = { + userName = "kontakt@fablab-nea.de"; + address = "kontakt@fablab-nea.de"; + realName = "FabLab NEA"; + imap = { + host = "hha.jalr.de"; + port = 143; + tls = { + enable = true; + useStartTls = true; + }; + }; + smtp = { + host = "hha.jalr.de"; + port = 465; + tls = { + enable = true; + useStartTls = false; + }; + }; + thunderbird = { + enable = true; + profiles = [ "default" ]; + }; + }; + "Weinturm Open Air - IT" = { + userName = "it@weinturm-open-air.de"; + address = "it@weinturm-open-air.de"; + realName = "Weinturm Open Air IT"; + imap = { + host = "mail.agenturserver.de"; + port = 143; + tls = { + enable = true; + useStartTls = true; + }; + }; + smtp = { + host = "mail.agenturserver.de"; + port = 587; + tls = { + enable = true; + useStartTls = true; + }; + }; + thunderbird = { + enable = true; + profiles = [ "default" ]; + }; + }; + }; + }; + }; + }; +} diff --git a/home-manager/modules/openscad.nix b/users/jalr/modules/3d-modeling.nix similarity index 66% rename from home-manager/modules/openscad.nix rename to users/jalr/modules/3d-modeling.nix index f4b5c6f..aa681d7 100644 --- a/home-manager/modules/openscad.nix +++ b/users/jalr/modules/3d-modeling.nix @@ -1,5 +1,5 @@ { nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.gui.enable { +lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ openscad ]; diff --git a/home-manager/modules/communication/telegram-desktop.nix b/users/jalr/modules/3d-printing.nix similarity index 56% rename from home-manager/modules/communication/telegram-desktop.nix rename to users/jalr/modules/3d-printing.nix index e49471d..5107e9c 100644 --- a/home-manager/modules/communication/telegram-desktop.nix +++ b/users/jalr/modules/3d-printing.nix @@ -1,7 +1,7 @@ { nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.gui.enable { +lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ - tdesktop + prusa-slicer ]; } diff --git a/users/jalr/modules/ardour.nix b/users/jalr/modules/ardour.nix new file mode 100644 index 0000000..4d3d5ac --- /dev/null +++ b/users/jalr/modules/ardour.nix @@ -0,0 +1,8 @@ +{ nixosConfig, lib, pkgs, ... }: +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = with pkgs; [ + ardour + x42-plugins + ]; + home.sessionVariables.LV2_PATH = "${pkgs.x42-plugins}/lib/lv2/"; +} diff --git a/home-manager/modules/aws.nix b/users/jalr/modules/aws.nix similarity index 74% rename from home-manager/modules/aws.nix rename to users/jalr/modules/aws.nix index 8fa7bd5..2e85f92 100644 --- a/home-manager/modules/aws.nix +++ b/users/jalr/modules/aws.nix @@ -1,10 +1,10 @@ -{ nixosConfig, lib, pkgs, config, ... }: +{ nixosConfig, lib, config, ... }: let - xdg = config.xdg; + inherit (config) xdg; in { - config = lib.mkIf nixosConfig.myConfig.aws.enable { + config = lib.mkIf nixosConfig.jalr.aws.enable { # https://github.com/aws/aws-sdk/issues/30 home.sessionVariables = { AWS_CONFIG_FILE = "${xdg.configHome}/aws/config"; @@ -17,9 +17,9 @@ in xdg.configFile."aws/config".text = lib.generators.toINI { } ( lib.mapAttrs' (name: value: - lib.attrsets.nameValuePair ("profile ${name}") (value) + lib.attrsets.nameValuePair "profile ${name}" value ) - nixosConfig.myConfig.aws.accounts + nixosConfig.jalr.aws.accounts // { "default" = { diff --git a/users/jalr/modules/cli/default.nix b/users/jalr/modules/cli/default.nix new file mode 100644 index 0000000..5ab60f5 --- /dev/null +++ b/users/jalr/modules/cli/default.nix @@ -0,0 +1,28 @@ +{ nixosConfig, pkgs, ... }: +{ + imports = [ + ./htop.nix + ]; + + config = { + home.packages = with pkgs; [ + cached-nix-shell + fd + file + inetutils + jq + lsof + ncdu + ripgrep + tio + unzip + ] ++ (if ! nixosConfig.jalr.workstation.enable then [ ] else [ + dnsutils + screen + speedtest-cli + usbutils + wget + yt-dlp + ]); + }; +} diff --git a/users/jalr/modules/cli/htop.nix b/users/jalr/modules/cli/htop.nix new file mode 100644 index 0000000..cc0b753 --- /dev/null +++ b/users/jalr/modules/cli/htop.nix @@ -0,0 +1,24 @@ +{ nixosConfig +, config +, lib +, ... +}: + +{ + programs.htop = { + enable = true; + settings = { + color_scheme = 6; + } // (with config.lib.htop; leftMeters ([ + (bar "LeftCPUs") + (bar "Memory") + ] ++ lib.lists.optional nixosConfig.zramSwap.enable (bar "Zram") ++ lib.lists.optional (nixosConfig.swapDevices != [ ]) (bar "Swap") ++ [ + (bar "DiskIO") + ])) // (with config.lib.htop; rightMeters [ + (bar "RightCPUs") + (text "Tasks") + (text "LoadAverage") + (text "NetworkIO") + ]); + }; +} diff --git a/users/jalr/modules/communication/default.nix b/users/jalr/modules/communication/default.nix new file mode 100644 index 0000000..f461097 --- /dev/null +++ b/users/jalr/modules/communication/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./ferdium.nix + ./mumble.nix + ]; +} diff --git a/users/jalr/modules/communication/ferdium.nix b/users/jalr/modules/communication/ferdium.nix new file mode 100644 index 0000000..1d0b4d5 --- /dev/null +++ b/users/jalr/modules/communication/ferdium.nix @@ -0,0 +1,14 @@ +{ nixosConfig, lib, ... }: + +lib.mkIf nixosConfig.jalr.gui.enable { + xdg.desktopEntries.Ferdium = { + name = "Ferdium"; + exec = "flatpak run --branch=stable --arch=x86_64 --command=ferdium --file-forwarding org.ferdium.Ferdium @@u %U @@"; + terminal = false; + type = "Application"; + icon = "org.ferdium.Ferdium"; + comment = "Desktop app bringing all your messaging services into one installable"; + mimeType = [ "x-scheme-handler/ferdium" ]; + categories = [ "Network" "InstantMessaging" ]; + }; +} diff --git a/home-manager/modules/communication/mumble.nix b/users/jalr/modules/communication/mumble.nix similarity index 65% rename from home-manager/modules/communication/mumble.nix rename to users/jalr/modules/communication/mumble.nix index 3d873b3..4fb6026 100644 --- a/home-manager/modules/communication/mumble.nix +++ b/users/jalr/modules/communication/mumble.nix @@ -1,6 +1,6 @@ { nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.gui.enable { +lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ mumble ]; diff --git a/users/jalr/modules/dconf.nix b/users/jalr/modules/dconf.nix new file mode 100644 index 0000000..eed759c --- /dev/null +++ b/users/jalr/modules/dconf.nix @@ -0,0 +1,15 @@ +{ lib, nixosConfig, ... }: + +lib.mkIf nixosConfig.jalr.gui.enable { + dconf.settings = { + "org/gnome/desktop/input-sources" = { + xkb-options = [ "grp:win_space_toggle" ]; + show-all-sources = true; + sources = [ + (lib.hm.gvariant.mkTuple [ "xkb" "de" ]) + (lib.hm.gvariant.mkTuple [ "xkb" "de+neo" ]) + (lib.hm.gvariant.mkTuple [ "xkb" "us" ]) + ]; + }; + }; +} diff --git a/users/jalr/modules/ddev.nix b/users/jalr/modules/ddev.nix new file mode 100644 index 0000000..2e154d2 --- /dev/null +++ b/users/jalr/modules/ddev.nix @@ -0,0 +1,6 @@ +{ nixosConfig, lib, pkgs, ... }: +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = [ + pkgs.master.ddev + ]; +} diff --git a/users/jalr/modules/default.nix b/users/jalr/modules/default.nix new file mode 100644 index 0000000..f97916b --- /dev/null +++ b/users/jalr/modules/default.nix @@ -0,0 +1,51 @@ +{ + imports = [ + ./3d-modeling.nix + ./3d-printing.nix + ./ardour.nix + ./aws.nix + ./cli + ./communication + ./dconf.nix + ./ddev.nix + ./direnv.nix + ./do-not-disturb + ./dynamic-colors.nix + ./firefox + ./fish.nix + ./fpv.nix + ./freetube.nix + ./git.nix + ./gnuradio.nix + ./graphics + ./gui.nix + ./jameica.nix + ./kicad.nix + ./lsd + ./mixxc + ./mpv.nix + ./mute-indicator.nix + ./mycli + ./neovim + ./nix-index.nix + ./obs-studio + ./ots.nix + ./pace.nix + ./pass.nix + ./pomodoro.nix + ./python.nix + ./remarkable + ./roomeqwizard.nix + ./snapclient.nix + ./sound + ./sway + ./thunar.nix + ./thunderbird.nix + ./tmux.nix + ./tor-browser.nix + ./trilium.nix + ./vdirsyncer.nix + ./vesc-tool.nix + ./wezterm.nix + ]; +} diff --git a/home-manager/modules/direnv.nix b/users/jalr/modules/direnv.nix similarity index 100% rename from home-manager/modules/direnv.nix rename to users/jalr/modules/direnv.nix diff --git a/users/jalr/modules/do-not-disturb/android-set-dnd.py b/users/jalr/modules/do-not-disturb/android-set-dnd.py new file mode 100755 index 0000000..13a7eba --- /dev/null +++ b/users/jalr/modules/do-not-disturb/android-set-dnd.py @@ -0,0 +1,28 @@ +import os +import sys +from urllib import request + + +def read_url(): + try: + with open(os.getenv("NTFY_URL_FILE")) as f: + return f.read() + except FileNotFoundError: + return None + + +def set_android_dnd(active: bool): + url = read_url() + if url is not None: + request.urlopen( + request.Request( + read_url(), + method="POST", + data=("DND on" if active else "DND off").encode(), + ) + ) + + +if __name__ == "__main__": + _, state = sys.argv + set_android_dnd(state == "on") diff --git a/users/jalr/modules/do-not-disturb/default.nix b/users/jalr/modules/do-not-disturb/default.nix new file mode 100644 index 0000000..29b0c2f --- /dev/null +++ b/users/jalr/modules/do-not-disturb/default.nix @@ -0,0 +1,29 @@ +{ nixosConfig, lib, pkgs, ... }: + +let + androidSetDnd = pkgs.writeScript "android-set-dnd" '' + #!${pkgs.python3}/bin/python3 + ${builtins.readFile ./android-set-dnd.py} + ''; + do-not-disturb = pkgs.writeShellScriptBin "dnd" '' + export PATH=${pkgs.lib.makeBinPath [pkgs.mako]} + + if [[ "$1" != off && "$1" != on ]]; then + echo "USAGE: $0 [on|off]" >&2 + exit 1 + fi + + export NTFY_URL_FILE=/run/secrets/ntfy_shiftphone + ${androidSetDnd} $1 + + if [[ $1 == on ]]; then + makoctl mode -a dnd > /dev/null + else + makoctl mode -r dnd > /dev/null + fi + ''; + +in +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = [ do-not-disturb ]; +} diff --git a/users/jalr/modules/dynamic-colors.nix b/users/jalr/modules/dynamic-colors.nix new file mode 100644 index 0000000..c97e9ed --- /dev/null +++ b/users/jalr/modules/dynamic-colors.nix @@ -0,0 +1,141 @@ +{ nixosConfig, lib, pkgs, ... }: + +let + loadSwayTheme = pkgs.writeShellScript "load-sway-theme" '' + while IFS= read -r line; do + ${pkgs.sway}/bin/swaymsg "$line" + done < "$1" + ''; + applicationConfig = [ + { + dir = "~/.config/wofi"; + light = "color-light"; + dark = "color-dark"; + target = "color"; + } + { + dir = "~/.config/sway"; + light = "light-theme"; + dark = "dark-theme"; + target = "theme"; + exec = [ loadSwayTheme "theme" ]; + } + { + dir = "~/.config/waybar"; + light = "theme-light.css"; + dark = "theme-dark.css"; + target = "theme.css"; + exec = [ "${pkgs.systemd}/bin/systemctl" "--user" "restart" "waybar.service" ]; + } + { + dir = "~/.config/mycli"; + light = "theme-light.ini"; + dark = "theme-dark.ini"; + target = "myclirc"; + } + { + exec = + if nixosConfig.jalr.gui.enable + then [ "/usr/bin/env" "gsettings" "set" "org.gnome.desktop.interface" "color-scheme" "prefer-%scheme%" ] + else null; + } + { + exec = + if nixosConfig.jalr.gui.enable + then [ "/usr/bin/env" "gsettings" "set" "org.gnome.desktop.interface" "gtk-theme" "Adwaita-%scheme%" ] + else null; + } + { + dir = "~/.config/lsd"; + light = "colors-light.yaml"; + dark = "colors-dark.yaml"; + target = "colors.yaml"; + } + ]; + dynamic-colors = pkgs.writers.writePython3Bin "dynamic-colors" { } '' + import json + import os + import pathlib + import subprocess + import sys + + + def main(): + DEFAULT_SCHEME = 'light' + CONFIG_FILE = "~/.config/dynamic-colors/config.json" + with open(pathlib.Path(CONFIG_FILE).expanduser(), "r") as fh: + config = json.load(fh) + + command, = sys.argv[1:] + + scheme = None + if command in ('light', 'dark'): + scheme = command + elif command == 'install': + pass + else: + raise NotImplementedError + + for entry in config: + directory = ( + pathlib.Path(entry['dir']).expanduser() if 'dir' in entry else None + ) + if all(key in entry for key in ( + 'target', + 'light', + 'dark' + )): + target = directory.joinpath(entry['target']) + + if scheme is None: + if target.exists(): + continue + scheme = DEFAULT_SCHEME + else: + if target.exists() and target.is_symlink: + os.remove(target) + + src = { + 'light': entry['light'], + 'dark': entry['dark'], + }[scheme] + + try: + os.symlink(src, target) + except FileNotFoundError: + pass + + if entry.get('exec') is not None: + command, *args = entry["exec"] + args = [ + arg.replace( + '%scheme%', scheme + if scheme is not None + else DEFAULT_SCHEME + ) + for arg in args + ] + print(command, *args) + try: + subprocess.run( + (command, *args), + cwd=directory + ) + except FileNotFoundError: + pass + + + if __name__ == '__main__': + main() + ''; +in +{ + xdg.configFile."dynamic-colors/config.json" = { + text = lib.generators.toJSON { } applicationConfig; + onChange = "${dynamic-colors}/bin/dynamic-colors install"; + }; + + home.packages = [ + dynamic-colors + ]; +} diff --git a/users/jalr/modules/firefox/default.nix b/users/jalr/modules/firefox/default.nix new file mode 100644 index 0000000..62b3eb5 --- /dev/null +++ b/users/jalr/modules/firefox/default.nix @@ -0,0 +1,385 @@ +{ nixosConfig, pkgs, ... }: +{ + programs.firefox = { + inherit (nixosConfig.jalr.gui) enable; + package = pkgs.firefox-esr.override { + nativeMessagingHosts = [ + pkgs.browserpass + ]; + }; + policies = { + AllowedDomainsForApps = ""; + CaptivePortal = false; + DNSOverHTTPS.Enabled = false; + DisableAppUpdate = true; + DisableFeedbackCommands = true; + DisableFirefoxAccounts = true; + DisableFirefoxScreenshots = true; + DisableFirefoxStudies = true; + DisablePocket = true; + DisableTelemetry = true; + DisplayBookmarksToolbar = "newtab"; + DisplayMenuBar = "never"; + EncryptedMediaExtensions = { Enabled = false; Locked = true; }; + NoDefaultBookmarks = true; + OfferToSaveLogins = false; + StartDownloadsInTempDirectory = true; + UserMessaging = { + WhatsNew = false; + ExtensionRecommendations = false; + FeatureRecommendations = false; + UrlbarInterventions = false; + SkipOnboarding = true; + MoreFromMozilla = false; + Locked = false; + }; + Permissions = { + Camera = { + /* + Allow = ["https://example.org" "https://example.org:1234"]; + Block = ["https://example.edu"]; + BlockNewRequests = true | false; + Locked = true | false; + */ + }; + Microphone = { }; + Location = { }; + Notifications = { }; + Autoplay = { }; + }; + PopupBlocking = { + /* Allow = ["http://example.org/" "http://example.edu/"]; */ + Default = false; + Locked = false; + }; + Bookmarks = ( + builtins.map + (b: b // { + Folder = "Nix"; + Placement = "toolbar"; + }) [ + { + Title = "NixOS Manual"; + URL = "https://nixos.org/manual/nixos/stable/"; + } + { + Title = "Nix manual"; + URL = "https://nix.dev/manual/nix/2.18/stable"; + } + { + Title = "Nixpkgs manual"; + URL = "https://nixos.org/manual/nixpkgs/stable/"; + } + { + Title = "Noogle"; + URL = "https://noogle.dev/"; + } + { + Title = "Home Manager Configuration Options"; + URL = "https://nix-community.github.io/home-manager/options.xhtml"; + } + { + Title = "Home Manager Option Search"; + URL = "https://mipmip.github.io/home-manager-option-search/"; + } + { + Title = "NixOS Status"; + URL = "https://status.nixos.org/"; + } + { + Title = "krops"; + URL = "https://cgit.krebsco.de/krops/about/"; + } + { + Title = "Awesome Nix"; + URL = "https://github.com/nix-community/awesome-nix"; + } + ] + ) ++ ( + builtins.map + (b: b // { + Folder = "Digitaler Dienst"; + Placement = "toolbar"; + }) [ + { + Title = "GitLab"; + URL = "https://gitlab.digitaler-dienst.net/"; + } + { + Title = "Moco"; + URL = "https://digitaler-dienst.mocoapp.com/activities"; + } + { + Title = "Leantime"; + URL = "https://todo.digitaler-dienst.gmbh/"; + } + { + Title = "Nextcloud"; + URL = "https://nx52865.your-storageshare.de/"; + } + { + Title = "FreeScout"; + URL = "https://tickets.digitaler-dienst.gmbh/"; + } + { + Title = "Personio"; + URL = "https://laemmermann.personio.de/"; + } + ] + ) ++ [ + { + Title = "Fefes Blog"; + URL = "https://blog.fefe.de"; + Placement = "toolbar"; + #Placement = "menu"; + #Favicon = "https://example.com/favicon.ico"; + } + ]; + /* + ManagedBookmarks = [ + { + toplevel_name = "My managed bookmarks folder"; + } + { + url = "example.com"; + name = "Example"; + } + { + name = "Mozilla links"; + children = [ + { + url = "https://mozilla.org"; + name = "Mozilla.org"; + } + { + url = "https://support.mozilla.org/"; + name = "SUMO"; + } + ]; + } + ]; + */ + SearchEngines = { + Default = "DuckDuckGo"; + Remove = [ + "Google" + "Wikipedia (en)" + ]; + Add = [ + { + Name = "Startpage"; + URLTemplate = "https://www.startpage.com/sp/search"; + Method = "POST"; + PostData = "qadf=none&query={searchTerms}"; + IconURL = "https://www.startpage.com/sp/cdn/favicons/mobile/android-icon-192x192.png"; + Alias = "sp"; + } + { + Name = "DuckDuckGo"; + URLTemplate = "https://duckduckgo.com/?q={searchTerms}"; + Method = "GET"; + IconURL = "https://duckduckgo.com/favicon.ico"; + Alias = "ddg"; + } + + # Wikipedia + { + Name = "Wikipedia en"; + URLTemplate = "https://en.wikipedia.org/wiki/Special:Search?search={searchTerms}"; + Method = "GET"; + IconURL = "https://en.wikipedia.org/static/images/icons/wikipedia.png"; + Alias = "wen"; + } + { + Name = "Wikipedia de"; + URLTemplate = "https://de.wikipedia.org/w/index.php?search={searchTerms}"; + Method = "GET"; + IconURL = "https://www.wikipedia.de/img/wikipedia.png"; + Alias = "wde"; + } + { + Name = "Nix Packages"; + URLTemplate = "https://search.nixos.org/packages?query={searchTerms}"; + Method = "GET"; + IconURL = "https://nixos.org/favicon.png"; + Alias = "pkg"; + } + { + Name = "NixOS Options"; + URLTemplate = "https://search.nixos.org/options?query={searchTerms}"; + Method = "GET"; + IconURL = "https://nixos.org/favicon.png"; + Alias = "opt"; + } + { + Name = "Docker images"; + URLTemplate = "https://hub.docker.com/search/?q={searchTerms}"; + Method = "GET"; + IconURL = "https://hub.docker.com/favicon.ico"; + Alias = "docker"; + } + { + Name = "GitHub"; + URLTemplate = "https://github.com/search?q={searchTerms}"; + Method = "GET"; + IconURL = "https://github.githubassets.com/favicons/favicon.svg"; + Alias = "gh"; + } + + # Shopping + { + Name = "Amazon de"; + URLTemplate = "https://www.amazon.de/s?k={searchTerms}"; + Method = "GET"; + IconURL = "https://www.amazon.de/favicon.ico"; + Alias = "amde"; + } + { + Name = "Ebay de"; + URLTemplate = "https://www.ebay.de/sch/i.html?_nkw={searchTerms}"; + Method = "GET"; + IconURL = "https://pages.ebay.com/favicon.ico"; + Alias = "ebde"; + } + + # Dictionary + { + Name = "dict.cc"; + URLTemplate = "https://www.dict.cc/?s={searchTerms}"; + Method = "GET"; + IconURL = "https://www4.dict.cc/img/favicons/favicon4.png"; + Alias = "dcc"; + } + { + Name = "Duden"; + URLTemplate = "https://www.duden.de/suchen/dudenonline/{searchTerms}"; + Method = "GET"; + IconURL = "https://www.duden.de/sites/default/res/apple-touch-icon/180x180.png"; + Alias = "duden"; + } + + # Map + { + Name = "OpenStreetMap"; + URLTemplate = "https://www.openstreetmap.org/search?query={searchTerms}"; + Method = "GET"; + IconURL = "https://www.openstreetmap.org/assets/favicon-194x194-79d3fb0152c735866e64b1d7535d504483cd13c2fad0131a6142bd9629d30de2.png"; + Alias = "osm"; + } + ]; + }; + }; + profiles.default = { + id = 0; + isDefault = true; + extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [ + browserpass + darkreader + sponsorblock + (tree-style-tab.override { + version = "4.1.6"; + url = "https://addons.mozilla.org/firefox/downloads/file/4488104/tree_style_tab-4.1.6.xpi"; + sha256 = "sha256-X0HC6jzytjBsM+8HmbK48DUihtdN9oCsqLUJqp29csQ="; + }) + ublock-origin + umatrix + violentmonkey + xdebug-helper-for-firefox + youtube-shorts-block + ]; + settings = { + #"browser.startup.homepage" = "https://nixos.org"; + #"browser.search.region" = "GB"; + #"browser.search.isUS" = false; + #"distribution.searchplugins.defaultLocale" = "en-GB"; + #"general.useragent.locale" = "en-GB"; + #"browser.bookmarks.showMobileBookmarks" = true; + "app.normandy.enabled" = false; + "app.shield.optoutstudies.enabled" = false; + "app.update.auto" = false; + "browser.bookmarks.addedImportButton" = false; + "browser.ctrlTab.sortByRecentlyUsed" = true; + "browser.fixup.alternate.enabled" = false; + "browser.formfill.enable" = false; + "browser.link.open_newwindow.restriction" = 0; + "browser.newtabpage.enabled" = false; + "browser.ping-centre.telemetry" = false; + "browser.safebrowsing.downloads.enabled" = false; + "browser.safebrowsing.downloads.remote.block_dangerous" = false; + "browser.safebrowsing.downloads.remote.block_dangerous_host" = false; + "browser.safebrowsing.downloads.remote.block_potentially_unwanted" = false; + "browser.safebrowsing.downloads.remote.block_uncommon" = false; + "browser.safebrowsing.downloads.remote.enabled" = false; + "browser.safebrowsing.downloads.remote.url" = ""; + "browser.safebrowsing.malware.enabled" = false; + "browser.safebrowsing.phishing.enabled" = false; + "browser.safebrowsing.provider.google.advisoryURL" = ""; + "browser.safebrowsing.provider.google.gethashURL" = ""; + "browser.safebrowsing.provider.google.lists" = ""; + "browser.safebrowsing.provider.google.reportMalwareMistakeURL" = ""; + "browser.safebrowsing.provider.google.reportPhishMistakeURL" = ""; + "browser.safebrowsing.provider.google.reportURL" = ""; + "browser.safebrowsing.provider.google.updateURL" = ""; + "browser.safebrowsing.provider.google4.advisoryURL" = ""; + "browser.safebrowsing.provider.google4.dataSharingURL" = ""; + "browser.safebrowsing.provider.google4.gethashURL" = ""; + "browser.safebrowsing.provider.google4.lists" = ""; + "browser.safebrowsing.provider.google4.reportMalwareMistakeURL" = ""; + "browser.safebrowsing.provider.google4.reportPhishMistakeURL" = ""; + "browser.safebrowsing.provider.google4.reportURL" = ""; + "browser.safebrowsing.provider.google4.updateURL" = ""; + "browser.safebrowsing.provider.mozilla.gethashURL" = ""; + "browser.safebrowsing.provider.mozilla.lists" = ""; + "browser.safebrowsing.provider.mozilla.updateURL" = ""; + "browser.search.suggest.enabled" = false; + "browser.search.widget.inNavBar" = true; + "browser.startup.page" = 0; + "extensions.pocket.enabled" = false; + "extensions.update.enabled" = false; + "identity.fxaccounts.enabled" = false; + "keyword.enabled" = false; + "network.captive-portal-service.enabled" = false; + "network.predictor.enabled" = false; + "privacy.donottrackheader.enabled" = true; + "startup.homepage_welcome_url" = "about:blank"; + "toolkit.legacyUserProfileCustomizations.stylesheets" = true; + "toolkit.telemetry.archive.enabled" = false; + "toolkit.telemetry.bhrPing.enabled" = false; + "toolkit.telemetry.firstShutdownPing.enabled" = false; + "toolkit.telemetry.newProfilePing.enabled" = false; + "toolkit.telemetry.server" = "http://127.0.0.1:4711"; + "toolkit.telemetry.server_owner" = ""; + "toolkit.telemetry.shutdownPingSender.enabled" = false; + "toolkit.telemetry.updatePing.enabled" = false; + "urlclassifier.downloadAllowTable" = ""; + "urlclassifier.downloadBlockTable" = ""; + "urlclassifier.malwareTable" = ""; + "urlclassifier.phishTable" = ""; + "datareporting.healthreport.uploadEnabled" = ""; + "app.normandy.api_url" = ""; + "breakpad.reportURL" = ""; + "browser.region.network.url" = ""; + "browser.search.geoSpecificDefaults.url" = ""; + "browser.shell.checkDefaultBrowser" = false; + + "privacy.userContext.enabled" = true; + "privacy.userContext.ui.enabled" = true; + "network.dnsCacheExpiration" = 0; + + # disable disk cache to reduce ssd writes + "browser.cache.disk.enable" = false; + "browser.cache.memory.enable" = true; + "browser.cache.memory.capacity" = -1; + }; + + userChrome = builtins.readFile ./userChrome.css; + }; + }; + + programs.browserpass = { + enable = true; + browsers = [ "firefox" ]; + }; + + xdg.configFile."treestyletab.css".source = ./treestyletab.css; +} diff --git a/users/jalr/modules/firefox/treestyletab.css b/users/jalr/modules/firefox/treestyletab.css new file mode 100644 index 0000000..b5b430a --- /dev/null +++ b/users/jalr/modules/firefox/treestyletab.css @@ -0,0 +1,17 @@ +@media (prefers-color-scheme: light) { + tab-item:not(.active):not(.bundled-active):not(.highlighted), + #background { + --toolbar-non-lwt-bgcolor: ThreeDShadow; + --toolbar-non-lwt-textcolor: ButtonText; + } +} + +@media (prefers-color-scheme: dark) { + #tabbar { + background-color: var(--in-content-page-background); + } +} + +tab-item.coloredTabsHue0 tab-item-substance { + background: transparent; +} diff --git a/home-manager/modules/firefox/userChrome.css b/users/jalr/modules/firefox/userChrome.css similarity index 92% rename from home-manager/modules/firefox/userChrome.css rename to users/jalr/modules/firefox/userChrome.css index dcc6b2b..889027a 100644 --- a/home-manager/modules/firefox/userChrome.css +++ b/users/jalr/modules/firefox/userChrome.css @@ -218,4 +218,28 @@ url(chrome://browser/content/browser.xhtml) { } /*** End of: Megabar Styler One-Offs ***/ + + /* Hide "Firefox Suggest" in location bar search results */ + .urlbarView-row[label="Firefox Suggest"]::before { + display: none !important + } + .urlbarView-row[label] { + margin-block-start: 4px !important; + } + + /* Hide search button in location bar */ + #identity-box[pageproxystate=invalid] > .identity-box-button, + .searchbar-search-button { + display: none + } + + /* Hide search placeholder in location bar */ + #urlbar-input::placeholder { + color: transparent; + } + + /* Hide back & forward buttons */ + toolbarbutton#back-button, toolbarbutton#forward-button { + display: none; + } } diff --git a/users/jalr/modules/fish.nix b/users/jalr/modules/fish.nix new file mode 100644 index 0000000..f929e20 --- /dev/null +++ b/users/jalr/modules/fish.nix @@ -0,0 +1,206 @@ +{ config, pkgs, ... }: +{ + home.packages = with pkgs; [ + fzf + ]; + programs.fish = { + enable = true; + plugins = [ + { + name = "theme-agnoster"; + src = pkgs.fetchFromGitHub { + owner = "oh-my-fish"; + repo = "theme-agnoster"; + rev = "4c5518c89ebcef393ef154c9f576a52651400d27"; + sha256 = "OFESuesnfqhXM0aij+79kdxjp4xgCt28YwTrcwQhFMU="; + fetchSubmodules = true; + }; + } + { + name = "fzf"; + src = pkgs.fetchFromGitHub { + owner = "jethrokuan"; + repo = "fzf"; + rev = "479fa67d7439b23095e01b64987ae79a91a4e283"; + sha256 = "0k6l21j192hrhy95092dm8029p52aakvzis7jiw48wnbckyidi6v"; + fetchSubmodules = true; + }; + } + ]; + shellAliases = { + ls = if config.programs.lsd.enable then "lsd" else "ls --color=auto"; + crontab = "crontab -i"; + }; + + shellAbbrs = { + lessr = "less -R"; + jqc = "jq -C"; + }; + + #interactiveShellInit = '' + # echo "programs.fish.interactiveShellInit" + #''; + shellInit = '' + # key bindings + bind \cr '__fzf_reverse_isearch' + + # PATH + set -U fish_user_paths $HOME/.local/bin $HOME/.local/bin/pio + + # pass + #set -x PASSWORD_STORE_ENABLE_EXTENSIONS true + set -x AWS_VAULT_BACKEND pass + set -x AWS_VAULT_PASS_PREFIX aws + complete -c pw --no-files -a '(__fish_pass_print_entries)' + + # colors + set -x GCC_COLORS 'error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01' + + abbr --add v vim + + #alias cal='ncal -b -M' + alias myip='dig +short myip.opendns.com @resolver1.opendns.com' + + history --merge >/dev/null 2>&1 + + # fancy tools + if which lsd > /dev/null 2>&1 + abbr --add l "lsd" + abbr --add ll "lsd -l --date +'%Y-%m-%d %H:%M:%S'" + abbr --add la "lsd -la --date +'%Y-%m-%d %H:%M:%S'" + abbr --add tree "lsd --tree" + abbr --add llt "lsd -l --timesort --date relative -r" + else + abbr --add l ls + abbr --add ll 'ls -l' + abbr --add la 'ls -la' + abbr --add llt 'ls -trl' + end + + if which rg > /dev/null 2>&1 + abbr --add g rg + complete -c g -w rg + else if which ag > /dev/null 2>&1 + abbr --add g ag + complete -c g -w ag + else + abbr --add g 'grep --color=auto' + complete -c g -w grep + end + + + # NixOS direnv + if which direnv > /dev/null + eval (direnv hook fish) + end + + bind \ed 'dirh-fzf' + + # fix too dark color on solarized theme + set -g fish_color_autosuggestion brgreen + + if type -q fish_set_git_author_by_pwd + fish_set_git_author_by_pwd + end + ''; + + functions = { + jqless = { + body = '' + jq -C $argv | less -R + ''; + }; + __cut_commandline = { + description = "cut commandline and paste it later"; + body = '' + set -g commandline_buffer (commandline) + commandline "" + ''; + }; + __postexec = { + onEvent = "fish_postexec"; + body = '' + if test $status -ne 0; and test -z "$hist_cmd"; and test -z "$fish_private_mode" + #$SHELL -c " + history delete --exact --case-sensitive -- $argv[1] + #" & + end + ''; + }; + dirh-nocolor = { + description = "Print the current directory history (the prev and next lists)"; + body = '' + set -l options h/help + argparse -n dirh --max-args=0 $options -- $argv + or return + + if set -q _flag_help + __fish_print_help dirh + return 0 + end + + set -l dirc (count $dirprev) + if test $dirc -gt 0 + set -l dirprev_rev $dirprev[-1..1] + # This can't be (seq $dirc -1 1) because of BSD. + set -l dirnum (seq 1 $dirc) + for i in $dirnum[-1..1] + printf '%s\n' $dirprev_rev[$i] + end + end + + echo $PWD + + set -l dirc (count $dirnext) + if test $dirc -gt 0 + set -l dirnext_rev $dirnext[-1..1] + for i in (seq $dirc) + printf '%s\n' $dirnext_rev[$i] + end + end + ''; + }; + dirh-fzf = { + description = "directory history fuzzy finder"; + body = '' + builtin cd (dirh-nocolor | uniq | fzf) + ''; + }; + }; + }; + + xdg.configFile = { + "fish/completions/mycli.fish".text = '' + complete -e -c mycli + complete -c mycli -f + complete -c mycli -f -s h -l host -d "Host address of the database." + complete -c mycli -f -s P -l port -d "Port number to use for connection." + complete -c mycli -f -s u -l user -d "User name to connect to the database." + complete -c mycli -f -s S -l socket -d "The socket file to use for connection." + complete -c mycli -f -s p -l pass \ + -l password -d "Password to connect to the database." + complete -c mycli -f -s V -l version -d "Output mycli's version." + complete -c mycli -f -s v -l verbose -d "Verbose output." + complete -c mycli -f -s d -l dsn -d "Use DSN configured into the [alias_dsn] section of myclirc file." + complete -c mycli -f -l list-dsn -d "list of DSN configured into the [alias_dsn] section of myclirc file." + + complete -c mycli -f -s t -l table -d "Display batch output in table format." + complete -c mycli -f -l csv -d "Display batch output in CSV format." + complete -c mycli -f -l warn \ + -l no-warn -d "Warn before running a destructive query." + complete -c mycli -f -s e -l execute -d "Execute command and quit." + + + complete -c mycli -f -s h -l host -r -a '(__fish_print_hostnames)' + complete -c mycli -f -s d -l dsn -r -a '(mycli --list-dsn)' + ''; + + "fish/completions/myssh.fish".text = '' + complete -c myssh -f -a '(myssh --list)' + ''; + + "fish/completions/just.fish".source = pkgs.runCommand "just-fish-completions" { } '' + ${pkgs.just}/bin/just --completions fish > $out + ''; + }; +} diff --git a/home-manager/modules/fpv.nix b/users/jalr/modules/fpv.nix similarity index 51% rename from home-manager/modules/fpv.nix rename to users/jalr/modules/fpv.nix index 0a0b3b8..b51fb63 100644 --- a/home-manager/modules/fpv.nix +++ b/users/jalr/modules/fpv.nix @@ -1,8 +1,8 @@ { nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.gui.enable { +lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ - master.betaflight-configurator + betaflight-configurator fpvout ]; } diff --git a/users/jalr/modules/freetube.nix b/users/jalr/modules/freetube.nix new file mode 100644 index 0000000..3c45d96 --- /dev/null +++ b/users/jalr/modules/freetube.nix @@ -0,0 +1,7 @@ +{ nixosConfig, ... }: + +{ + programs.freetube = { + inherit (nixosConfig.jalr.gui) enable; + }; +} diff --git a/home-manager/modules/git.nix b/users/jalr/modules/git.nix similarity index 67% rename from home-manager/modules/git.nix rename to users/jalr/modules/git.nix index fededfd..3cf4007 100644 --- a/home-manager/modules/git.nix +++ b/users/jalr/modules/git.nix @@ -1,28 +1,59 @@ -{ nixosConfig, pkgs, ... }: - +{ pkgs, ... }: +let + identity.DigitalerDienst = { + name = "Jakob Lechner"; + email = "j.lechner@digitaler-dienst.gmbh"; + }; +in { programs = { git = { enable = true; - userName = nixosConfig.myConfig.git.user.name; - userEmail = nixosConfig.myConfig.git.user.email; + userName = "Jakob Lechner"; + userEmail = "mail@jalr.de"; signing = { - key = nixosConfig.myConfig.gpg.defaultKey; - signByDefault = nixosConfig.myConfig.git.signByDefault; + key = "3044E71E3DEFF49B586CF5809BF4FCCB90854DA9"; + signByDefault = false; + }; + diff-so-fancy = { + enable = true; + markEmptyLines = false; }; extraConfig = { init.defaultBranch = "main"; - core.pager = "${pkgs.diff-so-fancy}/bin/diff-so-fancy | less --tabs=4 -RFX"; diff.sops.textconv = "${pkgs.sops}/bin/sops -d"; pull.ff = "only"; alias.find-merge = "!sh -c 'commit=$0 && branch=\${1:-HEAD} && (git rev-list $commit..$branch --ancestry-path | cat -n; git rev-list $commit..$branch --first-parent | cat -n) | sort -k2 -s | uniq -f1 -d | sort -n | tail -1 | cut -f2'"; alias.show-merge = "!sh -c 'merge=$(git find-merge $0 $1) && [ -n \"$merge\" ] && git show $merge'"; + color = { + ui = true; + meta = "11"; + frag = "magenta bold"; + func = "146 bold"; + commit = "yellow bold"; + old = "red bold"; + new = "green bold"; + whitespace = "red reverse"; + diff-highlight = { + oldNormal = "red bold"; + oldHighlight = "red bold 52"; + newNormal = "green bold"; + newHighlight = "green bold 22"; + }; + }; + }; + lfs.enable = true; + }; + lazygit = { + enable = true; + settings = { + gui.scrollHeight = 8; }; }; fish = { shellAbbrs = { ga = "git add"; - gam = "git commit --amend"; + gam = "git commit --amend --no-edit"; gap = "git add --patch"; gb = "git branch"; gbd = "git branch --delete"; @@ -35,12 +66,10 @@ gd = "git diff"; gdc = "git diff --cached"; gf = "git fetch"; - ginit = "git init"; gl = "git log"; - gpll = "git pull"; + gpll = "git pull --rebase"; gpsh = "git push"; grb = "git rebase --autostash"; - grbi = "git rebase --autostash --interactive --autosquash \(git merge-base HEAD origin/master\)"; gr = "git restore"; grs = "git restore --staged"; grst = "git reset"; @@ -55,6 +84,7 @@ gswc = "git switch -c"; gwl = "git worktree list"; gwr = "git worktree remove"; + lg = "lazygit"; }; functions = { #function gwa -d 'git worktree add' @@ -95,19 +125,28 @@ end ''; }; - git_pick-commit_merge-base_origin_master = { + git_pick-commit = { description = "fuzzy find a commit hash"; body = '' - git log --oneline (git merge-base HEAD origin/master)..HEAD | ${pkgs.fzf}/bin/fzf --preview='git show (echo {} | cut -d" " -f 1)' --preview-window=top:75% | cut -d" " -f 1 + git log --decorate --oneline --color=always \ + | ${pkgs.fzf}/bin/fzf --ansi --preview='git show --color=always (echo {} | cut -d" " -f 1)' --preview-window=top:75% \ + | cut -d" " -f 1 ''; }; gfix = { description = "git commit --fixup with fuzzy find commmit picker"; body = '' - set commit (git_pick-commit_merge-base_origin_master) + set commit (git_pick-commit) commandline "git commit --fixup=$commit" ''; }; + gi = { + description = "git interactive rebase with fuzzy find commmit picker"; + body = '' + set commit (git_pick-commit) + commandline "git rebase --autostash --interactive --autosquash $commit" + ''; + }; ".g" = { description = "change directory to repository root"; body = '' @@ -150,7 +189,23 @@ end ''; }; + "fish_set_git_author_by_pwd" = { + description = "Set Git identity by PWD"; + body = '' + if string match -n $HOME'/digitaler-dienst/*' $PWD/ > /dev/null + if git rev-parse --git-dir >/dev/null 2>&1 + git config --local user.name >/dev/null || git config --local user.name "${identity.DigitalerDienst.name}" + git config --local user.email >/dev/null || git config --local user.email "${identity.DigitalerDienst.email}" + end + end + ''; + onVariable = "PWD"; + }; }; }; }; + home.packages = with pkgs; [ + git-crypt + tig + ]; } diff --git a/home-manager/modules/gnuradio.nix b/users/jalr/modules/gnuradio.nix similarity index 53% rename from home-manager/modules/gnuradio.nix rename to users/jalr/modules/gnuradio.nix index c878718..8a380a7 100644 --- a/home-manager/modules/gnuradio.nix +++ b/users/jalr/modules/gnuradio.nix @@ -1,13 +1,13 @@ { nixosConfig, lib, pkgs, ... }: let - gnuradioEnv = pkgs.gnuradio3_8.override { + gnuradioEnv = pkgs.gnuradio.override { extraPackages = pkgs.lib.attrVals [ "osmosdr" ] - pkgs.gnuradio3_8Packages; + pkgs.gnuradioPackages; }; in -(lib.mkIf nixosConfig.myConfig.gui.enable { +lib.mkIf nixosConfig.jalr.gui.enable { home.packages = [ gnuradioEnv ]; -}) +} diff --git a/home-manager/modules/graphics/default.nix b/users/jalr/modules/graphics/default.nix similarity index 76% rename from home-manager/modules/graphics/default.nix rename to users/jalr/modules/graphics/default.nix index 802e665..3af9b75 100644 --- a/home-manager/modules/graphics/default.nix +++ b/users/jalr/modules/graphics/default.nix @@ -1,5 +1,3 @@ -{ nixosConfig, ... }: - { imports = [ ./gimp.nix diff --git a/home-manager/modules/graphics/gimp.nix b/users/jalr/modules/graphics/gimp.nix similarity index 65% rename from home-manager/modules/graphics/gimp.nix rename to users/jalr/modules/graphics/gimp.nix index a424b10..c64e108 100644 --- a/home-manager/modules/graphics/gimp.nix +++ b/users/jalr/modules/graphics/gimp.nix @@ -1,6 +1,6 @@ { nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.gui.enable { +lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ gimp ]; diff --git a/home-manager/modules/graphics/inkscape.nix b/users/jalr/modules/graphics/inkscape.nix similarity index 80% rename from home-manager/modules/graphics/inkscape.nix rename to users/jalr/modules/graphics/inkscape.nix index 189c77b..b4d3346 100644 --- a/home-manager/modules/graphics/inkscape.nix +++ b/users/jalr/modules/graphics/inkscape.nix @@ -1,6 +1,6 @@ { nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.gui.enable { +lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ inkscape ]; diff --git a/home-manager/modules/graphics/krita.nix b/users/jalr/modules/graphics/krita.nix similarity index 65% rename from home-manager/modules/graphics/krita.nix rename to users/jalr/modules/graphics/krita.nix index 9b1595c..f053c46 100644 --- a/home-manager/modules/graphics/krita.nix +++ b/users/jalr/modules/graphics/krita.nix @@ -1,6 +1,6 @@ { nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.gui.enable { +lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ krita ]; diff --git a/users/jalr/modules/gui.nix b/users/jalr/modules/gui.nix new file mode 100644 index 0000000..4bc6762 --- /dev/null +++ b/users/jalr/modules/gui.nix @@ -0,0 +1,72 @@ +{ nixosConfig, lib, pkgs, ... }: +lib.mkIf nixosConfig.jalr.gui.enable { + home.pointerCursor = { + package = pkgs.gnome-themes-extra; + name = "Adwaita"; + size = lib.mkDefault 16; + }; + + home.packages = with pkgs; [ + evince + exiftool + geeqie + libreoffice-qt6-fresh + mpv + networkmanagerapplet + streamlink + supersonic-wayland + vlc + xdg-utils + xfce.mousepad + ]; + + services.kanshi = + let + internalDisplay = { + criteria = "BOE 0x0BC9 Unknown"; + status = "enable"; + mode = "2560x1600"; + }; + in + { + enable = true; + settings = [ + { + profile.name = "laemmermann"; + profile.outputs = [ + { + criteria = "ViewSonic Corporation VX3276-QHD VSX2403A0490"; + status = "enable"; + mode = "2560x1440@59.951Hz"; + position = "0,0"; + } + { + criteria = "ViewSonic Corporation VX3276-QHD VSX2351A0801"; + status = "enable"; + mode = "2560x1440@59.951Hz"; + position = "2560,0"; + } + (internalDisplay // { position = "5120,0"; }) + ]; + } + { + profile.name = "digitaler-dienst"; + profile.outputs = [ + { + criteria = "Lenovo Group Limited L27q-35 URB5T16W"; + status = "enable"; + mode = "2560x1440@59.951Hz"; + position = "0,0"; + } + { + criteria = "Lenovo Group Limited L27q-35 URB5T174"; + status = "enable"; + mode = "2560x1440@59.951Hz"; + position = "2560,0"; + } + (internalDisplay // { position = "5120,0"; }) + ]; + } + ]; + }; +} diff --git a/users/jalr/modules/jameica.nix b/users/jalr/modules/jameica.nix new file mode 100644 index 0000000..e50c86e --- /dev/null +++ b/users/jalr/modules/jameica.nix @@ -0,0 +1,16 @@ +{ nixosConfig, lib, pkgs, ... }: +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = [ + ( + pkgs.jameica.overrideAttrs (_: { + version = "2.11.0-nightly"; + src = pkgs.fetchFromGitHub { + owner = "willuhn"; + repo = "jameica"; + rev = "e51bffc0e42907cbd802a644ab52810e0a36fff8"; + hash = "sha256-0KcT52dh/tJSX6q+uKkRybz33jKnYRTNDo1BftwJLAc="; + }; + }) + ) + ]; +} diff --git a/home-manager/modules/kicad.nix b/users/jalr/modules/kicad.nix similarity index 65% rename from home-manager/modules/kicad.nix rename to users/jalr/modules/kicad.nix index 5179138..17ea868 100644 --- a/home-manager/modules/kicad.nix +++ b/users/jalr/modules/kicad.nix @@ -1,5 +1,5 @@ { nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.gui.enable { +lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ kicad ]; diff --git a/users/jalr/modules/lsd/colors-dark.yaml b/users/jalr/modules/lsd/colors-dark.yaml new file mode 100644 index 0000000..40165eb --- /dev/null +++ b/users/jalr/modules/lsd/colors-dark.yaml @@ -0,0 +1,27 @@ +user: 125 +group: 136 +permission: + read: 166 + write: 64 + exec: 160 + exec-sticky: 125 + no-access: 245 + octal: 37 + acl: 37 + context: 245 +date: + hour-old: 64 + day-old: 136 + older: 240 +size: + none: 160 + small: 61 + medium: 37 + large: 33 +inode: + valid: 64 + invalid: 160 +links: + valid: 61 + invalid: 240 +tree-edge: 245 diff --git a/users/jalr/modules/lsd/colors-light.yaml b/users/jalr/modules/lsd/colors-light.yaml new file mode 100644 index 0000000..bbbbe59 --- /dev/null +++ b/users/jalr/modules/lsd/colors-light.yaml @@ -0,0 +1,27 @@ +user: 125 +group: 136 +permission: + read: 166 + write: 64 + exec: 160 + exec-sticky: 125 + no-access: 234 + octal: 37 + acl: 37 + context: 235 +date: + hour-old: 64 + day-old: 136 + older: 240 +size: + none: 160 + small: 61 + medium: 37 + large: 33 +inode: + valid: 61 + invalid: 160 +links: + valid: 61 + invalid: 235 +tree-edge: 235 diff --git a/users/jalr/modules/lsd/default.nix b/users/jalr/modules/lsd/default.nix new file mode 100644 index 0000000..5547aee --- /dev/null +++ b/users/jalr/modules/lsd/default.nix @@ -0,0 +1,34 @@ +{ lib, nixosConfig, ... }: + +lib.mkIf nixosConfig.jalr.workstation.enable { + programs.lsd = { + enable = true; + enableFishIntegration = false; + settings = { + color.theme = "custom"; + }; + icons = { + extension = { + dxf = "📏"; + flac = "🎶"; + json = "🄹"; + md = "📝"; + mp3 = "🎶"; + opus = "🎶"; + scad = "🔩"; + wav = "🎶"; + yaml = "🅈"; + yml = "🅈"; + zip = "📦"; + }; + name = { + ".envrc" = ""; + Justfile = ""; + justfile = ""; + public = "📢"; + }; + }; + }; + xdg.configFile."lsd/colors-light.yaml".source = ./colors-light.yaml; + xdg.configFile."lsd/colors-dark.yaml".source = ./colors-dark.yaml; +} diff --git a/users/jalr/modules/mixxc/default.nix b/users/jalr/modules/mixxc/default.nix new file mode 100644 index 0000000..efde8d1 --- /dev/null +++ b/users/jalr/modules/mixxc/default.nix @@ -0,0 +1,10 @@ +{ nixosConfig, lib, pkgs, ... }: + +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = with pkgs; [ + mixxc + ]; + xdg.configFile = { + "mixxc/style.css".source = ./style.css; + }; +} diff --git a/users/jalr/modules/mixxc/style.css b/users/jalr/modules/mixxc/style.css new file mode 100644 index 0000000..33dbf39 --- /dev/null +++ b/users/jalr/modules/mixxc/style.css @@ -0,0 +1,224 @@ +.side { + $hide: false; + + .output { + transition: background 750ms; + padding: 5px; + + &.master { + transition: background 0ms; + } + + &.master:hover { + } + + .icon { + -gtk-icon-style: symbolic; + -gtk-icon-size: 16px; + } + } + + @if $hide { + min-height: 0; + min-width: 0; + + .output { + padding: 0; + + .icon { + -gtk-icon-style: symbolic; + -gtk-icon-size: 0; + } + } + } +} + +.main { + margin: 20px; +} + +.client { + $hide-name: false; + $hide-description: false; + + font-family: 'Iosevka Nerd Font'; + font-size: 1.2em; + + .icon { + -gtk-icon-style: symbolic; + } + + @if $hide-name { + .name { + font-size: 0; + } + } + + @if $hide-description { + .description { + font-size: 0; + } + } + + scale { + trough { + /* Slider Bar */ + border-radius: 10px; + + slider { + /* Slider Knob */ + padding: 0; + + border: none; + border-radius: 2px; + + transition-duration: 400ms; + } + + highlight { + /* Slider Bar Filled */ + border: none; + border-radius: 10px; + + margin: 2px; + + transition: background-image 300ms; + } + + fill { + /* Slider Peak */ + background: none; + + border-radius: 10px; + + margin: 0px; + } + } + } + + scale:active { + trough slider { + /* Slider Knob */ + transform: scale(1.1); + } + } +} + +.client.horizontal { + &.new { + animation: client-add-horizontal 300ms ease; + } + + &.removed { + animation: client-remove-horizontal 300ms ease; + } + + .icon { + padding-right: 13px; + + -gtk-icon-size: 16px; + } + + .volume { + /* Numeric Volume Level */ + padding-left: 22px; + padding-bottom: 3px; + } + + scale { + trough { + /* Slider Bar */ + min-height: 6px; + + slider { + /* Slider Knob */ + min-height: 21px; + min-width: 9px; + + margin-top: -7px; + margin-bottom: -7px; + } + } + } + +} + +@keyframes client-add-horizontal { + from { + transform: translateX(-200px); + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes client-remove-horizontal { + from { + opacity: 1; + } + to { + transform: translateX(-200px); + opacity: 0; + } +} + +.client.vertical { + &.new { + animation: client-add-vertical 300ms ease; + } + + &.removed { + animation: client-remove-vertical 300ms ease; + } + + .icon { + padding-bottom: 5px; + + -gtk-icon-size: 20px; + } + + .volume { + /* Numeric Volume Level */ + padding-top: 10px; + } + + scale { + trough { + /* Slider Bar */ + min-width: 4px; + + margin-top: 10px; + + slider { + /* Slider Knob */ + margin-left: -7px; + margin-right: -7px; + + min-height: 6px; + min-width: 14px; + } + } + } +} + +@keyframes client-add-vertical { + from { + transform: translateY(200px); + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes client-remove-vertical { + from { + opacity: 1; + } + to { + transform: translateY(200px); + opacity: 0; + } +} + diff --git a/home-manager/modules/mpv.nix b/users/jalr/modules/mpv.nix similarity index 88% rename from home-manager/modules/mpv.nix rename to users/jalr/modules/mpv.nix index a9c1b81..5de2d7e 100644 --- a/home-manager/modules/mpv.nix +++ b/users/jalr/modules/mpv.nix @@ -1,6 +1,6 @@ { nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.gui.enable { +lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ mpv ]; diff --git a/home-manager/modules/mute-indicator.nix b/users/jalr/modules/mute-indicator.nix similarity index 90% rename from home-manager/modules/mute-indicator.nix rename to users/jalr/modules/mute-indicator.nix index eb04c75..ecde0cd 100644 --- a/home-manager/modules/mute-indicator.nix +++ b/users/jalr/modules/mute-indicator.nix @@ -1,6 +1,6 @@ { nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.gui.enable { +lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ mute-indicator ]; diff --git a/users/jalr/modules/mycli/default.nix b/users/jalr/modules/mycli/default.nix new file mode 100644 index 0000000..844102b --- /dev/null +++ b/users/jalr/modules/mycli/default.nix @@ -0,0 +1,173 @@ +{ lib, pkgs, ... }: +let + quoteValues = ini: lib.mapAttrs + (_: attrs: + lib.mapAttrs (_: value: if builtins.isString value then ''"${value}"'' else value) attrs + ) + ini; + + solarized = import ../solarized.nix; + + config = { + main = { + # Enables context sensitive auto-completion. If this is disabled the all + # possible completions will be listed. + smart_completion = true; + + # Multi-line mode allows breaking up the sql statements into multiple lines. If + # this is set to True, then the end of the statements must have a semi-colon. + # If this is set to False then sql statements can't be split into multiple + # lines. End of line (return) is considered as the end of the statement. + multi_line = false; + + # Destructive warning mode will alert you before executing a sql statement + # that may cause harm to the database such as "drop table", "drop database" + # or "shutdown". + destructive_warning = true; + + # log_file location. + log_file = "~/.mycli.log"; + + # Default log level. Possible values: "CRITICAL", "ERROR", "WARNING", "INFO" + # and "DEBUG". "NONE" disables logging. + log_level = "INFO"; + + # Log every query and its results to a file. Enable this by uncommenting the + # line below. + # audit_log = "~/.mycli-audit.log"; + + # Timing of sql statments and table rendering. + timing = true; + + # Table format. Possible values: ascii, double, github, + # psql, plain, simple, grid, fancy_grid, pipe, orgtbl, rst, mediawiki, html, + # latex, latex_booktabs, textile, moinmoin, jira, vertical, tsv, csv. + # Recommended: ascii + table_format = "pipe"; + + # Syntax coloring style. Possible values (many support the "-dark" suffix): + # manni, igor, xcode, vim, autumn, vs, rrt, native, perldoc, borland, tango, emacs, + # friendly, monokai, paraiso, colorful, murphy, bw, pastie, paraiso, trac, default, + # fruity. + # Screenshots at http://mycli.net/syntax + # Can be further modified in [colors] + syntax_style = "native"; + + # Keybindings: Possible values: emacs, vi. + # Emacs mode: Ctrl-A is home, Ctrl-E is end. All emacs keybindings are available in the REPL. + # When Vi mode is enabled you can use modal editing features offered by Vi in the REPL. + key_bindings = "emacs"; + + # Enabling this option will show the suggestions in a wider menu. Thus more items are suggested. + wider_completion_menu = false; + + # MySQL prompt + # \D - The full current date + # \d - Database name + # \h - Hostname of the server + # \m - Minutes of the current time + # \n - Newline + # \P - AM/PM + # \p - Port + # \R - The current time, in 24-hour military time (0–23) + # \r - The current time, standard 12-hour time (1–12) + # \s - Seconds of the current time + # \t - Product type (Percona, MySQL, MariaDB) + # \A - DSN alias name (from the [alias_dsn] section) + # \u - Username + # \x1b[...m - insert ANSI escape sequence + prompt = "\\u@\\A:\\d> "; + prompt_continuation = "-> "; + + # Skip intro info on startup and outro info on exit + less_chatty = false; + + # Use alias from --login-path instead of host name in prompt + login_path_as_host = false; + + # Cause result sets to be displayed vertically if they are too wide for the current window, + # and using normal tabular format otherwise. (This applies to statements terminated by ; or \G.) + auto_vertical_output = false; + + # keyword casing preference. Possible values "lower", "upper", "auto" + keyword_casing = "upper"; + + # disabled pager on startup + enable_pager = true; + }; + # Favorite queries. + favorite_queries = { }; + + # Use the -d option to reference a DSN. + # Special characters in passwords and other strings can be escaped with URL encoding. + alias_dsn = { + # example_dsn = "mysql://[user[:password]@][host][:port][/dbname]"; + }; + }; + + colors = + let + c = solarized.colors; + in + { + common = { + "output.header" = "bold ${c.green}"; + "sql.datatype" = "nobold ${c.yellow}"; + "sql.function" = "bold ${c.violet}"; + "sql.keyword" = c.green; + "sql.literal" = c.green; + "sql.number" = c.cyan; + "sql.string" = c.cyan; + "sql.variable" = c.red; + "sql.quoted-schema-object" = c.blue; + }; + light = { + "prompt" = "bg:${c.blue} ${c.base02}"; + "selected" = "bg:${c.base2} ${c.base00}"; + "output.odd-row" = "${c.base01}"; + "output.even-row" = "${c.base01} bg:${c.base2}"; + "sql.comment" = "italic ${c.base1}"; + "sql.operator" = "bold ${c.base02}"; + "sql.punctuation" = "bold ${c.base01}"; + "sql.symbol" = "${c.base01}"; + }; + dark = { + "prompt" = "bg:${c.blue} ${c.base2}"; + "selected" = "bg:${c.base02} ${c.base0}"; + "output.odd-row" = "${c.base1}"; + "output.even-row" = "${c.base1} bg:${c.base02}"; + "sql.comment" = "italic ${c.base01}"; + "sql.operator" = "bold ${c.base2}"; + "sql.punctuation" = "bold ${c.base1}"; + "sql.symbol" = "${c.base1}"; + }; + }; +in +{ + home.packages = [ + (pkgs.mycli.overridePythonAttrs (old: { + dependencies = old.dependencies ++ [ pkgs.python3Packages.sshtunnel ]; + })) + (pkgs.stdenv.mkDerivation { + name = "myssh"; + propagatedBuildInputs = [ + (pkgs.python3.withPackages (pp: with pp; [ + pyyaml + ])) + ]; + dontUnpack = true; + installPhase = "install -Dm755 ${./myssh.py} $out/bin/myssh"; + }) + ]; + + xdg.configFile = lib.attrsets.mapAttrs' + ( + name: value: lib.attrsets.nameValuePair "mycli/theme-${name}.ini" { + text = lib.generators.toINI { } (quoteValues (config // { colors = value; })); + } + ) + { + light = colors.common // colors.light; + dark = colors.common // colors.dark; + }; +} diff --git a/users/jalr/modules/mycli/myssh.py b/users/jalr/modules/mycli/myssh.py new file mode 100755 index 0000000..c79b430 --- /dev/null +++ b/users/jalr/modules/mycli/myssh.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python + +import argparse +import json +import os +import subprocess +import sys +import yaml + + +def get_db_connecition_from_typo3_config(ssh_host, ssh_user, config_file): + php_code = """ + 1 + } + + +def connect(connection_name, connection): + if "t3_config" in connection: + db = get_db_connecition_from_typo3_config( + connection["ssh_host"], connection["ssh_user"], connection["t3_config"] + ) + db_host = db["host"] + db_user = db["user"] + db_password = db["password"] + db_name = db["dbname"] + elif "staging_level" in connection: + env = get_db_connection_from_env_file( + connection["ssh_host"], connection["ssh_user"], connection["staging_level"] + ) + db_host = env["TYPO3_DB_HOST"] + db_user = env["TYPO3_DB_USER"] + db_password = env["TYPO3_DB_PASSWORD"] + db_name = env["TYPO3_DB_DATABASE"] + + os.execl( + "/usr/bin/env", + "env", + "mycli", + "--prompt", + f"{connection_name}>", + "--ssh-user", + connection["ssh_user"], + "--ssh-host", + connection["ssh_host"], + "-h", + db_host, + "-u", + db_user, + "-p", + db_password, + db_name, + ) + + +def read_config(path): + with open(path, "r") as fh: + config = yaml.safe_load(fh.read()) + + return config + + +def main(): + connections = read_config(os.path.expanduser("~/.config/mycli/connections.yaml")) + + parser = argparse.ArgumentParser() + parser.add_argument("--list", action="store_true") + parser.add_argument("connection", type=str, nargs="?") + + args = vars(parser.parse_args()) + + if args["list"]: + print("\n".join(connections.keys())) + + if args["connection"] is not None: + connect(args["connection"], connections[args["connection"]]) + + +if __name__ == "__main__": + main() diff --git a/users/jalr/modules/neovim/default.nix b/users/jalr/modules/neovim/default.nix new file mode 100644 index 0000000..03b8b0b --- /dev/null +++ b/users/jalr/modules/neovim/default.nix @@ -0,0 +1,305 @@ +{ lib, nixosConfig, config, pkgs, ... }: + +let + fakePlugin = pkgs.runCommand "neovim-fake-plugin" { } "mkdir $out"; +in +{ + programs.neovim = { + enable = true; + vimAlias = true; + defaultEditor = true; + extraConfig = '' + """"""""""""""""" + " Swap and undo " + set noswapfile + set nobackup + if has('persistent_undo') + " yay persistent undo + :silent !mkdir -p ~/.local/vim-undo + set undofile + set undodir=~/.local/vim-undo + endif + + cabbr %% expand('%:p:h') + + set listchars=trail:·,precedes:«,extends:»,eol:↲,tab:▸\ + nmap c :set list! + + set smartcase + set hlsearch + nnoremap :nohlsearch:set nolist + + " highlight whitespace + highlight ExtraWhitespace ctermbg=red guibg=red + highlight WrongIndent ctermbg=2 guibg=blue + match ExtraWhitespace /\s\+$/ + augroup highlight_extra_whitespace + autocmd BufWinEnter * match ExtraWhitespace /\s\+$/ + autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\@ e lua vim.diagnostic.enable(not vim.diagnostic.is_enabled()) + nnoremap i lua vim.diagnostic.open_float() + nnoremap gd lua vim.lsp.buf.definition() + nnoremap gi lua vim.lsp.buf.implementation() + nnoremap gr lua vim.lsp.buf.references() + nnoremap gD lua vim.lsp.buf.declaration() + nnoremap ge lua vim.lsp.diagnostic.set_loclist() + nnoremap K lua vim.lsp.buf.hover() + nnoremap f lua vim.lsp.buf.formatting() + nnoremap rn lua vim.lsp.buf.rename() + + nnoremap dh lua require('dap.ui.widgets').hover() + nnoremap dp lua require('dap.ui.widgets').preview() + nnoremap dc lua require('dap').continue() + nnoremap do lua require('dap').step_over() + nnoremap di lua require('dap').step_into() + nnoremap dn lua require('dap').step_out() + nnoremap b lua require('dap').toggle_breakpoint() + + nnoremap a lua vim.lsp.buf.code_action() + xmap a lua vim.lsp.buf.range_code_action() + + lua require('init') + ''; + + plugins = [ + { + plugin = fakePlugin; + config = '' + " use space as leader + let mapleader = " " + ''; + } + ] ++ + # nix-env -f '' -qaP -A vimPlugins + (with pkgs.vimPlugins; [ + { + plugin = NeoSolarized; + config = '' + colorscheme NeoSolarized + ''; + } + { + plugin = nvim-dap; + type = "lua"; + config = '' + local dap = require('dap') + dap.adapters.php = { + type = "executable", + command = "${pkgs.nodejs}/bin/node", + args = { "${pkgs.vscode-extensions.xdebug.php-debug}/share/vscode/extensions/xdebug.php-debug/out/phpDebug.js" } + } + + dap.configurations.php = { + { + type = "php", + request = "launch", + name = "Listen for Xdebug", + port = 9003, + --stopOnEntry = true, + pathMappings = { + ["/app/"] = vim.fn.getcwd().."/", + }, + log = true, + hostname = "0.0.0.0", + } + } + + vim.api.nvim_create_autocmd( + "FileType", { + pattern = "dap-float", + callback = function() + vim.api.nvim_buf_set_keymap(0, "n", "q", "close!", { noremap = true, silent = true }) + end + } + ) + ''; + } + deoplete-nvim + editorconfig-vim + jinja-vim + nvim-lspconfig + { + plugin = telescope-nvim; + type = "lua"; + config = '' + require('telescope').setup() + vim.keymap.set('n', '', 'Telescope find_files') + vim.keymap.set('n', '', 'Telescope live_grep') + vim.keymap.set('n', '', 'Telescope buffers') + ''; + } + { + plugin = telescope-ultisnips-nvim; + type = "lua"; + config = '' + require('telescope').load_extension('ultisnips') + ''; + } + nvim-treesitter-parsers.twig + { + plugin = nvim-treesitter; + type = "lua"; + config = '' + require'nvim-treesitter.configs'.setup { highlight = { enable = true, }, } + ''; + } + { + plugin = ultisnips; + config = ''; + let g:UltiSnipsSnippetDirectories = [ "UltiSnips" ] + inoremap :Telescope ultisnips + ''; + } + { + plugin = vim-fluid; + config = '' + au BufRead *.html if join(getline(1,3), "\n") =~ 'data-namespace-typo3-fluid="true"' | setlocal filetype=fluid | endif + ''; + } + vim-gitgutter + vim-indent-guides + vim-nix + vim-terraform + vim-typoscript + ( + lib.mkIf nixosConfig.jalr.workstation.enable { + plugin = lsp_signature-nvim; + type = "lua"; + config = "require('lsp_signature').setup()"; + } + ) + ]); + }; + + xdg.configFile = { + "nvim/ftplugin/gitcommit.vim".text = '' + setlocal spell + setlocal colorcolumn=73 + ''; + "nvim/ftplugin/markdown.vim".text = '' + setlocal spell + setlocal colorcolumn=81 + ''; + "nvim/ftplugin/sshconfig.vim".text = '' + setlocal tabstop=4 shiftwidth=4 softtabstop=4 expandtab + ''; + "nvim/lua/init.lua".text = builtins.concatStringsSep "\n" ( + [ + '' + -- init.lua + -- this configuration applies to servers and workstations + '' + ] ++ lib.optional nixosConfig.jalr.workstation.enable ( + '' + -- this configuration applies to workstations only + -- https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md + local lsp = require('lspconfig') + + -- show linter messages + vim.diagnostic.config({ virtual_text = true }) + '' + + builtins.concatStringsSep "\n" ( + lib.mapAttrsToList + ( + lang: cfg: "lsp.${lang}.setup\n" + lib.generators.toLua { } cfg + ) + { + # C and C++ + ccls = { + cmd = [ "${pkgs.ccls}/bin/ccls" ]; + }; + + # Nix + nixd = { + cmd = [ "${pkgs.nixd}/bin/nixd" ]; + }; + + # PHP + phpactor = { + cmd = [ "${pkgs.phpactor}/bin/phpactor" "language-server" ]; + init_options = { + "language_server.diagnostic_ignore_codes" = [ + "worse.docblock_missing_param" + "worse.docblock_missing_return_type" + ]; + }; + }; + + # Python + pylsp = { + cmd = [ "${pkgs.python3Packages.python-lsp-server}/bin/pylsp" ]; + settings = { + # https://github.com/python-lsp/python-lsp-server/blob/develop/CONFIGURATION.md + pylsp = { + plugins = { + flake8 = { + enabled = true; + executable = "${pkgs.python3Packages.flake8}/bin/flake8"; + }; + jedi_completion = { enabled = true; }; + jedi_definition = { enabled = true; }; + jedi_hover = { enabled = true; }; + jedi_references = { enabled = true; }; + jedi_signature_help = { enabled = true; }; + jedi_symbols = { enabled = true; }; + mccabe = { enabled = true; }; + lsp_signature-nvim = { enabled = true; }; + preload = { enabled = true; }; + pycodestyle = { enabled = true; }; + pyflakes = { enabled = true; }; + rope_completion = { enabled = true; }; + yapf = { enabled = true; }; + }; + }; + }; + }; + + # Ruby + solargraph = { + cmd = [ "${pkgs.solargraph}/bin/solargraph" "stdio" ]; + }; + + # Rust + rust_analyzer = { + cmd = [ "${pkgs.rust-analyzer}/bin/rust-analyzer" ]; + }; + + # Bash + bashls = { + cmd = [ "${pkgs.nodePackages.bash-language-server}/bin/bash-language-server" "start" ]; + }; + + # Terraform + terraformls = { + cmd = [ "${pkgs.terraform-ls}/bin/terraform-ls" "serve" ]; + }; + + # YAML + yamlls = { + cmd = [ "${pkgs.nodePackages.yaml-language-server}/bin/yaml-language-server" "--stdio" ]; + settings = { + yaml = { + keyOrdering = false; + }; + }; + }; + } + ) + ) + ); + }; +} diff --git a/users/jalr/modules/nix-index.nix b/users/jalr/modules/nix-index.nix new file mode 100644 index 0000000..c753a5e --- /dev/null +++ b/users/jalr/modules/nix-index.nix @@ -0,0 +1,5 @@ +{ + programs.nix-index = { + enable = true; + }; +} diff --git a/home-manager/modules/obs-studio/default.nix b/users/jalr/modules/obs-studio/default.nix similarity index 73% rename from home-manager/modules/obs-studio/default.nix rename to users/jalr/modules/obs-studio/default.nix index 4afb15d..ffd39b3 100644 --- a/home-manager/modules/obs-studio/default.nix +++ b/users/jalr/modules/obs-studio/default.nix @@ -2,7 +2,7 @@ { programs.obs-studio = { - enable = nixosConfig.myConfig.gui.enable; + inherit (nixosConfig.jalr.gui) enable; plugins = with pkgs; [ obs-studio-plugins.wlrobs ]; diff --git a/users/jalr/modules/ots.nix b/users/jalr/modules/ots.nix new file mode 100644 index 0000000..4f3e85c --- /dev/null +++ b/users/jalr/modules/ots.nix @@ -0,0 +1,49 @@ +{ pkgs, ... }: + +{ + home.packages = [ + ( + pkgs.writeShellScriptBin "ots" '' + set -e + set -o nounset + + args=() + region="" + new="" + while [[ $# -gt 0 ]]; do + case "$1" in + new) + new=1 + args+=("$1"); shift + ;; + --region|--region=*) + region=1 + args+=("$1"); shift + ;; + *) + args+=("$1"); shift + ;; + esac + done + + if [[ $new && ! $region ]]; then + args+=("--region" "eu-central-1") + fi + + exec ${pkgs.ots}/bin/ots "''${args[@]}" + '' + ) + ]; + + /* + xdg.configFile."${configFile}".text = lib.generators.toJSON {} ( + let + region = "eu-central-1"; + in + { + apiUrl = "https://ots.${region}.api.sniptt.com/secrets"; + apiKey = ""; + } + ); + */ +} diff --git a/users/jalr/modules/pace.nix b/users/jalr/modules/pace.nix new file mode 100644 index 0000000..0a0b43f --- /dev/null +++ b/users/jalr/modules/pace.nix @@ -0,0 +1,28 @@ +{ config, nixosConfig, lib, pkgs, ... }: + + +let + tomlFormat = pkgs.formats.toml { }; +in +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = with pkgs; [ + pace + ]; + + home.sessionVariables.PACE_HOME = "${config.xdg.configHome}/pace"; + + xdg.configFile."pace/pace.toml".source = tomlFormat.generate "pace.toml" { + general = { + path = "${config.home.homeDirectory}/.local/share/pace/activities/activities.pace.toml"; + storage-kind = "file"; + category-separator = "::"; + default-priority = "medium"; + most-recent-count = 9; + default-time-zone = "${nixosConfig.time.timeZone}"; + }; + }; + + programs.fish.interactiveShellInit = lib.mkAfter '' + ${pkgs.pace}/bin/pace setup completions fish | source + ''; +} diff --git a/users/jalr/modules/pass.nix b/users/jalr/modules/pass.nix new file mode 100644 index 0000000..e77fdb9 --- /dev/null +++ b/users/jalr/modules/pass.nix @@ -0,0 +1,18 @@ +{ nixosConfig, lib, config, pkgs, ... }: + +{ + programs.password-store = { + enable = true; + package = + if nixosConfig.jalr.gui.enable + then pkgs.pass-wayland.withExtensions (exts: [ exts.pass-otp ]) + else pkgs.pass.withExtensions (exts: [ exts.pass-otp ]); + }; + home.packages = lib.optionals nixosConfig.jalr.gui.enable [ + pkgs.qtpass + ]; + xdg.dataFile.password-store = { + source = config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/.password-store"; + target = "password-store"; + }; +} diff --git a/users/jalr/modules/pomodoro.nix b/users/jalr/modules/pomodoro.nix new file mode 100644 index 0000000..6df182a --- /dev/null +++ b/users/jalr/modules/pomodoro.nix @@ -0,0 +1,70 @@ +{ nixosConfig, lib, pkgs, ... }: + +let + tomlFormat = pkgs.formats.toml { }; +in +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = with pkgs; [ + uair + pomodoro-timer + ]; + + xdg.configFile."uair/uair.toml".source = tomlFormat.generate "uair.toml" { + defaults = { + loop_on_end = true; + paused_state_text = "paused"; + resumed_state_text = "resumed"; + overrides = { + yad = { + format = "{percent}\n#{time}\n"; + }; + json = { + format = ''{"name": "{name}", "percent": {percent}", "time": "{time}", "running": {state}, "total": "{total}"} + ''; + paused_state_text = "false"; + resumed_state_text = "true"; + }; + waybar = { + format = ''{"text": "{name}: {time}", "class": "{state}"} + ''; + paused_state_text = "paused"; + resumed_state_text = "resumed"; + }; + }; + }; + sessions = + let + notify-send = "notify-send --app-name='Pomodoro timer' --icon='pomodorotimer'"; + work-finished = "${notify-send} 'Pomodoro done! Enjoy your break.'; dnd off"; + break-finished = "${notify-send} 'Get back to work!'; dnd on"; + in + [ + { name = "Work 1"; duration = "25m"; command = work-finished; } + { name = "Break 1"; duration = "5m"; command = break-finished; } + { name = "Work 2"; duration = "25m"; command = work-finished; } + { name = "Break 2"; duration = "5m"; command = break-finished; } + { name = "Work 3"; duration = "25m"; command = work-finished; } + { name = "Break 3"; duration = "5m"; command = break-finished; } + { name = "Work 4"; duration = "25m"; command = work-finished; } + { name = "Long Break"; duration = "20m"; command = break-finished; } + ]; + }; + + systemd.user.services.uair = { + Unit.Description = "Pomodoro timer"; + Service = { + ExecStart = "${pkgs.uair}/bin/uair"; + NoNewPrivileges = true; + ProtectControlGroups = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX"; + RestrictNamespaces = true; + Type = "simple"; + Environment = [ + "PATH=/etc/profiles/per-user/jalr/bin:/run/current-system/sw/bin/" + ]; + }; + Install.WantedBy = [ "default.target" ]; + }; +} diff --git a/users/jalr/modules/python.nix b/users/jalr/modules/python.nix new file mode 100644 index 0000000..d73b0e3 --- /dev/null +++ b/users/jalr/modules/python.nix @@ -0,0 +1,10 @@ +{ nixosConfig, lib, pkgs, ... }: +lib.mkIf nixosConfig.jalr.workstation.enable { + home.packages = with pkgs; [ + (python3.withPackages (pp: with pp; [ + ipython + pyyaml + virtualenv + ])) + ]; +} diff --git a/users/jalr/modules/remarkable/default.nix b/users/jalr/modules/remarkable/default.nix new file mode 100644 index 0000000..73bffe8 --- /dev/null +++ b/users/jalr/modules/remarkable/default.nix @@ -0,0 +1,8 @@ +{ nixosConfig, lib, ... }: + +{ + imports = lib.optionals nixosConfig.jalr.gui.enable [ + ./restream.nix + ./rmview.nix + ]; +} diff --git a/users/jalr/modules/remarkable/restream.nix b/users/jalr/modules/remarkable/restream.nix new file mode 100644 index 0000000..6dcbec5 --- /dev/null +++ b/users/jalr/modules/remarkable/restream.nix @@ -0,0 +1,7 @@ +{ pkgs, ... }: + +{ + home.packages = with pkgs; [ + restream + ]; +} diff --git a/users/jalr/modules/remarkable/rmview.nix b/users/jalr/modules/remarkable/rmview.nix new file mode 100644 index 0000000..0bf96ed --- /dev/null +++ b/users/jalr/modules/remarkable/rmview.nix @@ -0,0 +1,28 @@ +{ lib, pkgs, ... }: + +let + config = { + ssh = { + address = "remarkable"; + auth_method = "key"; + key = "~/.ssh/id_rsa"; + }; + orientation = "auto"; + pen_size = 15; + pen_color = "red"; + pen_trail = 200; + }; +in +{ + home.packages = with pkgs; [ + ( + writeShellScriptBin "rmview" '' + export QT_QPA_PLATFORM=xcb + exec ${pkgs.rmview}/bin/rmview "$@" + '' + ) + ]; + xdg.configFile."rmview.json" = { + text = lib.generators.toJSON { } config; + }; +} diff --git a/users/jalr/modules/roomeqwizard.nix b/users/jalr/modules/roomeqwizard.nix new file mode 100644 index 0000000..42b49aa --- /dev/null +++ b/users/jalr/modules/roomeqwizard.nix @@ -0,0 +1,6 @@ +{ nixosConfig, lib, pkgs, ... }: +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = with pkgs; [ + roomeqwizard + ]; +} diff --git a/users/jalr/modules/snapclient.nix b/users/jalr/modules/snapclient.nix new file mode 100644 index 0000000..c391bfb --- /dev/null +++ b/users/jalr/modules/snapclient.nix @@ -0,0 +1,20 @@ +{ nixosConfig, lib, pkgs, ... }: + +lib.mkIf nixosConfig.jalr.gui.enable { + systemd.user.services.snapclient = { + Unit.Description = "Snapcast client"; + Service = { + BindPaths = [ "/run/user/1000/pulse" ]; + ExecStart = "${pkgs.snapcast}/bin/snapclient --player pulse"; + NoNewPrivileges = true; + ProtectControlGroups = true; + ProtectHome = "tmpfs"; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX"; + RestrictNamespaces = true; + Type = "simple"; + }; + Install.WantedBy = [ "default.target" ]; + }; +} diff --git a/users/jalr/modules/solarized.nix b/users/jalr/modules/solarized.nix new file mode 100644 index 0000000..baa2547 --- /dev/null +++ b/users/jalr/modules/solarized.nix @@ -0,0 +1,68 @@ +let + colors = { + base00 = "#657b83"; + base01 = "#586e75"; + base02 = "#073642"; + base03 = "#002b36"; + base0 = "#839496"; + base1 = "#93a1a1"; + base2 = "#eee8d5"; + base3 = "#fdf6e3"; + blue = "#268bd2"; + cyan = "#2aa198"; + green = "#859900"; + magenta = "#d33682"; + orange = "#cb4b16"; + red = "#dc322f"; + violet = "#6c71c4"; + yellow = "#b58900"; + }; + common = { + base08 = colors.red; + base09 = colors.orange; + base0A = colors.yellow; + base0B = colors.green; + base0C = colors.cyan; + base0D = colors.blue; + base0E = colors.violet; + base0F = colors.magenta; + }; + light = common // { + base00 = colors.base3; + base01 = colors.base2; + base02 = colors.base1; + base03 = colors.base0; + base04 = colors.base00; + base05 = colors.base01; + base06 = colors.base02; + base07 = colors.base03; + }; + dark = common // { + base00 = colors.base03; + base01 = colors.base02; + base02 = colors.base01; + base03 = colors.base00; + base04 = colors.base0; + base05 = colors.base1; + base06 = colors.base2; + base07 = colors.base3; + }; + toRgb = hex: builtins.concatStringsSep "," ( + map + ( + f: toString (builtins.fromTOML "i = 0x${f hex}").i + ) + ( + map (pos: builtins.substring pos 2) [ 1 3 5 ] + ) + ); + makeScheme = colors: { + hex = colors; + rgb = builtins.mapAttrs (_: hex: (toRgb hex)) colors; + }; +in +{ + inherit colors; + light = makeScheme light; + dark = makeScheme dark; +} diff --git a/home-manager/modules/sound/audacity.nix b/users/jalr/modules/sound/audacity.nix similarity index 66% rename from home-manager/modules/sound/audacity.nix rename to users/jalr/modules/sound/audacity.nix index 9dbd835..2b6cc21 100644 --- a/home-manager/modules/sound/audacity.nix +++ b/users/jalr/modules/sound/audacity.nix @@ -1,6 +1,6 @@ { nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.gui.enable { +lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ audacity ]; diff --git a/home-manager/modules/sound/default.nix b/users/jalr/modules/sound/default.nix similarity index 59% rename from home-manager/modules/sound/default.nix rename to users/jalr/modules/sound/default.nix index 5c7de31..d5764d3 100644 --- a/home-manager/modules/sound/default.nix +++ b/users/jalr/modules/sound/default.nix @@ -1,8 +1,8 @@ -{ nixosConfig, ... }: - { imports = [ ./audacity.nix + ./easyeffects.nix ./pipewire.nix + #./ksoloti.nix ]; } diff --git a/users/jalr/modules/sound/easyeffects.nix b/users/jalr/modules/sound/easyeffects.nix new file mode 100644 index 0000000..eaad63c --- /dev/null +++ b/users/jalr/modules/sound/easyeffects.nix @@ -0,0 +1,14 @@ +{ nixosConfig, lib, pkgs, ... }: + +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = with pkgs; [ + easyeffects + ]; + + services.easyeffects.enable = true; + + xdg.configFile."easyeffects/output/framework-16.json".source = pkgs.fetchurl { + url = "https://gist.githubusercontent.com/amesb/cc5d717472d7e322b5f551b643ff03f4/raw/85029e48072ab3802615b2824dce7df204f0d8ab/amesb%2520fw16%2520EE%2520profile.json"; + sha256 = "sha256-Te8S9DsG5P/NuNk5WE6mSB/DjHS+rKjOFRN7mDEVg8g="; + }; +} diff --git a/home-manager/modules/communication/qtox.nix b/users/jalr/modules/sound/ksoloti.nix similarity index 58% rename from home-manager/modules/communication/qtox.nix rename to users/jalr/modules/sound/ksoloti.nix index 265535d..3528602 100644 --- a/home-manager/modules/communication/qtox.nix +++ b/users/jalr/modules/sound/ksoloti.nix @@ -1,7 +1,7 @@ { nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.gui.enable { +lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ - qtox + ksoloti ]; } diff --git a/home-manager/modules/sound/pipewire.nix b/users/jalr/modules/sound/pipewire.nix similarity index 63% rename from home-manager/modules/sound/pipewire.nix rename to users/jalr/modules/sound/pipewire.nix index 72ae522..22503f8 100644 --- a/home-manager/modules/sound/pipewire.nix +++ b/users/jalr/modules/sound/pipewire.nix @@ -1,8 +1,7 @@ { nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.gui.enable { +lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ - easyeffects pavucontrol qpwgraph ]; diff --git a/home-manager/modules/sway/default.nix b/users/jalr/modules/sway/default.nix similarity index 65% rename from home-manager/modules/sway/default.nix rename to users/jalr/modules/sway/default.nix index 216b977..7e4e764 100644 --- a/home-manager/modules/sway/default.nix +++ b/users/jalr/modules/sway/default.nix @@ -1,24 +1,21 @@ -{ nixosConfig, config, lib, pkgs, stdenv, ... }: +{ nixosConfig, config, lib, pkgs, ... }: let solarized = import ../solarized.nix; terminalEmulator = - if nixosConfig.myConfig.terminalEmulator == "alacritty" - then - pkgs.writeShellScript "alacritty-sway-cwd" '' - this_alacritty_pid="$(${pkgs.sway}/bin/swaymsg -t get_tree | ${pkgs.jq}/bin/jq -e 'recurse(.nodes[]?) | select((.focused==true) and (.app_id=="Alacritty")).pid')" + pkgs.writeShellScript "wezterm-sway-cwd" '' + this_wezterm_pid="$(${pkgs.sway}/bin/swaymsg -t get_tree --raw | ${pkgs.jq}/bin/jq -e 'recurse(.nodes[]?) | select((.focused==true) and (.app_id=="org.wezfurlong.wezterm")).pid')" - if [ "$this_alacritty_pid" ]; then - child_pid="$(pgrep -P "$this_alacritty_pid")" - cwd="$(readlink /proc/$child_pid/cwd)" - fi - if [ -e "$cwd" ]; then - exec ${pkgs.alacritty}/bin/alacritty --working-directory "$cwd" - fi + if [ "$this_wezterm_pid" ]; then + child_pid="$(pgrep -P "$this_wezterm_pid")" + cwd="$(readlink /proc/$child_pid/cwd)" + fi + if [ -e "$cwd" ]; then + exec ${pkgs.wezterm}/bin/wezterm start --cwd "$cwd" + fi - exec ${pkgs.alacritty}/bin/alacritty - '' - else nixosConfig.myConfig.terminalEmulator; + exec ${pkgs.wezterm}/bin/wezterm + ''; cfg = config.wayland.windowManager.sway.config; wallpaper = pkgs.fetchurl { url = "https://raw.githubusercontent.com/swaywm/sway/3b2bc894a5ebbcbbd6707d45a25d171779c2e874/assets/Sway_Wallpaper_Blue_1920x1080.png"; @@ -27,18 +24,59 @@ let meta.license = lib.licenses.cc0; }; move-to-output = pkgs.callPackage ./move-to-output { }; + gsettings = + let + schema = pkgs.gsettings-desktop-schemas; + datadir = "${schema}/share/gsettings-schemas/${schema.name}"; + in + pkgs.writeShellScriptBin "gsettings" '' + export XDG_DATA_DIRS=${datadir}:$XDG_DATA_DIRS + gnome_schema=org.gnome.desktop.interface + #gsettings set $gnome_schema gtk-theme 'Dracula' + ${pkgs.glib}/bin/gsettings "$@" + ''; + matchHostname = hostname: lib.optionalAttrs (nixosConfig.networking.hostName == hostname); + resumeTimeTrackingNotification = pkgs.writeShellScript "resume-time-tracking-notification" '' + export PATH=${pkgs.lib.makeBinPath [pkgs.timewarrior pkgs.libnotify]} + task="$1" + date="$2" + if [ $(notify-send --action 'default=Resume time tracking' "Tracking '$task' stopped at $date, resume?") = "default" ]; then + timew continue + fi + ''; + lockScreen = pkgs.writeShellScript "lock-screen" '' + export PATH="${pkgs.lib.makeBinPath [pkgs.gnused pkgs.timewarrior pkgs.coreutils pkgs.swaylock]}" + task="$(timew | sed -n -r 's/^Tracking (.*)$/\1/p')" + date="$(date --rfc-3339=seconds)" + if [ "$task" != "" ]; then + timew stop + nohup ${resumeTimeTrackingNotification} "$task" "$date" >/dev/null 2>&1 & + fi + swaylock -f -i ${wallpaper} + ''; in { - imports = lib.optionals nixosConfig.myConfig.gui.enable [ + imports = lib.optionals nixosConfig.jalr.gui.enable [ ./gammastep.nix + ./mako.nix + ./screenshare.nix ./waybar.nix + ./wofi-bluetooth.nix ./wofi.nix ./yubikey-touch-detector.nix ]; -} // (lib.mkIf nixosConfig.myConfig.gui.enable { +} // (lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ + gsettings + libnotify # notify-send + mako + slurp + swappy # screenshot editing sway-contrib.grimshot # screenshots + timewarrior wdisplays # graphical output manager + wl-clipboard + wl-mirror ]; home.sessionVariables = { @@ -50,28 +88,6 @@ in _JAVA_AWT_WM_NONREPARENTING = "1"; }; - #home.sessionVariables = { - # CLUTTER_BACKEND = "wayland"; - # GDK_BACKEND = "wayland"; - # GDK_DPI_SCALE = 1; - # MOZ_ENABLE_WAYLAND = 1; - # QT_QPA_PLATFORM = "wayland-egl"; - # QT_WAYLAND_DISABLE_WINDOWDECORATION = 1; - # SDL_VIDEODRIVER = "wayland"; - # WLR_NO_HARDWARE_CURSORS = 1; - # _JAVA_AWT_WM_NONREPARENTING = 1; - # _JAVA_OPTIONS = "-Dawt.useSystemAAFontSettings=on"; - #}; - - programs.fish.loginShellInit = '' - if [ -z $WAYLAND_DISPLAY ] && [ (tty) = /dev/tty1 ] - export XDG_SESSION_TYPE="wayland" # otherwise set to tty - set -e __HM_SESS_VARS_SOURCED - set -e __NIXOS_SET_ENVIRONMENT_DONE - exec systemd-cat -t sway sway - end - ''; - wayland.windowManager.sway = { enable = true; @@ -83,19 +99,19 @@ in down = "r"; terminal = "${terminalEmulator}"; - menu = "${pkgs.wofi}/bin/wofi --allow-images --show drun"; + menu = "${pkgs.wofi}/bin/wofi --allow-images --show drun --color=$HOME/.config/wofi/color"; - output."*".bg = "${wallpaper} fill"; + input."type:keyboard" = { + xkb_layout = "de,de,us"; + xkb_variant = "neo,,"; + xkb_options = "grp:win_space_toggle"; + }; - # FIXME - #input = { - # #"type:keyboard" = { - # # xkb_layout = "neo"; - # #}; - #} // (lib.optionalAttrs (nixosConfig.networking.hostName == "mayushii") { - # "type:touchpad".events = "disabled"; - # "2:10:TPPS/2_Elan_TrackPoint".pointer_accel = "-0.15"; - #}); + output = { + "*".bg = "${wallpaper} fill"; + } // matchHostname "copper" { + eDP-1.scale = toString 1.5; + }; keybindings = { "${cfg.modifier}+Return" = "exec ${cfg.terminal}"; @@ -215,9 +231,10 @@ in #"Shift_R+Shift" = "exec ${pkgs.dbus}/bin/dbus-send --session --type=method_call --dest=net.sourceforge.mumble.mumble / net.sourceforge.mumble.Mumble.stopTalking"; - "XF86AudioMute" = "exec pactl set-source-mute alsa_input.usb-BEHRINGER_UMC202HD_192k-00.analog-stereo toggle"; + "XF86AudioMute" = "exec pactl set-source-mute alsa_input.usb-BEHRINGER_UMC202HD_192k-00.HiFi__umc202hd_mono_in_U192k_0_1__source toggle"; - "${cfg.modifier}+l" = "exec ${pkgs.swaylock}/bin/swaylock -f -i ${wallpaper}"; + "${cfg.modifier}+l" = "exec ${lockScreen}"; + "${cfg.modifier}+v" = "exec GSK_RENDERER=cairo GTK_USE_PORTAL=0 ${pkgs.mixxc}/bin/mixxc -A"; }; bars = [ ]; # managed as systemd user unit @@ -250,6 +267,27 @@ in ]; }; + window.commands = [ + { + criteria = { app_id = "firefox"; title = "Firefox — Sharing Indicator"; }; + command = "kill"; + } + { + criteria = { + app_id = "firefox-esr"; + title = "Extension: \\\\(Tree Style Tab\\\\) - Close tabs\\\\? — Mozilla Firefox"; + }; + command = "floating enable"; + } + { + criteria = { + app_id = "yad"; + title = "Pomodoro"; + }; + command = "floating enable"; + } + ]; + window.border = 2; gaps = { inner = 6; @@ -261,45 +299,22 @@ in border = 1; }; - colors = { - focused = rec { - border = solarized.base1.hex; - background = solarized.base2.hex; - text = solarized.base1.hex; - indicator = solarized.cyan.hex; - childBorder = background; - }; - focusedInactive = rec { - border = solarized.base0.hex; - background = solarized.base03.hex; - text = solarized.base0.hex; - indicator = solarized.cyan.hex; - childBorder = background; - }; - unfocused = rec { - border = solarized.base0.hex; - background = solarized.base03.hex; - text = solarized.base0.hex; - indicator = solarized.cyan.hex; - childBorder = background; - }; - urgent = rec { - border = solarized.base02.hex; - background = solarized.red.hex; - text = solarized.base02.hex; - indicator = solarized.cyan.hex; - childBorder = background; - }; - }; - fonts = { names = [ "monospace" ]; style = "Regular"; - size = 10.0; + + # FIXME: this is an ugly workaround until https://github.com/swaywm/sway/issues/7409 is fixed + size = 0.001; }; }; extraConfig = '' + include ~/.config/sway/theme + + # Hide title bar, see https://github.com/swaywm/sway/issues/6946 + titlebar_border_thickness 0 + titlebar_padding 1 + # Cursor seat seat0 xcursor_theme Adwaita '' + ( @@ -340,35 +355,48 @@ in Environment = "PATH=${pkgs.bash}/bin:${config.wayland.windowManager.sway.package}/bin"; ExecStart = '' ${pkgs.swayidle}/bin/swayidle -w \ - timeout 300 "${pkgs.swaylock}/bin/swaylock -f -i ${wallpaper}" \ - timeout 300 '${pkgs.sway}/bin/swaymsg "output * dpms off"' \ + timeout 300 "${lockScreen}" \ + timeout 270 '${pkgs.sway}/bin/swaymsg "output * dpms off"' \ resume '${pkgs.sway}/bin/swaymsg "output * dpms on"' \ - before-sleep "${pkgs.swaylock}/bin/swaylock -f -i ${wallpaper}" + before-sleep "${lockScreen}" ''; Restart = "on-failure"; }; }; - xdg.configFile."swaynag/config".text = + xdg.configFile = let - # adding it to the header doesn’t work since the defaults overwrite it - commonConfig = /* ini */ '' - background=${lib.substring 1 6 solarized.base3.hex} - border-bottom=${lib.substring 1 6 solarized.base2.hex} - border=${lib.substring 1 6 solarized.base2.hex} - button-background=${lib.substring 1 6 solarized.base3.hex} - button-text=${lib.substring 1 6 solarized.base00.hex} + makeTheme = scheme: '' + client.focused ${scheme.base05} ${scheme.base0D} ${scheme.base00} ${scheme.base0D} ${scheme.base0D} + client.focused_inactive ${scheme.base01} ${scheme.base01} ${scheme.base05} ${scheme.base03} ${scheme.base01} + client.unfocused ${scheme.base01} ${scheme.base00} ${scheme.base05} ${scheme.base01} ${scheme.base01} + client.urgent ${scheme.base08} ${scheme.base08} ${scheme.base00} ${scheme.base08} ${scheme.base08} ''; in - /* ini */ '' - font=Monospace 12 + { + "sway/light-theme".text = makeTheme solarized.light.hex; + "sway/dark-theme".text = makeTheme solarized.dark.hex; + "swaynag/config".text = + let + # adding it to the header doesn’t work since the defaults overwrite it + commonConfig = /* ini */ '' + background=${lib.substring 1 6 solarized.colors.base3} + border-bottom=${lib.substring 1 6 solarized.colors.base2} + border=${lib.substring 1 6 solarized.colors.base2} + button-background=${lib.substring 1 6 solarized.colors.base3} + button-text=${lib.substring 1 6 solarized.colors.base00} + ''; + in + /* ini */ '' + font=Monospace 12 - [warning] - text=${lib.substring 1 6 solarized.yellow.hex} - ${commonConfig} + [warning] + text=${lib.substring 1 6 solarized.colors.yellow} + ${commonConfig} - [error] - text=${lib.substring 1 6 solarized.red.hex} - ${commonConfig} - ''; + [error] + text=${lib.substring 1 6 solarized.colors.red} + ${commonConfig} + ''; + }; }) diff --git a/home-manager/modules/sway/gammastep.nix b/users/jalr/modules/sway/gammastep.nix similarity index 100% rename from home-manager/modules/sway/gammastep.nix rename to users/jalr/modules/sway/gammastep.nix diff --git a/users/jalr/modules/sway/mako.nix b/users/jalr/modules/sway/mako.nix new file mode 100644 index 0000000..767e6b5 --- /dev/null +++ b/users/jalr/modules/sway/mako.nix @@ -0,0 +1,16 @@ +{ pkgs, ... }: + +{ + xdg.configFile."mako/config".text = '' + [mode=dnd] + invisible=1 + + [mode=screencast] + icons=0 + format=%a + + [app-name="Pomodoro timer"] + invisible=0 + icon-path="${pkgs.pomodoro-timer}/share/icons/hicolor" + ''; +} diff --git a/home-manager/modules/sway/move-to-output/default.nix b/users/jalr/modules/sway/move-to-output/default.nix similarity index 93% rename from home-manager/modules/sway/move-to-output/default.nix rename to users/jalr/modules/sway/move-to-output/default.nix index a85a425..0daabfe 100644 --- a/home-manager/modules/sway/move-to-output/default.nix +++ b/users/jalr/modules/sway/move-to-output/default.nix @@ -1,5 +1,5 @@ -{ lib, stdenv, pkgs, writeShellScript, ... }: -stdenv.mkDerivation rec { +{ stdenv, pkgs, ... }: +stdenv.mkDerivation { name = "sway-move-to-output"; phases = "installPhase"; installPhase = '' @@ -8,7 +8,7 @@ stdenv.mkDerivation rec { chmod +x $out/bin/move-to-output ''; script = '' - #!${pkgs.python310}/bin/python + #!${pkgs.python3}/bin/python import sys import json import subprocess diff --git a/users/jalr/modules/sway/screenshare.nix b/users/jalr/modules/sway/screenshare.nix new file mode 100644 index 0000000..c5a21d6 --- /dev/null +++ b/users/jalr/modules/sway/screenshare.nix @@ -0,0 +1,55 @@ +{ pkgs, ... }: +{ + home.packages = [ + ( + pkgs.writeShellScriptBin "screenshare" '' + # https://github.com/emersion/xdg-desktop-portal-wlr/issues/107 + + case "$1" in + start) + # Step 1: Create a new output + swaymsg create_output + + # Step 2: Find the name of the newly created output + NEW_OUTPUT=$(swaymsg -t get_outputs | jq -r '.[] | select(.name | startswith("HEADLESS-")) | .name' | sort | tail -n 1) + + # Check if the output was successfully created + if [ -z "$NEW_OUTPUT" ]; then + echo "Failed to create a new output." + exit 1 + fi + + # Step 3: Assign a workspace to the new output + swaymsg workspace sshr output "$NEW_OUTPUT" + + # Step 4: Set the resolution for the new output + swaymsg output "$NEW_OUTPUT" resolution 1280x720 + + # Step 5: Set the background color for the new output + swaymsg output "$NEW_OUTPUT" bg "#220900" solid_color + + # Step 6: Switch to workspace sshr and then back to the previous workspace + CURRENT_WORKSPACE=$(swaymsg -t get_workspaces | jq -r '.[] | select(.focused) | .name') + swaymsg workspace sshr + swaymsg workspace "$CURRENT_WORKSPACE" + ;; + stop) + headless_outputs=$(swaymsg -t get_outputs | jq -r '.[] | select(.name | startswith("HEADLESS-")) | .name') + + # Check if there are any HEADLESS outputs + if [ -z "$headless_outputs" ]; then + echo "No HEADLESS outputs found." + exit 0 + fi + + # Unplug each HEADLESS output + for output in $headless_outputs; do + echo "Unplugging $output..." + swaymsg output "$output" unplug + done + ;; + esac + '' + ) + ]; +} diff --git a/users/jalr/modules/sway/waybar.nix b/users/jalr/modules/sway/waybar.nix new file mode 100644 index 0000000..c507ce9 --- /dev/null +++ b/users/jalr/modules/sway/waybar.nix @@ -0,0 +1,447 @@ +{ config, lib, nixosConfig, pkgs, ... }: +let + watchUserUnitState = unit: started: stopped: pkgs.writeShellScript "watch-user-unit-${unit}-state" '' + ${pkgs.systemd}/bin/journalctl --user -u ${unit} -t systemd -o cat -f \ + | ${pkgs.gnugrep}/bin/grep --line-buffered -Eo '^(Started|Stopped)' \ + | ${pkgs.jq}/bin/jq --unbuffered -Rc 'if . == "Started" then ${builtins.toJSON started} else ${builtins.toJSON stopped} end' + ''; + + toggleUserUnitState = unit: pkgs.writeShellScript "toggle-user-unit-${unit}-state" '' + if ${pkgs.systemd}/bin/systemctl --user show ${unit} | ${pkgs.gnugrep}/bin/grep -q ActiveState=active; then + ${pkgs.systemd}/bin/systemctl --user stop ${unit} + else + ${pkgs.systemd}/bin/systemctl --user start ${unit} + fi + ''; + + # for fine-grained control over spacing + thinsp = " "; + + solarized = import ../solarized.nix; + solarizedColors = lib.attrsets.mapAttrsToList (name: color: "@define-color ${name} ${color};"); + themeCss = { + light = lib.strings.concatLines (solarizedColors solarized.light.hex); + dark = lib.strings.concatLines (solarizedColors solarized.dark.hex); + }; +in +{ + # home-manager’s waybar module performs additional checks that are overly strict + xdg.configFile."waybar/config".text = + let + makoctl = "${pkgs.mako}/bin/makoctl"; + sendSignal = signal: "${pkgs.procps}/bin/pkill -f -SIGRTMIN+${toString signal} waybar"; + in + lib.generators.toJSON { } { + layer = "top"; + output = [ + "!HEADLESS-1" + "*" + ]; + position = "top"; + height = 24; + + modules-center = [ ]; + modules-left = [ + "sway/workspaces" + "sway/mode" + ]; + modules-right = [ + "tray" + "custom/screencast" + "custom/redshift" + "idle_inhibitor" + "custom/pomodoro" + "backlight" + "pulseaudio" + "network" + "memory" + "cpu" + "temperature" + "battery" + "clock" + "custom/dnd" + ]; + + "sway/workspaces" = { + disable-scroll = true; + }; + "sway/mode" = { + format = "{}"; + }; + + tray = { + spacing = 5; + }; + "custom/redshift" = { + exec = watchUserUnitState + "gammastep" + { class = "active"; } + { class = "inactive"; }; + on-click = toggleUserUnitState "gammastep"; + return-type = "json"; + format = "󰌵"; + tooltip = false; + }; + "custom/pomodoro" = let uairctl = "${pkgs.uair}/bin/uairctl"; in { + # We need to remove nul-characters + # See https://github.com/metent/uair/issues/15 + exec = pkgs.writeShellScript "uairctl-without-null-characters" '' + ${uairctl} listen -o waybar | ${pkgs.gnused}/bin/sed --unbuffered 's/\x0//g' + ''; + + on-click = "${uairctl} toggle"; + on-scroll-up = "${uairctl} next"; + on-scroll-down = "${uairctl} prev"; + on-click-middle = "${uairctl} finish"; + menu = "on-click-right"; + /* + menu-actions = { + "work 1" = "${uairctl} jump 0"; + "break 1" = "${uairctl} jump 1"; + "work 2" = "${uairctl} jump 2"; + "break 2" = "${uairctl} jump 3"; + "work 3" = "${uairctl} jump 4"; + "break 3" = "${uairctl} jump 5"; + "work 4" = "${uairctl} jump 6"; + "long break" = "${uairctl} jump 7"; + }; + */ + return-type = "json"; + }; + idle_inhibitor = { + format = "{icon}"; + format-icons = { + activated = "󰈈 "; + deactivated = "󰈉 "; + }; + }; + "custom/screencast" = { + exec = pkgs.writeScript "screencast-monitor" /* python */ '' + #!${pkgs.python3}/bin/python3 + import subprocess + import sys + + active_outputs = 0 + + with subprocess.Popen( + ["${pkgs.coreutils}/bin/stdbuf", "-o0", "${nixosConfig.services.pipewire.package}/bin/pw-link", "-m", "-o", "xdg-desktop-portal-wlr"], + stdout=subprocess.PIPE, + text=True, + ) as proc: + for line in proc.stdout: + action = line.split(" ")[0] + if action == "=" or action == "+": + active_outputs += 1 + elif action == "-": + active_outputs -= 1 + else: + print(f"Invalid action {action} (in line {line})", file=sys.stderr) + + if active_outputs > 0: + print("󱒃") + subprocess.run(["${makoctl}", "mode", "-a" "screencast"], stdout=subprocess.DEVNULL) + else: + print() + subprocess.run(["${makoctl}", "mode", "-r" "screencast"], stdout=subprocess.DEVNULL) + + sys.stdout.flush() + ''; + format = "{}"; + tooltip = false; + }; + backlight = { + format = "{percent}% {icon}"; + format-icons = [ "󰛩" "󱩎" "󱩏" "󱩐" "󱩑" "󱩒" "󱩓" "󱩔" "󱩕" "󱩖" "󰛨" ]; + on-scroll-up = "${pkgs.brightnessctl}/bin/brightnessctl -q set +1%"; + on-scroll-down = "${pkgs.brightnessctl}/bin/brightnessctl -q set 1%-"; + }; + pulseaudio = { + format = "{volume}% {icon} {format_source}"; + format-bluetooth = "{volume}% {icon}󰗾{format_source}"; + format-bluetooth-muted = "{icon}󰗿{format_source}"; + format-muted = "󰝟 {format_source}"; + format-source = "{volume}% ${thinsp}"; + format-source-muted = "${thinsp}"; + format-icons = { + car = "󰄋 "; + default = [ "󰕿" "󰖀" "󰕾" ]; + hands-free = "󰋎"; + headphone = "󰋋"; + headset = "󰋎"; + phone = "󰏲"; + portable = "󰏲"; + }; + on-click-right = "GSK_RENDERER=cairo GTK_USE_PORTAL=0 ${pkgs.mixxc}/bin/mixxc -A -a t -a r"; + }; + network = { + format-wifi = "{essid} ({signalStrength}%) 󰖩 "; + format-ethernet = "{ipaddr}/{cidr} 󰈀 "; + format-linked = "{ifname} (No IP) 󰈀 "; + format-disconnected = "Disconnected ⚠ "; + format-alt = "{ifname}: {ipaddr}/{cidr}"; + tooltip = false; + on-click-right = "${config.programs.wezterm.package}/bin/wezterm start -e ${pkgs.networkmanager}/bin/nmtui"; + }; + memory = { + interval = 2; + format = "{:2}% 󰍛 "; + }; + cpu = { + interval = 2; + format = "{usage:2}%  "; + tooltip = false; + }; + temperature = { + critical-threshold = 80; + format = "{temperatureC}°C {icon}"; + format-icons = [ "" "" "" "" "" ]; + } // (lib.optionalAttrs (nixosConfig.networking.hostName == "mayushii") { + hwmon-path = "/sys/class/hwmon/hwmon3/temp1_input"; + }); + battery = { + interval = 5; + format = "{capacity}% {icon}"; + format-charging = "{capacity}% "; + format-plugged = "{capacity}% x"; + format-alt = "{time} {icon}"; + format-icons = [ "󰂎" "󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰂂" "󰁹" ]; + states = { + critical = 15; + good = 95; + warning = 30; + }; + }; + clock = { + format = "{:%H:%M %Z}"; + format-alt = "{:%Y-%m-%d (%a)}"; + tooltip-format = "{:%Y %B}\n{calendar}"; + }; + "custom/dnd" = + let + signal = 1; + dndScript = "/etc/profiles/per-user/jalr/bin/dnd"; + in + { + inherit signal; + exec = pkgs.writeScript "waybar-dnd-widget" '' + #!${pkgs.python3}/bin/python3 + + import subprocess + import sys + import time + import json + + while True: + proc = subprocess.Popen(["${makoctl}", "mode"], stdout=subprocess.PIPE, text=True) + modes = [line.rstrip() for line in proc.stdout.readlines()] + + text, class_name = ("󰂛", "active") if 'dnd' in modes else ("󰂚", "inactive") + + print(json.dumps({ + "text": text, + "tooltip": "", + "class": class_name, + "percentage": 0 + })) + + sys.stdout.flush() + + time.sleep(30) + ''; + on-click = "${dndScript} on; ${sendSignal signal}"; + on-click-right = "${dndScript} off; ${sendSignal signal}"; + exec-on-event = true; + return-type = "json"; + restart-interval = 10; + }; + }; + + xdg.configFile = { + "waybar/theme-light.css".text = themeCss.light; + "waybar/theme-dark.css".text = themeCss.dark; + "waybar/style.css".text = '' + @import "theme.css"; + + * { + border-radius: 0; + border: none; + font-family: "Iosevka Nerd Font"; + font-size: 14px; + min-height: 0; + transition-property: none; + } + + window#waybar { + background-color: @base00; + color: @base04; + } + + #workspaces button { + padding: 0 5px; + background-color: @base00; + color: inherit; + border-bottom: 2px solid transparent; + } + + #workspaces button:hover { + background: @base01; + box-shadow: inherit; + text-shadow: inherit; + } + + #workspaces button.focused { + border-bottom: 2px solid @base0B; + } + + #workspaces button.urgent { + background-color: @base08; + } + + #mode { + background-color: @base01; + font-style: italic; + } + + /* all modules on the right */ + #waybar > box > box:nth-child(3) > widget > label { + padding: 0 10px; + } + + #battery.charging { + color: @base01; + background-color: @base0B; + } + + @keyframes blink { + to { + background-color: @base07; + color: @base03; + } + } + + #battery.critical:not(.charging), + #temperature.critical { + background-color: @base08; + animation-name: blink; + animation-duration: 0.5s; + /* FIXME use nearest neighbor interpolation if possible */ + animation-timing-function: cubic-bezier(1, 0, 0, 1); + animation-iteration-count: infinite; + animation-direction: alternate; + } + + #cpu { + background-color: @base0C; + color: @base01 + } + + #memory { + background-color: @base0A; + color: @base01 + } + + #backlight { + background-color: @base07; + color: @base03; + } + + #network { + background-color: @base0E; + color: @base01 + } + + #network.disconnected { + background-color: @base08; + } + + #pulseaudio { + background-color: @base07; + color: @base03; + } + + #pulseaudio.muted { + background-color: @base00; + color: @base04; + } + + #temperature { + background-color: @base0F; + color: @base01; + } + + #idle_inhibitor.activated { + background-color: @base07; + color: @base00; + } + + #custom-redshift { + color: @base01; + } + + #custom-redshift.active { + background-color: @base08; + } + + #custom-redshift.inactive { + background-color: @base0D; + } + + #tray { + padding: 0 5px; + } + + #custom-notification_inhibitor.active { + background-color: @base07; + color: @base00; + } + + #custom-screencast { + background-color: @base08; + color: @base00; + animation-name: blink; + animation-duration: 1s; + animation-timing-function: ease-in-out; + animation-iteration-count: infinite; + animation-direction: alternate; + } + + #custom-pomodoro.resumed { + color: @base01; + background-color: @base0B; + } + + #custom-dnd.active { + color: @base01; + background-color: @base0B; + } + ''; + }; + + systemd.user.services.waybar = { + Unit = { + Description = "Highly customizable Wayland bar for Sway and Wlroots based compositors."; + Documentation = "https://github.com/Alexays/Waybar/wiki/"; + PartOf = [ "sway-session.target" ]; + }; + + Install.WantedBy = [ "sway-session.target" ]; + + Service = { + # ensure sway is already started, otherwise workspaces will not work + ExecStartPre = "${config.wayland.windowManager.sway.package}/bin/swaymsg"; + ExecStart = "${pkgs.waybar}/bin/waybar"; + ExecReload = "${pkgs.utillinux}/bin/kill -SIGUSR2 $MAINPID"; + Restart = "on-failure"; + RestartSec = "1s"; + }; + }; + + # TODO: remove when https://github.com/nix-community/home-manager/issues/2064 + # is resolved + systemd.user.targets.tray = { + Unit = { + Description = "Home Manager System Tray"; + Requires = [ "graphical-session-pre.target" ]; + }; + }; +} diff --git a/users/jalr/modules/sway/wofi-bluetooth.nix b/users/jalr/modules/sway/wofi-bluetooth.nix new file mode 100644 index 0000000..76d2d2b --- /dev/null +++ b/users/jalr/modules/sway/wofi-bluetooth.nix @@ -0,0 +1,5 @@ +{ nixosConfig, lib, pkgs, ... }: + +lib.mkIf nixosConfig.jalr.bluetooth.enable { + home.packages = [ pkgs.wofi-bluetooth ]; +} diff --git a/users/jalr/modules/sway/wofi.nix b/users/jalr/modules/sway/wofi.nix new file mode 100644 index 0000000..44d2914 --- /dev/null +++ b/users/jalr/modules/sway/wofi.nix @@ -0,0 +1,92 @@ +{ lib, ... }: + +let + inherit (import ../solarized.nix) colors; +in +{ + xdg.configFile = + let + commonColors = with colors; [ + red + orange + yellow + green + cyan + blue + violet + magenta + ]; + in + { + "wofi/color-light".text = with colors; lib.strings.concatLines ( + [ + base3 + base2 + base1 + base0 + base00 + base01 + base02 + base03 + ] ++ commonColors + ); + "wofi/color-dark".text = with colors; lib.strings.concatLines ( + with colors; [ + base03 + base02 + base01 + base00 + base0 + base1 + base2 + base3 + ] ++ commonColors + ); + "wofi/style.css".text = '' + window { + margin: 0px; + border: 3px solid --wofi-color1; + border-radius: 8px; + background-color: rgba(--wofi-rgb-color0,0.8); + } + + #input { + margin: 5px; + border: none; + color: --wofi-color4; + background-color: rgba(--wofi-rgb-color1,0.8); + } + + #inner-box { + margin: 5px; + border: none; + background: none; + } + + #outer-box { + margin: 5px; + border: none; + background: none; + } + + #scroll { + margin: 0px; + border: none; + } + + #text { + margin: 5px; + border: none; + color: --wofi-color4; + } + + #entry:selected { + background-color: rgba(--wofi-rgb-color1,0.8); + } + + #entry:selected #text{ + color: --wofi-color11; + } + ''; + }; +} diff --git a/home-manager/modules/sway/yubikey-touch-detector.nix b/users/jalr/modules/sway/yubikey-touch-detector.nix similarity index 95% rename from home-manager/modules/sway/yubikey-touch-detector.nix rename to users/jalr/modules/sway/yubikey-touch-detector.nix index 658f653..0d01b60 100644 --- a/home-manager/modules/sway/yubikey-touch-detector.nix +++ b/users/jalr/modules/sway/yubikey-touch-detector.nix @@ -1,4 +1,4 @@ -{ nixosConfig, config, lib, pkgs, ... }: +{ pkgs, ... }: { systemd.user.services.yubikey-touch-detector = { diff --git a/users/jalr/modules/thunar.nix b/users/jalr/modules/thunar.nix new file mode 100644 index 0000000..28b5b0b --- /dev/null +++ b/users/jalr/modules/thunar.nix @@ -0,0 +1,8 @@ +{ nixosConfig, lib, pkgs, ... }: +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = with pkgs; [ + xfce.thunar + xfce.thunar-volman + ]; +} + diff --git a/users/jalr/modules/thunderbird.nix b/users/jalr/modules/thunderbird.nix new file mode 100644 index 0000000..e8cd892 --- /dev/null +++ b/users/jalr/modules/thunderbird.nix @@ -0,0 +1,14 @@ +{ nixosConfig, pkgs, ... }: +{ + programs.thunderbird = { + inherit (nixosConfig.jalr.gui) enable; + package = pkgs.thunderbird-esr; + profiles."default" = { + isDefault = true; + withExternalGnupg = true; + }; + settings = { + "mail.chat.enabled" = false; + }; + }; +} diff --git a/home-manager/modules/tmux.nix b/users/jalr/modules/tmux.nix similarity index 81% rename from home-manager/modules/tmux.nix rename to users/jalr/modules/tmux.nix index a0781e4..c8d4c10 100644 --- a/home-manager/modules/tmux.nix +++ b/users/jalr/modules/tmux.nix @@ -1,4 +1,3 @@ -{ config, pkgs, ... }: { programs.tmux = { enable = true; diff --git a/users/jalr/modules/tor-browser.nix b/users/jalr/modules/tor-browser.nix new file mode 100644 index 0000000..f0420ab --- /dev/null +++ b/users/jalr/modules/tor-browser.nix @@ -0,0 +1,6 @@ +{ nixosConfig, lib, pkgs, ... }: +lib.mkIf nixosConfig.jalr.gui.enable { + home.packages = with pkgs; [ + tor-browser-bundle-bin + ]; +} diff --git a/home-manager/modules/claws-mail.nix b/users/jalr/modules/trilium.nix similarity index 52% rename from home-manager/modules/claws-mail.nix rename to users/jalr/modules/trilium.nix index baf6a8c..91b6dfe 100644 --- a/home-manager/modules/claws-mail.nix +++ b/users/jalr/modules/trilium.nix @@ -1,6 +1,6 @@ { nixosConfig, lib, pkgs, ... }: -lib.mkIf nixosConfig.myConfig.gui.enable { +lib.mkIf nixosConfig.jalr.gui.enable { home.packages = with pkgs; [ - claws-mail + trilium-next-desktop ]; } diff --git a/home-manager/modules/vdirsyncer.nix b/users/jalr/modules/vdirsyncer.nix similarity index 85% rename from home-manager/modules/vdirsyncer.nix rename to users/jalr/modules/vdirsyncer.nix index c8c828d..c853db1 100644 --- a/home-manager/modules/vdirsyncer.nix +++ b/users/jalr/modules/vdirsyncer.nix @@ -45,27 +45,6 @@ let }; }; - mkWebcalSection = { name, url ? null, urlCommand ? null }: assert url == null -> urlCommand != null; { - "pair calendar_${name}" = { - a = "calendar_${name}_local"; - b = "calendar_${name}_remote"; - collections = null; - }; - - "storage calendar_${name}_local" = { - type = "filesystem"; - path = "${calendarBasePath}/${name}/"; - fileext = ".ics"; - }; - - "storage calendar_${name}_remote" = { - type = "http"; - } // (if urlCommand != null then { - "url.fetch" = fetchCommand urlCommand; - } else { - inherit url; - }); - }; in { home.packages = with pkgs; [ diff --git a/users/jalr/modules/vesc-tool.nix b/users/jalr/modules/vesc-tool.nix new file mode 100644 index 0000000..3b5d90b --- /dev/null +++ b/users/jalr/modules/vesc-tool.nix @@ -0,0 +1,12 @@ +{ nixosConfig, lib, pkgs, ... }: + +lib.mkIf (nixosConfig.jalr.gui.enable && nixosConfig.networking.hostName == "copper") { + home.packages = with pkgs; [ vesc-tool ]; + xdg.dataFile."VESC/firmware".source = pkgs.bldc-fw.override { + fwBoards = [ + "410" + "60" + "60_mk3" + ]; + }; +} diff --git a/users/jalr/modules/wezterm.nix b/users/jalr/modules/wezterm.nix new file mode 100644 index 0000000..be238eb --- /dev/null +++ b/users/jalr/modules/wezterm.nix @@ -0,0 +1,46 @@ +{ config, lib, nixosConfig, ... }: + +let + weztermConfig = { + hide_tab_bar_if_only_one_tab = true; + #color_scheme = "Solarized (dark) (terminal.sexy)"; + #color_scheme = "Solarized (light) (terminal.sexy)"; + font = "FiraCode Nerd Font Mono"; + font_size = 12; + }; +in +{ + programs.wezterm = { + inherit (nixosConfig.jalr.gui) enable; + extraConfig = '' + local wezterm = require 'wezterm' + ${lib.generators.toLua {asBindings=true;} {config=weztermConfig;}} + config.font = wezterm.font(config.font) + + function get_appearance() + if wezterm.gui then + return wezterm.gui.get_appearance() + end + return 'Light' + end + + function scheme_for_appearance(appearance) + if appearance:find 'Dark' then + return 'Builtin Solarized Dark' + else + return 'Builtin Solarized Light' + end + end + + config.color_scheme = scheme_for_appearance(get_appearance()) + + return config + ''; + }; + /* + file = io.open("${config.xdg.configHome}/wezterm/color_scheme.txt", "r") + io.input(file) + config.color_scheme = io.read() + io.close(file) + */ +}