main
  1{lib, ...}: {
  2  den.aspects.services.grafana = {
  3    settings.host = {
  4      domain = lib.mkOption {
  5        type = lib.types.str;
  6      };
  7      address = lib.mkOption {
  8        type = lib.types.str;
  9        default = "127.0.0.1";
 10      };
 11      port = lib.mkOption {
 12        type = lib.types.port;
 13        default = 9251;
 14      };
 15      prometheus = lib.mkOption {
 16        type = lib.types.submodule {
 17          options.url = lib.mkOption {
 18            type = lib.types.nullOr lib.types.str;
 19            default = null;
 20          };
 21        };
 22        default = {};
 23      };
 24    };
 25
 26    persist = {
 27      directories = [
 28        {
 29          directory = "/var/lib/grafana";
 30          user = "grafana";
 31          group = "grafana";
 32          mode = "0700";
 33        }
 34      ];
 35    };
 36
 37    reverseProxy = {host, ...}: let
 38      cfg = host.settings.services.grafana;
 39    in {
 40      ${cfg.domain} = {
 41        port = cfg.port;
 42      };
 43    };
 44
 45    backup = {
 46      pkgs,
 47      lib,
 48      ...
 49    }: {
 50      grafana = let
 51        backupDir = "/tmp/backups/grafana";
 52      in {
 53        paths = [backupDir];
 54
 55        # See https://grafana.com/docs/grafana/latest/administration/back-up-grafana/
 56        backupPrepareCommand = lib.getExe (pkgs.writeShellApplication {
 57          name = "backup-grafana-prepare";
 58          runtimeInputs = [
 59            pkgs.sqlite
 60          ];
 61          text = ''
 62            set -euo pipefail
 63
 64            DATA_DIR="/var/lib/grafana/data"
 65
 66            mkdir -p "${backupDir}"
 67
 68            sqlite3 "$DATA_DIR/grafana.db" ".backup '${backupDir}/grafana.db'"
 69
 70            if [ -d "$DATA_DIR/plugins" ]; then
 71              cp -a "$DATA_DIR/plugins" "${backupDir}/"
 72            fi
 73          '';
 74        });
 75        backupCleanupCommand = ''
 76          rm -r "${backupDir}"
 77        '';
 78      };
 79    };
 80
 81    nixos = {
 82      host,
 83      config,
 84      ...
 85    }: let
 86      cfg = host.settings.services.grafana;
 87    in {
 88      services.grafana = {
 89        enable = true;
 90        settings = {
 91          server = {
 92            http_addr = cfg.address;
 93            http_port = cfg.port;
 94            protocol = "http";
 95            domain = cfg.domain;
 96            serve_from_sub_path = false;
 97            root_url = "%(protocol)s://%(domain)s:%(http_port)s/";
 98            read_timeout = "180s";
 99            enable_gzip = true;
100          };
101          security = {
102            secret_key = "$__file{${config.vaultix.secrets."grafana-security-key".path}}";
103            admin_user = "hpcesia";
104            admin_email = "me@hpcesia.com";
105            admin_password = "$__file{${config.vaultix.secrets."grafana-admin-password".path}}";
106          };
107          users = {
108            allow_sign_up = false;
109            default_theme = "system";
110            default_language = "detect";
111          };
112          analytics = {
113            reporting_enabled = false;
114            check_for_updates = false;
115            check_for_plugin_updates = false;
116          };
117        };
118        provision = {
119          enable = true;
120          datasources.settings.datasources = lib.optional (cfg.prometheus.url != null) {
121            name = "Prometheus";
122            type = "prometheus";
123            access = "proxy";
124            url = cfg.prometheus.url;
125            jsonData = {
126              httpMethod = "POST";
127              timeInterval = "30s";
128              manageAlerts = true;
129              prometheusType = "Prometheus";
130              prometheusVersion = "2.49.0";
131              cacheLevel = "High";
132              disableRecordingRules = false;
133              incrementalQueryOverlapWindow = "10m";
134            };
135          };
136          dashboards.settings.providers = [
137            {
138              name = "Nix";
139              type = "file";
140              options.path = ./dashboards;
141            }
142          ];
143        };
144      };
145
146      vaultix.secrets = {
147        grafana-admin-password = {
148          file = ./admin-password.age;
149          owner = "grafana";
150          group = "grafana";
151          mode = "0440";
152        };
153        grafana-security-key = {
154          file = ./security-key.age;
155          owner = "grafana";
156          group = "grafana";
157          mode = "0440";
158        };
159      };
160    };
161  };
162}