feat: add livekit

This commit is contained in:
Katharina Heidenreich 2026-04-05 20:25:28 +02:00
parent fb98563bb6
commit eee6905637
9 changed files with 384 additions and 109 deletions

View file

@ -1,78 +1,63 @@
let
lib = import <nixpkgs/lib>;
tunnelPorts = import ./tunnel_ports.nix;
allowedEndpointKeys = [ "type" "listenPort" "content" ];
allowedContentKeys = [ "host" "port" ];
assertAttrset = context: value:
if builtins.isAttrs value then
value
else
throw "${context} must be an attrset.";
ensureNoUnknownKeys = context: obj: allowedKeys:
assertString = context: value:
if builtins.isString value && value != "" then
value
else
throw "${context} must be a non-empty string.";
assertInt = context: value:
if builtins.isInt value then
value
else
throw "${context} must be an int.";
validateForwarding = index: endpoint:
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;
ensurePort = value:
builtins.isInt value && value > 0 && value <= 65535;
validateEndpointShape = index: endpoint:
let
_ = if builtins.isAttrs endpoint then null else throw "Endpoint at index ${toString index} must be an attrset.";
__ = ensureNoUnknownKeys "Endpoint at index ${toString index}" endpoint allowedEndpointKeys;
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 [ "forwarding" "proxy" ] then
null
else
throw "Endpoint at index ${toString index} type must be 'forwarding' or 'proxy'.";
_listenPort =
if endpoint ? listenPort && ensurePort endpoint.listenPort then
null
else
throw "Endpoint at index ${toString index} must define listenPort as int in range 1..65535.";
contentValue =
if endpoint ? content then
endpoint.content
else
throw "Endpoint at index ${toString index} must define content.";
_content =
let
_attrs = if builtins.isAttrs contentValue then null else throw "Endpoint at index ${toString index} must define content as an attrset.";
____ = ensureNoUnknownKeys "Endpoint content at index ${toString index}" contentValue allowedContentKeys;
in
if !(contentValue ? port) || !ensurePort contentValue.port then
throw "Endpoint at index ${toString index} must define content.port as int in range 1..65535."
else if typeValue == "proxy" && (!(contentValue ? host) || !builtins.isString contentValue.host || contentValue.host == "") then
throw "Proxy endpoint at index ${toString index} must define content.host as non-empty string."
else if typeValue == "forwarding" && contentValue ? host then
throw "Forwarding endpoint at index ${toString index} must not define content.host."
else
null;
content = assertAttrset "config/endpoints.nix[${toString index}].content" endpoint.content;
_ = assertInt "config/endpoints.nix[${toString index}].content.port" content.port;
__ = if tunnelPorts.isAllowedTunnelPort content.port then null else throw "config/endpoints.nix[${toString index}].content.port is not in config/network.nix tunnel.allowedPorts.";
___ = if !(content ? tls) || builtins.isBool content.tls then null else throw "config/endpoints.nix[${toString index}].content.tls must be a bool.";
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;
validateUniqueHostPath = endpoints:
validateProxy = index: endpoint:
let
keyFor = endpoint: toString endpoint.listenPort;
keys = map keyFor endpoints;
content = assertAttrset "config/endpoints.nix[${toString index}].content" endpoint.content;
_ = assertString "config/endpoints.nix[${toString index}].endpoint" endpoint.endpoint;
__ = assertString "config/endpoints.nix[${toString index}].content.host" content.host;
___ = assertInt "config/endpoints.nix[${toString index}].content.port" content.port;
____ = if !(endpoint ? force_ssl) || builtins.isBool endpoint.force_ssl then null else throw "config/endpoints.nix[${toString index}].force_ssl must be a bool.";
_____ = if !(content ? websocket) || builtins.isBool content.websocket then null else throw "config/endpoints.nix[${toString index}].content.websocket must be a bool.";
in
if builtins.length keys == builtins.length (lib.unique keys) then
endpoints
endpoint;
validateEndpoint = index: endpoint:
let
_ = assertAttrset "config/endpoints.nix[${toString index}]" endpoint;
__ = if endpoint ? type && (endpoint.type == "forwarding" || endpoint.type == "proxy") then null else throw "config/endpoints.nix[${toString index}].type must be \"forwarding\" or \"proxy\".";
___ = assertInt "config/endpoints.nix[${toString index}].listenPort" endpoint.listenPort;
____ = assertString "config/endpoints.nix[${toString index}].domain" endpoint.domain;
in
if endpoint.type == "forwarding" then
validateForwarding index endpoint
else
throw "Duplicate listenPort detected in config/endpoints.nix.";
validateProxy index endpoint;
getEndpointsConfig = endpoints:
if builtins.isList endpoints then
lib.imap0 validateEndpoint endpoints
else
throw "config/endpoints.nix must evaluate to a list.";
in
{
getValidatedEndpoints = endpoints:
validateUniqueHostPath (validateEndpointsShape endpoints);
inherit getEndpointsConfig;
}