update documentation

This commit is contained in:
Jonas Heinrich 2025-08-18 14:57:49 +02:00
parent a1ea6e4609
commit e924bf1db6
4 changed files with 52 additions and 28 deletions

View file

@ -1,6 +1,7 @@
# eintopf-radar-sync # mail-quota-warning
Small script to sync events of an radar.quad.net group to a specific Eintopf
instance. Small script to check a configured list of IMAP accounts for mailbox quota and send
a warning mail in case a specific threashold is exceeded.
## Installation ## Installation
@ -11,7 +12,7 @@ Add the module to your `flake.nix`:
```nix ```nix
{ {
inputs = { inputs = {
eintopf-radar-sync.url = "git+https://git.project-insanity.org/onny/eintopf-radar-sync.git"; mail-quota-warning.url = "git+https://git.project-insanity.org/onny/mail-quota-warning.git";
[...] [...]
}; };
@ -21,12 +22,12 @@ Add the module to your `flake.nix`:
system = "x86_64-linux"; system = "x86_64-linux";
specialArgs.inputs = inputs; specialArgs.inputs = inputs;
modules = [ modules = [
inputs.eintopf-radar-sync.nixosModule inputs.mail-quota-warning.nixosModule
({ pkgs, ... }:{ ({ pkgs, ... }:{
nixpkgs.overlays = [ nixpkgs.overlays = [
inputs.eintopf-radar-sync.overlay inputs.mail-quota-warning.overlay
]; ];
}) })
@ -42,30 +43,26 @@ Add the module to your `flake.nix`:
Add this to your `configuration.nix` file Add this to your `configuration.nix` file
```nix ```nix
environment.etc."eintopf-radar-sync-secrets".text = '' environment.etc."eintopf-radar-sync-secrets.yml".text = ''
EINTOPF_AUTHORIZATION_TOKEN=foobar23 EINTOPF_AUTHORIZATION_TOKEN=foobar23
''; '';
services.eintopf-radar-sync = { services.mail-quota-warning = {
enable = true; enable = true;
settings = { settings = {
EINTOPF_URL = "https://karlsunruh.eintopf.info"; EINTOPF_URL = "https://karlsunruh.eintopf.info";
RADAR_GROUP_ID = "436012"; RADAR_GROUP_ID = "436012";
}; };
secrets = [ /etc/eintopf-radar-sync-secrets ]; secrets = [ /etc/mail-quota-warning-secrets.yml ];
}; };
``` ```
Replace setting variables according to your setup. Replace setting variables according to your setup.
Get the authorization token through login request in the Eintopf
Swagger api interface, for example
https://karlsunruh.project-insanity.org/api/v1/swagger#/auth/login
### From source ### From source
``` ```
cd eintopf-radar-sync cd mail-quota-warning
nix develop nix develop
export EINTOPF_URL = "https://karlsunruh.eintopf.info" export EINTOPF_URL = "https://karlsunruh.eintopf.info"
export EINTOPF_AUTHORIZATION_TOKEN = "secret key" export EINTOPF_AUTHORIZATION_TOKEN = "secret key"

View file

@ -1,5 +1,6 @@
check_interval_days: 7 # Minimum days between warnings for same account check_interval_days: 7 # Minimum days between warnings for same account
quota_warning_threshold_percent: 80 quota_warning_threshold_percent: 80
working_dir: /var/lib/mail-quota-warning
accounts: accounts:
- name: Sales - name: Sales

View file

@ -5,30 +5,55 @@ import yaml
import json import json
import os import os
import threading import threading
import argparse
from email.mime.text import MIMEText from email.mime.text import MIMEText
from datetime import datetime, timedelta from datetime import datetime, timedelta
CONFIG_FILE = "config.yml" CONFIG_FILE = "config.yml"
STATE_FILE = "quota_state.json" STATE_FILE = "quota_state.json"
# TODO: # TODO
# - make working dir configurable for state file # - load config from file
# - optional read config.yml from command line argument # - override with env vars
# - note in warning mail that it exceeded XX% quota threashold
# - fix list summary mark all accounts which are critical
def load_config(): def get_config_value(config, env_var, config_key, default_value, value_type=int):
with open(CONFIG_FILE, "r") as f: """Get configuration value from environment variable or config file, with fallback to default"""
env_value = os.environ.get(env_var)
if env_value is not None:
try:
return value_type(env_value)
except ValueError:
log(f"Invalid value for {env_var}: {env_value}, using config/default")
return config.get(config_key, default_value)
def parse_args():
parser = argparse.ArgumentParser(description="Email quota monitoring script")
parser.add_argument(
"--config",
default="config.yml",
help="Path to config.yml file (default: config.yml in current directory)"
)
return parser.parse_args()
def load_config(config_file):
if not os.path.exists(config_file):
log(f"Config file not found: {config_file}")
raise FileNotFoundError(f"Config file not found: {config_file}")
with open(config_file, "r") as f:
return yaml.safe_load(f) return yaml.safe_load(f)
def load_state(): def load_state():
if os.path.exists(STATE_FILE): state_file = "quota_state.json"
with open(STATE_FILE, "r") as f: if os.path.exists(state_file):
with open(state_file, "r") as f:
return json.load(f) return json.load(f)
return {} return {}
def save_state(state): def save_state(state):
with open(STATE_FILE, "w") as f: state_file = "quota_state.json"
with open(state_file, "w") as f:
json.dump(state, f, indent=2) json.dump(state, f, indent=2)
def log(msg): def log(msg):
@ -235,7 +260,8 @@ def check_account_quota(account, config, state, threshold, interval_days):
return None, quota_info return None, quota_info
def main(): def main():
config = load_config() args = parse_args()
config = load_config(args.config)
state = load_state() state = load_state()
interval_days = config.get("check_interval_days", 7) interval_days = config.get("check_interval_days", 7)
threshold = config.get("quota_warning_threshold_percent", 80) threshold = config.get("quota_warning_threshold_percent", 80)

View file

@ -21,14 +21,14 @@ in
type = lib.types.submodule { type = lib.types.submodule {
freeformType = with lib.types; attrsOf types.str; freeformType = with lib.types; attrsOf types.str;
options = { options = {
EINTOPF_URL = lib.mkOption { CHECK_INTERVAL_DAYS = lib.mkOption {
default = ""; default = "";
type = lib.types.str; type = lib.types.str;
description = '' description = ''
Base URL of the target Eintopf host. Base URL of the target Eintopf host.
''; '';
}; };
RADAR_GROUP_ID = lib.mkOption { QUOTA_WARNING_THRESHOLD_PERCENT = lib.mkOption {
default = ""; default = "";
type = lib.types.str; type = lib.types.str;
description = '' description = ''
@ -49,7 +49,7 @@ in
''; '';
}; };
secrets = lib.mkOption { secretFile = lib.mkOption {
type = with lib.types; listOf path; type = with lib.types; listOf path;
description = '' description = ''
A list of files containing the various secrets. Should be in the A list of files containing the various secrets. Should be in the