infrastructure

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

simple-web-app.nix (3285B)


      1 {
      2   config,
      3   lib,
      4   pkgs,
      5   ...
      6 }:
      7 
      8 with lib;
      9 
     10 let
     11   cfg = config.services.simple-web-app;
     12 in
     13 {
     14   options = {
     15     services.simple-web-app = {
     16       enable = mkEnableOption "Simple Web App";
     17 
     18       package = mkOption {
     19         defaultText = lib.literalMD "`packages.default` from the simple-web-app flake";
     20       };
     21 
     22       addr = mkOption {
     23         type = types.str;
     24         default = "0.0.0.0:8000";
     25         description = "Address to bind the server to.";
     26       };
     27 
     28       dbPath = mkOption {
     29         type = types.str;
     30         default = "/var/lib/simple-web-app/data/app.db";
     31         description = "Path to the SQLite database file.";
     32       };
     33 
     34     };
     35   };
     36 
     37   config = mkIf cfg.enable {
     38     systemd.services.simple-web-app = {
     39       wantedBy = [ "multi-user.target" ];
     40       description = "Run simple web app.";
     41       after = [ "network.target" ];
     42       serviceConfig = {
     43         Type = "simple";
     44         ExecStart = "${cfg.package}/bin/simple-web-app";
     45         DynamicUser = true;
     46         StateDirectory = "simple-web-app";
     47         StateDirectoryMode = "0755";
     48       };
     49       environment = {
     50         ADDR = cfg.addr;
     51         DEBUG = "false";
     52         DB_PATH = cfg.dbPath;
     53         SMTP_ADDR = "localhost:25";
     54         SMTP_FROM = "noreply@silasbrack.com";
     55         SITE_URL = "https://simple.fnarglebeast.com";
     56       };
     57     };
     58 
     59     services.nginx.virtualHosts."simple.fnarglebeast.com" = {
     60       forceSSL = true;
     61       enableACME = true;
     62       locations."/" = {
     63         proxyPass = "http://127.0.0.1:8000";
     64         recommendedProxySettings = true;
     65       };
     66     };
     67 
     68 
     69     # Serve logs — firewall restricts to VPN (wg0) only
     70     networking.firewall.interfaces."wg0".allowedTCPPorts = [ 9090 ];
     71     services.nginx.virtualHosts."simple-web-app-logs" = {
     72       listen = [{ addr = "0.0.0.0"; port = 9090; }];
     73       locations."/" = {
     74         root = "/mnt/volume-hel1-1/logs";
     75         extraConfig = "autoindex on;";
     76       };
     77     };
     78 
     79     # Archive logs to volume
     80     systemd.services.log-collection = {
     81       script = let
     82         logDir = "/mnt/volume-hel1-1/logs";
     83       in ''
     84         PREVIOUS_HOUR=$(${pkgs.coreutils}/bin/date -d '1 hour ago 5 minutes ago' +%Y-%m-%dT%H:00:00)
     85         CURRENT_HOUR=$(${pkgs.coreutils}/bin/date -d '5 minutes ago' +%Y-%m-%dT%H:00:00)
     86         DATEPATH=$(date -d "$PREVIOUS_HOUR" +%Y/%m/%d/%H)
     87 
     88         # App logs (journald)
     89         mkdir -p ${logDir}/app/$(dirname $DATEPATH)
     90         journalctl -u simple-web-app \
     91           --since=$PREVIOUS_HOUR \
     92           --until=$CURRENT_HOUR \
     93           -o json | \
     94           ${pkgs.gzip}/bin/gzip \
     95           > ${logDir}/app/$DATEPATH.json.gz
     96 
     97         # Nginx access logs (raw lines, not JSON)
     98         mkdir -p ${logDir}/nginx/$(dirname $DATEPATH)
     99         ${pkgs.gawk}/bin/awk -v start="$(date -d "$PREVIOUS_HOUR" '+%d/%b/%Y:%H')" \
    100           '$0 ~ "\\[" start' /var/log/nginx/access.log | \
    101           ${pkgs.gzip}/bin/gzip \
    102           > ${logDir}/nginx/$DATEPATH.log.gz
    103       '';
    104       serviceConfig = {
    105         Type = "oneshot";
    106         User = "root";
    107       };
    108     };
    109     systemd.timers = {
    110       log-collection = {
    111         wantedBy = [ "timers.target" ];
    112         timerConfig = {
    113           OnCalendar = "hourly";
    114           Persistent = true;
    115           Unit = "log-collection.service";
    116         };
    117       };
    118     };
    119   };
    120 }