Home

dotnix @main - refs - log -
-
https://git.jolheiser.com/dotnix.git
My nix dotfiles
tree log patch
feat: miniserve Signed-off-by: jolheiser <git@jolheiser.com>
Signature
-----BEGIN SSH SIGNATURE----- U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgBTEvCQk6VqUAdN2RuH6bj1dNkY oOpbPWj+jw4ua1B1cAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5 AAAAQDsxHXc5R7P3DQFYje9qgVThoO/GKk+18V9S02dtVDn0vwpGPEGotUsW+02lUOii4I 9QXWk6M0C9SAIBROFsHwo= -----END SSH SIGNATURE-----
jolheiser <git@jolheiser.com>
1 week ago
5 changed files, 467 additions(+), 2 deletions(-)
M flake.nix -> flake.nix
diff --git a/flake.nix b/flake.nix
index 96f523e90d6ccb7105ad595542dc18408e116d5c..0b7b2e8682be01345b3744103adbf6e84aff6d1b 100644
--- a/flake.nix
+++ b/flake.nix
@@ -250,6 +250,8 @@               inputs.foundry.nixosModules.foundryvtt
               inputs.cfg-playground.nixosModules.default
               ./modules/tclip
 {
+            bennet = inputs.bennet.packages.${prev.system}.default;
+{
     let
             ];
             services.tclip.package = inputs.tclip.packages.${pkgs.system}.tclipd;
M machines/dragonwell/caddy.nix -> machines/dragonwell/caddy.nix
diff --git a/machines/dragonwell/caddy.nix b/machines/dragonwell/caddy.nix
index 68fdeee14cc968bd31143f08df953e639fedabf4..2ef0c23a555ab0e9ec1fdf325c7df4c3b0329ff3 100644
--- a/machines/dragonwell/caddy.nix
+++ b/machines/dragonwell/caddy.nix
@@ -81,8 +81,6 @@       "dnd.jolheiser.com".extraConfig = ''
         reverse_proxy localhost:30000
       '';
   services.caddy = {
-{
-  services.caddy = {
   services.caddy = {
   };
 }
M machines/dragonwell/default.nix -> machines/dragonwell/default.nix
diff --git a/machines/dragonwell/default.nix b/machines/dragonwell/default.nix
index 6c7d7a9668992cffca4039a087ce287e8469502a..c452874938790858dfe20d7c4d7b1259170175da 100644
--- a/machines/dragonwell/default.nix
+++ b/machines/dragonwell/default.nix
@@ -12,6 +12,7 @@     ./foundry.nix
     ./git-pr.nix
     ./golink.nix
     ./gotosocial.nix
+    ./miniserve.nix
     ./restic.nix
     ./soju.nix
     ./tandoor.nix
I machines/dragonwell/miniserve.nix
diff --git a/machines/dragonwell/miniserve.nix b/machines/dragonwell/miniserve.nix
new file mode 100644
index 0000000000000000000000000000000000000000..8905b1380508bd53125223ee2bf8cd2330961d3e
--- /dev/null
+++ b/machines/dragonwell/miniserve.nix
@@ -0,0 +1,26 @@
+{
+  services = {
+    miniserve = {
+      enable = true;
+      port = 3453;
+      showHidden = true;
+      uploadFiles = "";
+      mkdir = true;
+      overwriteFiles = true;
+      enableTar = true;
+      enableTarGz = true;
+      enableZip = true;
+      dirsFirst = true;
+      title = "Files";
+      hideThemeSelector = true;
+      hideVersionFooter = true;
+      readme = true;
+    };
+    tailproxy.miniserve = {
+      enable = true;
+      hostname = "files";
+      port = 3453;
+      authKey = "tskey-auth-kNNZJXfSDb11CNTRL-DsdZPygdA7Lrye5WJjnr6LGNffgzo3PUH"; # One-time key
+    };
+  };
+}
I modules/miniserve/default.nix
diff --git a/modules/miniserve/default.nix b/modules/miniserve/default.nix
new file mode 100644
index 0000000000000000000000000000000000000000..9b6fcf6ad28f41d9508002736b1050ab9d564fb3
--- /dev/null
+++ b/modules/miniserve/default.nix
@@ -0,0 +1,438 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+let
+  cfg = config.services.miniserve;
+  inherit (lib)
+    mkEnableOption
+    mkOption
+    mkIf
+    types
+    optionalString
+    concatMapStringsSep
+    concatStringsSep
+    ;
+in
+{
+  options.services.miniserve = {
+    enable = mkEnableOption "miniserve service";
+
+    package = mkOption {
+      type = types.package;
+      description = "miniserve package to use";
+      default = pkgs.miniserve;
+    };
+
+    user = mkOption {
+      type = types.str;
+      default = "miniserve";
+      description = "User account for miniserve service";
+    };
+
+    group = mkOption {
+      type = types.str;
+      default = "miniserve";
+      description = "Group for miniserve service";
+    };
+
+    path = mkOption {
+      type = types.str;
+      default = "/var/lib/miniserve";
+      description = "Which path to serve";
+    };
+
+    port = mkOption {
+      type = types.port;
+      default = 8080;
+      description = "Port to use";
+    };
+
+    interfaces = mkOption {
+      type = types.listOf types.str;
+      default = [ "127.0.0.1" ];
+      description = "Interface to listen on";
+    };
+
+    verbose = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Be verbose, includes emitting access logs";
+    };
+
+    indexFile = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        The name of a directory index file to serve, like "index.html"
+
+        Normally, when miniserve serves a directory, it creates a listing for that directory. However, if a directory
+        contains this file, miniserve will serve that file instead.
+      '';
+    };
+
+    spa = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Activate SPA (Single Page Application) mode
+
+        This will cause the file given by --index to be served for all non-existing file paths. In effect, this will serve
+        the index file whenever a 404 would otherwise occur in order to allow the SPA router to handle the request instead.
+      '';
+    };
+
+    prettyUrls = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Activate Pretty URLs mode
+
+        This will cause the server to serve the equivalent `.html` file indicated by the path.
+
+        `/about` will try to find `about.html` and serve it.
+      '';
+    };
+
+    auth = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = ''
+        Set authentication
+
+        Currently supported formats:
+        username:password, username:sha256:hash, username:sha512:hash
+        (e.g. joe:123, joe:sha256:a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3)
+      '';
+    };
+
+    authFile = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = ''
+        Read authentication values from a file
+
+        Example file content:
+
+        joe:123
+        bob:sha256:a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3
+        bill:
+      '';
+    };
+
+    routePrefix = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = "Use a specific route prefix";
+    };
+
+    randomRoute = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Generate a random 6-hexdigit route";
+    };
+
+    hideSymlinks = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Hide symlinks in listing and prevent them from being followed";
+    };
+
+    showHidden = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Show hidden files";
+    };
+
+    sortingMethod = mkOption {
+      type = types.enum [
+        "name"
+        "size"
+        "date"
+      ];
+      default = "name";
+      description = ''
+        Default sorting method for file list
+
+        Possible values:
+        - name: Sort by name
+        - size: Sort by size
+        - date: Sort by last modification date (natural sort: follows alphanumerical order)
+      '';
+    };
+
+    sortingOrder = mkOption {
+      type = types.enum [
+        "asc"
+        "desc"
+      ];
+      default = "desc";
+      description = ''
+        Default sorting order for file list
+
+        Possible values:
+        - asc:  Ascending order
+        - desc: Descending order
+      '';
+    };
+
+    colorScheme = mkOption {
+      type = types.enum [
+        "squirrel"
+        "archlinux"
+        "zenburn"
+        "monokai"
+      ];
+      default = "squirrel";
+      description = ''
+        Default color scheme
+
+        Possible values: squirrel, archlinux, zenburn, monokai
+      '';
+    };
+
+    colorSchemeDark = mkOption {
+      type = types.enum [
+        "squirrel"
+        "archlinux"
+        "zenburn"
+        "monokai"
+      ];
+      default = "archlinux";
+      description = ''
+        Default color scheme
+
+        Possible values: squirrel, archlinux, zenburn, monokai
+      '';
+    };
+
+    qrcode = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable QR code display";
+    };
+
+    uploadFiles = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = "Enable file uploading (and optionally specify for which directory)";
+    };
+
+    mkdir = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable creating directories";
+    };
+
+    mediaType = mkOption {
+      type = types.nullOr (
+        types.enum [
+          "image"
+          "audio"
+          "video"
+        ]
+      );
+      default = null;
+      description = ''
+        Specify uploadable media types
+
+        Possible values: image, audio, video
+      '';
+    };
+
+    rawMediaType = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = "Directly specify the uploadable media type expression";
+    };
+
+    overwriteFiles = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable overriding existing files during file upload";
+    };
+
+    enableTar = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable uncompressed tar archive generation";
+    };
+
+    enableTarGz = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable gz-compressed tar archive generation";
+    };
+
+    enableZip = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enable zip archive generation
+
+        WARNING: Zipping large directories can result in out-of-memory exception because zip generation is done in memory
+        and cannot be sent on the fly
+      '';
+    };
+
+    compressResponse = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Compress response
+
+        WARNING: Enabling this option may slow down transfers due to CPU overhead, so it is disabled by default.
+
+        Only enable this option if you know that your users have slow connections or if you want to minimize your server's bandwidth usage.
+      '';
+    };
+
+    dirsFirst = mkOption {
+      type = types.bool;
+      default = false;
+      description = "List directories first";
+    };
+
+    title = mkOption {
+      type = types.nullOr types.str;
+      default = null;
+      description = "Shown instead of host in page title and heading";
+    };
+
+    headers = mkOption {
+      type = types.listOf types.str;
+      default = [ ];
+      description = ''
+        Inserts custom headers into the responses. Specify each header as a 'Header:Value' pair.
+        This parameter can be used multiple times to add multiple headers.
+
+        Example:
+        --header "Header1:Value1" --header "Header2:Value2"
+        (If a header is already set or previously inserted, it will not be overwritten.)
+      '';
+    };
+
+    showSymlinkInfo = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Visualize symlinks in directory listing";
+    };
+
+    hideVersionFooter = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Hide version footer";
+    };
+
+    hideThemeSelector = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Hide theme selector";
+    };
+
+    showWgetFooter = mkOption {
+      type = types.bool;
+      default = false;
+      description = "If enabled, display a wget command to recursively download the current directory";
+    };
+
+    tlsCert = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = "TLS certificate to use";
+    };
+
+    tlsKey = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = "TLS private key to use";
+    };
+
+    readme = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable README.md rendering in directories";
+    };
+
+    disableIndexing = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Disable indexing
+
+        This will prevent directory listings from being generated and return an error instead.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.miniserve = {
+      description = "Miniserve File Server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        ExecStart =
+          let
+            args = [
+              (optionalString cfg.verbose "-v")
+              (optionalString (cfg.indexFile != null) "--index '${cfg.indexFile}'")
+              (optionalString cfg.spa "--spa")
+              (optionalString cfg.prettyUrls "--pretty-urls")
+              "-p ${toString cfg.port}"
+              (concatMapStringsSep " " (i: "-i ${i}") cfg.interfaces)
+              (optionalString (cfg.auth != null) "-a '${cfg.auth}'")
+              (optionalString (cfg.authFile != null) "--auth-file ${cfg.authFile}")
+              (optionalString (cfg.routePrefix != null) "--route-prefix '${cfg.routePrefix}'")
+              (optionalString cfg.randomRoute "--random-route")
+              (optionalString cfg.hideSymlinks "-P")
+              (optionalString cfg.showHidden "-H")
+              "-S ${cfg.sortingMethod}"
+              "-O ${cfg.sortingOrder}"
+              "-c ${cfg.colorScheme}"
+              "-d ${cfg.colorSchemeDark}"
+              (optionalString cfg.qrcode "-q")
+              (optionalString (cfg.uploadFiles != null) (
+                if (cfg.uploadFiles != "") then "-u '${cfg.uploadFiles}'" else "-u"
+              ))
+              (optionalString cfg.mkdir "-U")
+              (optionalString (cfg.mediaType != null) "-m ${cfg.mediaType}")
+              (optionalString (cfg.rawMediaType != null) "-M '${cfg.rawMediaType}'")
+              (optionalString cfg.overwriteFiles "-o")
+              (optionalString cfg.enableTar "-r")
+              (optionalString cfg.enableTarGz "-g")
+              (optionalString cfg.enableZip "-z")
+              (optionalString cfg.compressResponse "-C")
+              (optionalString cfg.dirsFirst "-D")
+              (optionalString (cfg.title != null) "-t '${cfg.title}'")
+              (concatMapStringsSep " " (h: "--header '${h}'") cfg.headers)
+              (optionalString cfg.showSymlinkInfo "-l")
+              (optionalString cfg.hideVersionFooter "-F")
+              (optionalString cfg.hideThemeSelector "--hide-theme-selector")
+              (optionalString cfg.showWgetFooter "-W")
+              (optionalString (cfg.tlsCert != null) "--tls-cert ${cfg.tlsCert}")
+              (optionalString (cfg.tlsKey != null) "--tls-key ${cfg.tlsKey}")
+              (optionalString cfg.readme "--readme")
+              (optionalString cfg.disableIndexing "-I")
+              cfg.path
+            ];
+          in
+          "${pkgs.miniserve}/bin/miniserve ${concatStringsSep " " args}";
+        Restart = "on-failure";
+        User = cfg.user;
+        Group = cfg.group;
+      };
+    };
+
+    users.users.${cfg.user} = {
+      group = cfg.group;
+      home = cfg.path;
+      createHome = true;
+      isSystemUser = true;
+      isNormalUser = false;
+    };
+    users.groups.${cfg.group} = { };
+  };
+}