dotnix @main -
refs -
log -
-
https://git.jolheiser.com/dotnix.git
My nix dotfiles
feat: miniserve
Signed-off-by: jolheiser <git@jolheiser.com>
Signature
-----BEGIN SSH SIGNATURE-----
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgBTEvCQk6VqUAdN2RuH6bj1dNkY
oOpbPWj+jw4ua1B1cAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
AAAAQDsxHXc5R7P3DQFYje9qgVThoO/GKk+18V9S02dtVDn0vwpGPEGotUsW+02lUOii4I
9QXWk6M0C9SAIBROFsHwo=
-----END SSH SIGNATURE-----
5 changed files, 467 additions(+), 2 deletions(-)
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;
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 = {
};
}
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
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
+ };
+ };
+}
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} = { };
+ };
+}