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