141 lines
6.4 KiB
Nix
141 lines
6.4 KiB
Nix
let
|
|
lib = import <nixpkgs/lib>;
|
|
|
|
allowedEndpointKeys = [ "type" "domain" "endpoint" "port" "force_ssl" "content" ];
|
|
allowedProxyContentKeys = [ "type" "ip" "port" "proxyWebsockets" ];
|
|
allowedWebContentKeys = [ "type" "files" ];
|
|
allowedWebFileKeys = [ "path" "filePath" "contentType" "status" ];
|
|
|
|
ensureNoUnknownKeys = context: obj: allowedKeys:
|
|
let
|
|
unknown = builtins.filter (key: !(lib.elem key allowedKeys)) (builtins.attrNames obj);
|
|
in
|
|
if unknown != [] then
|
|
throw "${context} contains unknown keys: ${builtins.concatStringsSep ", " unknown}"
|
|
else
|
|
obj;
|
|
|
|
ensurePath = value:
|
|
builtins.isString value
|
|
&& builtins.substring 0 1 value == "/";
|
|
|
|
ensurePort = value:
|
|
builtins.isInt value && value > 0 && value <= 65535;
|
|
|
|
validateEndpointShape = index: endpoint:
|
|
let
|
|
_ = if !builtins.isAttrs endpoint then throw "Endpoint at index ${toString index} must be an attrset." else null;
|
|
__ = ensureNoUnknownKeys "Endpoint at index ${toString index}" endpoint allowedEndpointKeys;
|
|
___ =
|
|
if endpoint ? forceSsl then
|
|
throw "Endpoint at index ${toString index} uses forceSsl. Use force_ssl instead."
|
|
else
|
|
null;
|
|
typeValue =
|
|
if endpoint ? type && builtins.isString endpoint.type then
|
|
endpoint.type
|
|
else
|
|
throw "Endpoint at index ${toString index} must define type as a string.";
|
|
_type =
|
|
if lib.elem typeValue [ "proxy" "web" ] then
|
|
null
|
|
else
|
|
throw "Endpoint at index ${toString index} type must be 'proxy' or 'web'.";
|
|
_domain =
|
|
if endpoint ? domain && builtins.isString endpoint.domain && endpoint.domain != "" then
|
|
null
|
|
else
|
|
throw "Endpoint at index ${toString index} must define a non-empty domain string.";
|
|
_endpoint =
|
|
if endpoint ? endpoint && ensurePath endpoint.endpoint then
|
|
null
|
|
else
|
|
throw "Endpoint at index ${toString index} must define endpoint as a path starting with '/'.";
|
|
_port =
|
|
if endpoint ? port && ensurePort endpoint.port then
|
|
null
|
|
else
|
|
throw "Endpoint at index ${toString index} must define port as int in range 1..65535.";
|
|
_forceSsl =
|
|
if endpoint ? force_ssl && builtins.isBool endpoint.force_ssl then
|
|
null
|
|
else
|
|
throw "Endpoint at index ${toString index} must define force_ssl as a bool.";
|
|
contentValue =
|
|
if endpoint ? content && builtins.isAttrs endpoint.content then
|
|
endpoint.content
|
|
else
|
|
throw "Endpoint at index ${toString index} must define content as an attrset.";
|
|
_content =
|
|
if typeValue == "proxy" then
|
|
let
|
|
____ = ensureNoUnknownKeys "Proxy content at endpoint index ${toString index}" contentValue allowedProxyContentKeys;
|
|
in
|
|
if !(contentValue ? type) || !builtins.isString contentValue.type then
|
|
throw "Proxy endpoint at index ${toString index} must define content.type as a string."
|
|
else if !(contentValue ? ip) || !builtins.isString contentValue.ip || contentValue.ip == "" then
|
|
throw "Proxy endpoint at index ${toString index} must define content.ip as a non-empty string."
|
|
else if !(contentValue ? port) || !ensurePort contentValue.port then
|
|
throw "Proxy endpoint at index ${toString index} must define content.port as int in range 1..65535."
|
|
else if !(contentValue ? proxyWebsockets) || !builtins.isBool contentValue.proxyWebsockets then
|
|
throw "Proxy endpoint at index ${toString index} must define content.proxyWebsockets as a bool."
|
|
else
|
|
null
|
|
else
|
|
let
|
|
____ = ensureNoUnknownKeys "Web content at endpoint index ${toString index}" contentValue allowedWebContentKeys;
|
|
filesValue =
|
|
if contentValue ? files && builtins.isList contentValue.files then
|
|
contentValue.files
|
|
else
|
|
throw "Web endpoint at index ${toString index} must define content.files as a list.";
|
|
_____ =
|
|
if contentValue ? type && contentValue.type == "store" then
|
|
null
|
|
else
|
|
throw "Web endpoint at index ${toString index} must define content.type = 'store'.";
|
|
______ = lib.imap0 (fileIndex: file:
|
|
let
|
|
_______ =
|
|
if builtins.isAttrs file then
|
|
null
|
|
else
|
|
throw "Web endpoint at index ${toString index} file at position ${toString fileIndex} must be an attrset.";
|
|
________ = ensureNoUnknownKeys "Web endpoint at index ${toString index} file at position ${toString fileIndex}" file allowedWebFileKeys;
|
|
_________ =
|
|
if file ? path && builtins.isString file.path && file.path != "" && builtins.substring 0 1 file.path != "/" then
|
|
null
|
|
else
|
|
throw "Web endpoint at index ${toString index} file at position ${toString fileIndex} must define path as a relative path string.";
|
|
__________ =
|
|
if file ? filePath && (builtins.isString file.filePath || builtins.isPath file.filePath) then
|
|
null
|
|
else
|
|
throw "Web endpoint at index ${toString index} file at position ${toString fileIndex} must define filePath as string or path.";
|
|
___________ =
|
|
if file ? contentType && builtins.isString file.contentType && file.contentType != "" then
|
|
null
|
|
else
|
|
throw "Web endpoint at index ${toString index} file at position ${toString fileIndex} must define contentType as non-empty string.";
|
|
____________ =
|
|
if file ? status && builtins.isInt file.status then
|
|
null
|
|
else
|
|
throw "Web endpoint at index ${toString index} file at position ${toString fileIndex} must define status as int.";
|
|
in
|
|
file
|
|
) filesValue;
|
|
in
|
|
null;
|
|
in
|
|
endpoint;
|
|
|
|
validateEndpointsShape = endpoints:
|
|
if !builtins.isList endpoints then
|
|
throw "config/endpoints.nix must evaluate to a list of endpoint attrsets."
|
|
else
|
|
lib.imap0 validateEndpointShape endpoints;
|
|
in
|
|
{
|
|
inherit validateEndpointShape validateEndpointsShape;
|
|
}
|