mirror of https://git.jolheiser.com/dotnix.git
parent
e7ce7296dd
commit
14e1946478
|
@ -249,6 +249,7 @@
|
||||||
inputs.foundry.nixosModules.foundryvtt
|
inputs.foundry.nixosModules.foundryvtt
|
||||||
inputs.cfg-playground.nixosModules.default
|
inputs.cfg-playground.nixosModules.default
|
||||||
./modules/tclip
|
./modules/tclip
|
||||||
|
./modules/miniserve
|
||||||
./machines/dragonwell
|
./machines/dragonwell
|
||||||
];
|
];
|
||||||
services.tclip.package = inputs.tclip.packages.${pkgs.system}.tclipd;
|
services.tclip.package = inputs.tclip.packages.${pkgs.system}.tclipd;
|
||||||
|
|
|
@ -80,7 +80,6 @@ in
|
||||||
"dnd.jolheiser.com".extraConfig = ''
|
"dnd.jolheiser.com".extraConfig = ''
|
||||||
reverse_proxy localhost:30000
|
reverse_proxy localhost:30000
|
||||||
'';
|
'';
|
||||||
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ in
|
||||||
./git-pr.nix
|
./git-pr.nix
|
||||||
./golink.nix
|
./golink.nix
|
||||||
./gotosocial.nix
|
./gotosocial.nix
|
||||||
|
./miniserve.nix
|
||||||
./restic.nix
|
./restic.nix
|
||||||
./soju.nix
|
./soju.nix
|
||||||
./tandoor.nix
|
./tandoor.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
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -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} = { };
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue