Compare commits
2 commits
350d652487
...
051d7dce95
Author | SHA1 | Date | |
---|---|---|---|
Tristan Daniël Maat | 051d7dce95 | ||
Tristan Daniël Maat | 8f178f776e |
|
@ -14,6 +14,8 @@
|
|||
"${modulesPath}/profiles/minimal.nix"
|
||||
(import ../modules)
|
||||
|
||||
./services/afvalcalendar.nix
|
||||
./services/auth.nix
|
||||
./services/backups.nix
|
||||
./services/battery-manager.nix
|
||||
./services/conduit.nix
|
||||
|
|
67
configuration/services/afvalcalendar.nix
Normal file
67
configuration/services/afvalcalendar.nix
Normal file
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}: {
|
||||
systemd.services.afvalcalendar = {
|
||||
description = "Enschede afvalcalendar -> ical converter";
|
||||
wantedBy = ["multi-user.target"];
|
||||
after = ["network.target"];
|
||||
|
||||
script = ''
|
||||
${pkgs.local.afvalcalendar}/bin/afvalcalendar > /srv/afvalcalendar/afvalcalendar.ical
|
||||
'';
|
||||
|
||||
startAt = "daily";
|
||||
|
||||
serviceConfig = {
|
||||
DynamicUser = true;
|
||||
ProtectHome = true; # Override the default (read-only)
|
||||
PrivateDevices = true;
|
||||
PrivateIPC = true;
|
||||
PrivateUsers = true;
|
||||
ProtectHostname = true;
|
||||
ProtectClock = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectControlGroups = true;
|
||||
RestrictAddressFamilies = ["AF_UNIX" "AF_INET" "AF_INET6"];
|
||||
RestrictNamespaces = true;
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = ["@system-service" "~@privileged @resources @setuid @keyring"];
|
||||
|
||||
Umask = 0002;
|
||||
SupplementaryGroups = "afvalcalendar-hosting";
|
||||
|
||||
ReadWritePaths = "/srv/afvalcalendar";
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx.virtualHosts."afvalcalendar.${config.services.nginx.domain}" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
enableHSTS = true;
|
||||
|
||||
root = "/srv/afvalcalendar";
|
||||
};
|
||||
|
||||
users.groups.afvalcalendar-hosting = {};
|
||||
systemd.tmpfiles.settings."10-afvalcalendar" = {
|
||||
"/srv/afvalcalendar".d = {
|
||||
user = "nginx";
|
||||
group = "afvalcalendar-hosting";
|
||||
mode = "0775";
|
||||
};
|
||||
|
||||
"/srv/afvalcalendar/afvalcalendar.ical".f = {
|
||||
user = "nginx";
|
||||
group = "afvalcalendar-hosting";
|
||||
mode = "0775";
|
||||
};
|
||||
};
|
||||
}
|
95
configuration/services/auth.nix
Normal file
95
configuration/services/auth.nix
Normal file
|
@ -0,0 +1,95 @@
|
|||
{
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}: let
|
||||
user = config.services.authelia.instances.main.user;
|
||||
domain = "auth.${config.services.nginx.domain}";
|
||||
in {
|
||||
services.authelia.instances.main = {
|
||||
enable = true;
|
||||
settings = {
|
||||
theme = "auto";
|
||||
|
||||
access_control.default_policy = "one_factor";
|
||||
|
||||
authentication_backend = {
|
||||
password_reset.disable = true;
|
||||
file.path = "/var/lib/authelia-main/users.yml";
|
||||
};
|
||||
|
||||
notifier.filesystem.filename = "/var/lib/authelia-main/notification.txt";
|
||||
|
||||
session = {
|
||||
domain = config.services.nginx.domain;
|
||||
redis.host = config.services.redis.servers.authelia.unixSocket;
|
||||
};
|
||||
|
||||
storage.postgres = {
|
||||
host = "/run/postgresql";
|
||||
port = 5432;
|
||||
database = user;
|
||||
username = user;
|
||||
|
||||
password = "unnecessary";
|
||||
};
|
||||
};
|
||||
|
||||
secrets = {
|
||||
storageEncryptionKeyFile = config.sops.secrets."authelia/storageEncryptionKey".path; # Database
|
||||
sessionSecretFile = config.sops.secrets."authelia/sessionSecret".path; # Redis
|
||||
jwtSecretFile = config.sops.secrets."authelia/jwtSecret".path;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.authelia-main.after = ["postgresql.service"];
|
||||
|
||||
services.nginx = {
|
||||
# TODO(tlater): Possibly remove on next authelia release
|
||||
additionalModules = with pkgs.nginxModules; [
|
||||
develkit
|
||||
set-misc
|
||||
];
|
||||
|
||||
virtualHosts."${domain}" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
enableHSTS = true;
|
||||
|
||||
locations = {
|
||||
"/" = {
|
||||
proxyPass = "http://127.0.0.1:9091";
|
||||
recommendedProxySettings = false;
|
||||
enableAutheliaProxy = true;
|
||||
};
|
||||
|
||||
"/api/verify" = {
|
||||
proxyPass = "http://127.0.0.1:9091";
|
||||
recommendedProxySettings = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.redis.servers.authelia = {
|
||||
inherit user;
|
||||
enable = true;
|
||||
};
|
||||
|
||||
sops.secrets = {
|
||||
"authelia/storageEncryptionKey" = {
|
||||
owner = user;
|
||||
group = user;
|
||||
};
|
||||
|
||||
"authelia/sessionSecret" = {
|
||||
owner = user;
|
||||
group = user;
|
||||
};
|
||||
|
||||
"authelia/jwtSecret" = {
|
||||
owner = user;
|
||||
group = user;
|
||||
};
|
||||
};
|
||||
}
|
|
@ -40,6 +40,7 @@ in {
|
|||
forceSSL = true;
|
||||
enableACME = true;
|
||||
enableHSTS = true;
|
||||
enableAuthorization = true;
|
||||
locations."/".proxyPass = "http://localhost:${toString config.services.grafana.settings.server.http_port}";
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
{pkgs, ...}: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}: {
|
||||
services.postgresql = {
|
||||
package = pkgs.postgresql_14;
|
||||
enable = true;
|
||||
|
@ -24,11 +28,16 @@
|
|||
name = "nextcloud";
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
{
|
||||
name = config.services.authelia.instances.main.user;
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
];
|
||||
|
||||
ensureDatabases = [
|
||||
"grafana"
|
||||
"nextcloud"
|
||||
config.services.authelia.instances.main.user
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
authelia:
|
||||
storageEncryptionKey: ENC[AES256_GCM,data:OUCC+6Gcr6U7Mub1+DaIyswTV6da1wd1u0WGEm4wpJ8L0mi7WSpEmVjH79YyRhp7AmiZhdFFDXFeEYthBb2AZl+xoS9gqs6rWyfU4ezaCbXBiS/dIhsA5foPg13wq5A33qJWtPTy7DJEgqHaIonnaBuVJIBwH3wzPTHc3bDvBo4=,iv:intiZzngz5cMTtjEI9rTKMW0Xv3KB3ZEgtYN3amwKCE=,tag:AKxfbeZlPs54esHCsVnNCg==,type:str]
|
||||
sessionSecret: ENC[AES256_GCM,data:GEMWhBltOIOs0g9FsWk3OQGs6dMcbwz3ZuhlyBFYROylsIZb4xTXWLgNwIpHwQukQU3TgvIxbCW/fGRWiALPanE2koSVAHNx0UU0hj1mVNRFQGK4H3EL10tPp7l4PofrcdeCbLPrOwM/xLOuPt+52sKlcbL2Awz5/MmpUVpCKXc=,iv:kWX2ptOpTgW3obBgri0MvVv6gCEPR3o77sldOXFQeks=,tag:je4pqLcEOhuBTQkoZHYNCw==,type:str]
|
||||
battery-manager:
|
||||
email: ENC[AES256_GCM,data:rYLUACXR/n+bLBmZ,iv:sUBEkh2+7qGjHZ5R23e/hoCiyTA7GTL4bJvXmxjZ5Sw=,tag:fdPMllaQQfRgX0WZKIre4g==,type:str]
|
||||
password: ENC[AES256_GCM,data:7cokZa6Q6ahSeiFPz+cV,iv:vz405P0IcG9FsAQXlY7mi78GuushQUKJm2irG6buGzc=,tag:JLHG2jTkJDGbinAq9dXRsQ==,type:str]
|
||||
|
@ -31,8 +34,8 @@ sops:
|
|||
azure_kv: []
|
||||
hc_vault: []
|
||||
age: []
|
||||
lastmodified: "2024-04-06T15:32:49Z"
|
||||
mac: ENC[AES256_GCM,data:ShqLJf9b20LdmjK6MMPtI3KicE+fPc0ejzVGEIdgbNs7ueDwdt7jqgpDrpiyf+vW86tr3I1E1VTlh127XlSH/RZDRRHehpX0tnBiF0zMscmt1vdinY4cPhTwhLJ1fdpVpY8ihdOqv0UFyC39HP78aWESX5S/dJZQ6vS7K5VGKTY=,iv:TYE9f9iyrUQxmMeKXApEYsSPcMWK8vndyBm7HtJyJPo=,tag:vSlobwA1R0Go7BYgNVpMkw==,type:str]
|
||||
lastmodified: "2024-04-11T23:38:56Z"
|
||||
mac: ENC[AES256_GCM,data:GjIB0EbWsh4o+QoFSyIXgGYnNhRlvfSmue1LyTt6oUlIjNgODhdIB8px8LnRo0rmm/f1YHbDq2MFOxlgdm3PTNaqm/MoKyW3r/wuAeWADsYayQszLNxyhTMXcjWtfm6zCRIuc/+YyM44pXRfVrOZRAin9B6pmJZsRJwBAZpogbU=,iv:r/ZQZvrP0E9dOW5fhBH2I21Z0uv2e3njdEGmadxEALg=,tag:iZvbGTvRJFo80n8aoKSSmQ==,type:str]
|
||||
pgp:
|
||||
- created_at: "2024-03-18T04:02:00Z"
|
||||
enc: |-
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
authelia:
|
||||
storageEncryptionKey: ENC[AES256_GCM,data:8X6Zvq7ct1MkMH+lc5kTTHXOo6rGyhSzc3aWxGRA5tnBet+TGcENo0RYTYmactsPGVpTIUGGplaG7B7dqRPhkdDHhbCCZCm2nLaYjpVJ241DrpUNKHn8lvg/bMxUQ/Dvw76ByYuWN6bREr3XRaBztBSPzld8zTSYx71I0CKY7vk=,iv:cJSwfuVWO39qqKCGt2Mvw7pN8+hD6kRH9v4c/u4hLuk=,tag:YhdlXuX2ETxjb443RI8MsA==,type:str]
|
||||
sessionSecret: ENC[AES256_GCM,data:dnoWmc4HND62w3jMXL+akncAEb61c/I70DgRytx55Wxcn4rMiswp6zCkRdsP4CkouTQ1lyAcQrubp5I8M9Kyow/KBMYz9dPkr4+2xJ9w0SEmAVhyPe2DFvYos3x0Uvx5S0B3o1mXoXqbg78e4w5yEIbALiJT8VPGrWK8Cl4nVPo=,iv:FHDXUW2DWUmEZzWUYkYduogdVOtvMlRH4/fVg05cZaI=,tag:u282WQnHpBsZGYJH7mFFKA==,type:str]
|
||||
jwtSecret: ENC[AES256_GCM,data:0M3AyoMp+orrljl5NsxmthzrHMmu0REcz7+9fpFKbwwqV6KqlpgGddjYZIsTpHEWEq9zhZ2YWLJkMxKdDgROVHUFZGKut28JPSAjjY+1V0wxNBnfSCnxEv5BUw2+cCxcpCwYQyNfRK6SotTt8aqpxvda4oRXpzxV6SW7ogDjc6E=,iv:D57SynZkW2JuFyX6bpZYkxpR2KtkOmKaySg1Bxim0r8=,tag:JCPGZaumdHrtgcH16A7b+g==,type:str]
|
||||
battery-manager:
|
||||
email: ENC[AES256_GCM,data:LM/EGzWHfVQ=,iv:jFaoUQuUfuGoOyj/GFpdI8TerH/c8D9fjvio+IEt2Tc=,tag:IWLiN011JEnHRLIXWQgfmA==,type:str]
|
||||
password: ENC[AES256_GCM,data:SUxjqS7SJHM=,iv:LvdKk88S+nSImh6/ZezbFGLCUBu1Lpdu+neF2xyHdBg=,tag:rcMyZuW4FVNbcbz00wQKBg==,type:str]
|
||||
|
@ -31,8 +35,8 @@ sops:
|
|||
azure_kv: []
|
||||
hc_vault: []
|
||||
age: []
|
||||
lastmodified: "2024-04-06T15:33:40Z"
|
||||
mac: ENC[AES256_GCM,data:qB9uDDM5K6+BmeAKyTJ0Sel6Um0Fc9IhdV3wAn13WxpwDtxmMsdqnwaewI/KepsRG3k76x9vkYL+oKkUysqq1r1FkocUDg4DnKnf1KtKo2Zm9MPcVRG833m6oDoTeGnmgrAMTDKy1tUdGkXW40IfbMakbSjSIfLbrymtoHeVbaE=,iv:8P8M4Ueo3Idlgo+Yqj6JUtFfWX949fz6HfRHEOy1/Vg=,tag:ou+IGZSQSfX6gNoxbpAipg==,type:str]
|
||||
lastmodified: "2024-04-12T01:00:31Z"
|
||||
mac: ENC[AES256_GCM,data:fVnMwfvGi7vtP1Fg4NLrhGvLF2PcIgZPOcwk4Ssm4iw5iSj0K1npOX3pd5BWzyszqchfYYRHY99GllAump0bZmprVAld9rf70B2HZIVvowBPuUXfc9Cz/5q0z+s8bQ5vCdElW1Bh7h8W/POePdc8cFGAyBS4i1ZVNheIDOHdDjI=,iv:Bi6rekXOx3/dwwPRryF3CoAoQi3D06ABysRF1oBeG5A=,tag:0TCra+AkhBDczj4uvAzKMw==,type:str]
|
||||
pgp:
|
||||
- created_at: "2023-12-29T15:25:27Z"
|
||||
enc: |
|
||||
|
|
|
@ -11,12 +11,72 @@
|
|||
};
|
||||
|
||||
services.nginx.virtualHosts = let
|
||||
autheliaDomain = "auth.${config.services.nginx.domain}";
|
||||
extraLocationOptions = {config, ...}: {
|
||||
options = {
|
||||
enableAutheliaProxy = lib.mkEnableOption "Enable recommended authelia proxy settings";
|
||||
enableAuthorization = lib.mkEnableOption "Enable authorization via authelia";
|
||||
};
|
||||
|
||||
config = {
|
||||
recommendedProxySettings = lib.mkIf config.enableAutheliaProxy false;
|
||||
|
||||
extraConfig = lib.concatStringsSep "\n" [
|
||||
(lib.optionalString config.enableAutheliaProxy ''
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
proxy_set_header X-Forwarded-URI $request_uri;
|
||||
proxy_set_header X-Forwarded-Ssl on;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Connection "";
|
||||
|
||||
client_body_buffer_size 128k;
|
||||
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
|
||||
proxy_redirect http:// $scheme://;
|
||||
proxy_http_version 1.1;
|
||||
proxy_cache_bypass $cookie_session;
|
||||
proxy_no_cache $cookie_session;
|
||||
proxy_buffers 64 256k;
|
||||
|
||||
real_ip_header X-Forwarded-For;
|
||||
real_ip_recursive on;
|
||||
|
||||
send_timeout 5m;
|
||||
proxy_read_timeout 360;
|
||||
proxy_send_timeout 360;
|
||||
proxy_connect_timeout 360;
|
||||
'')
|
||||
(lib.optionalString config.enableAuthorization ''
|
||||
auth_request /authelia;
|
||||
|
||||
set_escape_uri $target_url $scheme://$http_host$request_uri;
|
||||
|
||||
auth_request_set $user $upstream_http_remote_user;
|
||||
auth_request_set $groups $upstream_http_remote_groups;
|
||||
auth_request_set $name $upstream_http_remote_name;
|
||||
auth_request_set $email $upstream_http_remote_email;
|
||||
|
||||
proxy_set_header Remote-User $user;
|
||||
proxy_set_header Remote-Groups $groups;
|
||||
proxy_set_header Remote-Email $email;
|
||||
proxy_set_header Remote-Name $name;
|
||||
|
||||
error_page 401 =302 https://${autheliaDomain}/?rd=$target_url;
|
||||
'')
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
extraVirtualHostOptions = {
|
||||
name,
|
||||
config,
|
||||
...
|
||||
}: {
|
||||
options = {
|
||||
enableAuthorization = lib.mkEnableOption "Enable authorization via authelia";
|
||||
enableHSTS = lib.mkEnableOption "Enable HSTS";
|
||||
|
||||
addAccessLog = lib.mkOption {
|
||||
|
@ -26,6 +86,10 @@
|
|||
Add special logging to `/var/log/nginx/''${serverName}`
|
||||
'';
|
||||
};
|
||||
|
||||
locations = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.submodule extraLocationOptions);
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
|
@ -37,6 +101,41 @@
|
|||
access_log /var/log/nginx/${name}/access.log upstream_time;
|
||||
'')
|
||||
];
|
||||
|
||||
locations = lib.mkIf config.enableAuthorization {
|
||||
"/".enableAuthorization = true;
|
||||
"/authelia" = {
|
||||
proxyPass = "http://127.0.0.1:9091/api/verify";
|
||||
recommendedProxySettings = false;
|
||||
extraConfig = ''
|
||||
internal;
|
||||
|
||||
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
|
||||
proxy_set_header X-Original-Method $request_method;
|
||||
proxy_set_header X-Forwarded-Method $request_method;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
proxy_set_header X-Forwarded-Uri $request_uri;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Content-Length "";
|
||||
proxy_set_header Connection "";
|
||||
|
||||
proxy_pass_request_body off;
|
||||
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
|
||||
proxy_redirect http:// $scheme://;
|
||||
proxy_http_version 1.1;
|
||||
proxy_cache_bypass $cookie_session;
|
||||
proxy_no_cache $cookie_session;
|
||||
proxy_buffers 4 32k;
|
||||
client_body_buffer_size 128k;
|
||||
|
||||
send_timeout 5m;
|
||||
proxy_read_timeout 240;
|
||||
proxy_send_timeout 240;
|
||||
proxy_connect_timeout 240;
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
|
|
1430
pkgs/afvalcalendar/Cargo.lock
generated
Normal file
1430
pkgs/afvalcalendar/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
16
pkgs/afvalcalendar/Cargo.toml
Normal file
16
pkgs/afvalcalendar/Cargo.toml
Normal file
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "afvalcalendar"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
icalendar = "0.16.0"
|
||||
serde = { version = "1.0.195", features = ["derive"] }
|
||||
tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread"] }
|
||||
reqwest = { version = "0.11", features = ["cookies", "json"] }
|
||||
chrono = { version = "0.4.34", features = ["serde"] }
|
||||
serde_json = "1.0.114"
|
||||
serde_repr = "0.1.18"
|
||||
hostname = "0.3.1"
|
20
pkgs/afvalcalendar/default.nix
Normal file
20
pkgs/afvalcalendar/default.nix
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
pkgs,
|
||||
rustPlatform,
|
||||
...
|
||||
}:
|
||||
rustPlatform.buildRustPackage {
|
||||
pname = "afvalcalendar";
|
||||
version = "0.1.0";
|
||||
src = ./.;
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
pkg-config
|
||||
];
|
||||
|
||||
buildInputs = with pkgs; [
|
||||
openssl
|
||||
];
|
||||
|
||||
cargoHash = "sha256-JXx6aUKdKbUTBCwlBw5i1hZy8ofCfSrhLCwFzqdA8cI=";
|
||||
}
|
43
pkgs/afvalcalendar/src/calendar.rs
Normal file
43
pkgs/afvalcalendar/src/calendar.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use chrono::{Duration, NaiveDate};
|
||||
use icalendar::{Alarm, Calendar, Component, Event, EventLike, Property};
|
||||
|
||||
use crate::trash::TrashType;
|
||||
|
||||
pub(crate) fn calendar_from_pickup_dates(dates: Vec<(TrashType, NaiveDate)>) -> Calendar {
|
||||
let mut ical = Calendar::new();
|
||||
ical.name("Twente Milieu Afvalkalender");
|
||||
|
||||
let events = dates.iter().map(|date| {
|
||||
let description = match date.0 {
|
||||
TrashType::Grey => "Restafval wordt opgehaald",
|
||||
TrashType::Green => "GFT wordt opgehaald",
|
||||
TrashType::Paper => "Papier wordt opgehaald",
|
||||
TrashType::Packages => "Verpakkingen worden opgehaald",
|
||||
};
|
||||
|
||||
let color = Property::new(
|
||||
"COLOR",
|
||||
match date.0 {
|
||||
TrashType::Grey => "darkgray",
|
||||
TrashType::Green => "darkgreen",
|
||||
TrashType::Paper => "royalblue",
|
||||
TrashType::Packages => "darkorange",
|
||||
},
|
||||
);
|
||||
|
||||
let reminder = Alarm::display(description, -Duration::hours(5));
|
||||
|
||||
Event::new()
|
||||
.all_day(date.1)
|
||||
.summary(description)
|
||||
.append_property(color)
|
||||
.alarm(reminder)
|
||||
.done()
|
||||
});
|
||||
|
||||
for event in events {
|
||||
ical.push(event);
|
||||
}
|
||||
|
||||
ical.done()
|
||||
}
|
10
pkgs/afvalcalendar/src/main.rs
Normal file
10
pkgs/afvalcalendar/src/main.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
mod calendar;
|
||||
mod trash;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let dates = trash::get_pickup_dates().await.unwrap();
|
||||
let calendar = calendar::calendar_from_pickup_dates(dates);
|
||||
|
||||
calendar.print().unwrap();
|
||||
}
|
59
pkgs/afvalcalendar/src/trash.rs
Normal file
59
pkgs/afvalcalendar/src/trash.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
use chrono::{Months, NaiveDate, NaiveDateTime, Utc};
|
||||
use serde::Deserialize;
|
||||
use serde_repr::Deserialize_repr;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct CalendarAPIDatum {
|
||||
pickup_dates: Vec<NaiveDateTime>,
|
||||
pickup_type: TrashType,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct CalendarAPIResponse {
|
||||
data_list: Vec<CalendarAPIDatum>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Deserialize_repr, Debug)]
|
||||
#[repr(u8)]
|
||||
pub(crate) enum TrashType {
|
||||
Grey = 0,
|
||||
Green = 1,
|
||||
Paper = 2,
|
||||
Packages = 10,
|
||||
}
|
||||
|
||||
pub(crate) async fn get_pickup_dates() -> Result<Vec<(TrashType, NaiveDate)>, reqwest::Error> {
|
||||
let today = Utc::now().date_naive();
|
||||
let next_month = (today + Months::new(1)).to_string();
|
||||
let today = today.to_string();
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let params = [
|
||||
("companyCode", "8d97bb56-5afd-4cbc-a651-b4f7314264b4"),
|
||||
("uniqueAddressID", "1300002485"),
|
||||
("startDate", &today),
|
||||
("endDate", &next_month),
|
||||
];
|
||||
|
||||
let calendar = client
|
||||
.post("https://twentemilieuapi.ximmio.com/api/GetCalendar")
|
||||
.form(¶ms)
|
||||
.send()
|
||||
.await?
|
||||
.json::<CalendarAPIResponse>()
|
||||
.await?;
|
||||
|
||||
Ok(calendar
|
||||
.data_list
|
||||
.iter()
|
||||
.flat_map(|datum| {
|
||||
datum
|
||||
.pickup_dates
|
||||
.iter()
|
||||
.map(|date| (datum.pickup_type, NaiveDate::from(*date)))
|
||||
})
|
||||
.collect::<Vec<(TrashType, NaiveDate)>>())
|
||||
}
|
4
pkgs/afvalcalendar/test.rest
Normal file
4
pkgs/afvalcalendar/test.rest
Normal file
|
@ -0,0 +1,4 @@
|
|||
POST https://twentemilieuapi.ximmio.com/api/GetCalendar
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
companyCode=8d97bb56-5afd-4cbc-a651-b4f7314264b4&uniqueAddressID=1300002485&startDate=2024-02-01&endDate=2024-02-29
|
|
@ -10,6 +10,7 @@ in
|
|||
prometheus-fail2ban-exporter = callPackage ./prometheus/fail2ban-exporter.nix {
|
||||
sources = pkgs.callPackage ./_sources_pkgs/generated.nix {};
|
||||
};
|
||||
afvalcalendar = callPackage ./afvalcalendar {};
|
||||
}
|
||||
// (
|
||||
# Add nextcloud apps
|
||||
|
|
Loading…
Reference in a new issue