diff options
-rw-r--r-- | instance-options.nix | 200 | ||||
-rw-r--r-- | workadventure.nix | 218 |
2 files changed, 262 insertions, 156 deletions
diff --git a/instance-options.nix b/instance-options.nix index a2e77be..f7236da 100644 --- a/instance-options.nix +++ b/instance-options.nix @@ -16,7 +16,16 @@ in with wapkgs; { options = rec { + + settings = {}; + + backend = { + enable = mkOption { + default = true; + type = types.bool; + }; + httpPort = mkOption { default = 8081; type = types.ints.u16; @@ -38,6 +47,11 @@ with wapkgs; }; pusher = { + enable = mkOption { + default = true; + type = types.bool; + }; + port = mkOption { default = 8080; type = types.ints.u16; @@ -60,26 +74,43 @@ with wapkgs; description = "Front package to use"; }; - defaultMap = mkOption { - default = null; - defaultText = "not set"; - type = types.nullOr types.str; - description = "The url to the default map, which will be loaded if none is given in the url. Must be a reachable url relative to the public map url defined in `maps.url`."; + debugMode = mkOption { + default = false; + description = "Whether or not to run the frontend in debug mode"; + type = types.bool; }; - settings = mkOption { - default = {}; - type = types.attrsOf types.str; - description = "Settings for workadventure's frontend."; - example = { - stunServer = "stun:some.stunserver:3478"; - turnServer = "turn:some.turnserver"; - turnUser = "user"; - turnPassword = "password"; - }; - + startRoomUrl = mkOption { + default = "/_/global/localhost/maps/Floor0/floor0.json"; + description = "The workadventure map url that users join by default"; + type = types.str; }; - + + resolution = mkOption { + default = 2; + description = "resolution of workadventure"; + type = types.int; + }; + + zoomLevel = mkOption { + default = 1; + description = "The default zoom level of maps"; + type = types.int; + }; + + positionDelay = mkOption { + default = 200; + description = "Delay in milliseconds between sending position events"; + type = types.int; + }; + + maxExtrapolationTime = mkOption { + default = 100; + description = "Maximum time period in which movements of other players are extrapolated"; + type = types.int; + }; + + urls = { api = mkOption { default = "/pusher"; @@ -107,16 +138,99 @@ with wapkgs; }; }; - maps = { - path = mkOption { - default = workadventure.maps.outPath + "/workadventuremaps/"; - defaultText = "third_party.workadventure-nix.maps"; - type = types.path; - description = "Maps package to use"; + commonConfig = { + secretKey = mkOption { + default = "THECODINGMACHINE_SECRET_KEY"; + type = types.str; + }; + + minimumDistance = mkOption { + default = 64; + type = types.int; + }; + + groupRadius = mkOption { + default = 48; + type = types.int; + }; + + allowArtillery = mkOption { + default = false; + type = types.bool; + }; + + maxUsersPerRoom = mkOption { + default = 600; + type = types.int; + }; + + cpuOverheatThreshold = mkOption { + default = 80; + type = types.int; + }; + + socketIdleTime = mkOption { + default = 30; + type = types.int; + }; + + webrtc = { + stun = { + url = mkOption { + default = "stun:stun.l.google.com:19302"; + description = "The STUN server to use for peer connections"; + type = types.str; + }; + }; + turn = { + url = mkOption { + default = "turn:coturn.workadventure.localhost:3478"; + description = "The TURN server to use for peer connections"; + type = types.str; + }; + user = mkOption { + default = "workadventure"; + description = "Username for TURN authentication"; + type = types.str; # TODO: also allow no user + }; + password = mkOption { + default = "workadventure"; + description = "Password for TURN authentication"; + type = types.str; + }; + }; + }; + + jitsi = { + url = mkOption { + default = "meet.jit.si"; + description = "Jitsi instance to use for conference rooms"; + type = types.str; + }; + privateMode = mkOption { + default = false; + description = "Jitsi private mode"; + type = types.bool; + }; + iss = mkOption { + default = ""; + type = types.str; + }; + secretKey = mkOption { + default = ""; + type = types.str; + }; }; }; + nginx = { + enable = mkOption { + default = true; + type = types.bool; + description = "enable nginx as proxy, and for serving maps"; + }; + default = mkOption { default = false; type = types.bool; @@ -126,38 +240,20 @@ with wapkgs; domain = mkOption { default = "localhost"; type = types.str; - description = "The domain name to serve workadenture services under. Mutually exclusive with domains.X"; + description = "The domain name to serve workadenture services under."; }; - serveDefaultMaps = mkOption { - default = true; - type = types.bool; - description = "Whether to serve the maps provided by workadventure"; - }; - - domains = { - back = mkOption { - default = null; - type = types.nullOr types.str; - description = "The domain name to serve the backend under"; - }; - - pusher = mkOption { - default = null; - type = types.nullOr types.str; - description = "The domain name to serve the pusher under"; + maps = { + serve = mkOption { + default = true; + type = types.bool; + description = "Whether to serve maps through nginx."; }; - - maps = mkOption { - default = null; - type = types.nullOr types.str; - description = "The domain name to serve the maps under"; - }; - - front = mkOption { - default = null; - type = types.nullOr types.str; - description = "The domain name to serve the front under"; + path = mkOption { + default = workadventure.maps.outPath + "/workadventuremaps/"; + defaultText = "third_party.workadventure-nix.maps"; + type = types.path; + description = "Maps package to use"; }; }; }; diff --git a/workadventure.nix b/workadventure.nix index 449c5a1..a7aa826 100644 --- a/workadventure.nix +++ b/workadventure.nix @@ -1,12 +1,11 @@ -# Workadventure NixOS module. Used to deploy fediventure-compatible instances. +# Workadventure NixOS module. { config, lib, pkgs, ... }: with lib; let - cfg = config.services.workadventure; - + instances = config.services.workadventure; urls = instanceConfig: if instanceConfig.nginx.domain != null then { api = instanceConfig.nginx.domain + instanceConfig.frontend.urls.api; @@ -15,85 +14,113 @@ let maps = instanceConfig.nginx.domain + instanceConfig.frontend.urls.maps; } else instanceConfig.urls; + envCommonConfig = instanceConfig: with instanceConfig; { + SECRET_KEY = commonConfig.secretKey; + MINIMUM_DISTANCE = toString commonConfig.minimumDistance; + GROUP_RADIUS = toString commonConfig.groupRadius; + ALLOW_ARTILLERY = if commonConfig.allowArtillery then "true" else "false"; + MAX_USERS_PER_ROOM = toString commonConfig.maxUsersPerRoom; + CPU_OVERHEAT_THRESHOLD = toString commonConfig.cpuOverheatThreshold; + JITSI_URL = commonConfig.jitsi.url; + JITSI_ISS = commonConfig.jitsi.iss; + SECRET_JITSI_KEY = commonConfig.jitsi.secretKey; + SOCKET_IDLE_TIME = toString commonConfig.socketIdleTime; + }; + servicesBack = mapAttrs' (instanceName: instanceConfig: { - name = "wa-back-${instanceName}"; - value = { - description = "WorkAdventure backend ${instanceName}"; - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - # Hack to get node-grpc-precompiled to work on NixOS by adding getconf to - # $PATH. - # - # It uses node-pre-gyp which attempts to select the right native module - # via npmjs.com/package/detect-libc, which says 'yep, it's glibc' as long - # as `getconf GNU_LIBC_VERSION` returns something sensible. This happens - # during the build process (as stdenv.mkDerivation has enough of a glibc - # dev env to make it work) but doesn't happen on production deployments - # in which the environment is much more limited. This is regardless of - # actual glibc ABI presence wrt. to /nix/store vs. /usr/lib64 paths. - # - # This should be fixed in workadventure-nix. - path = [ - pkgs.getconf - ]; - environment = { - HTTP_PORT = toString instanceConfig.backend.httpPort; - GRPC_PORT = toString instanceConfig.backend.grpcPort; - #ADMIN_API_TOKEN = "lalala"; - #ADMIN_API_URL = toString (urls instanceConfig).admin; - #ALLOW_ARTILLERY = "true"; - }; - serviceConfig = { - User = "workadventure-backend"; - Group = "workadventure-backend"; - DynamicUser = true; # Note: this implies a lot of other security features. - ExecStart = "${instanceConfig.backend.package}/bin/workadventureback"; - Restart = "always"; - RestartSec = "10s"; + name = "wa-back-${instanceName}"; + value = mkIf instanceConfig.backend.enable { + description = "WorkAdventure backend ${instanceName}"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + # Hack to get node-grpc-precompiled to work on NixOS by adding getconf to + # $PATH. + # + # It uses node-pre-gyp which attempts to select the right native module + # via npmjs.com/package/detect-libc, which says 'yep, it's glibc' as long + # as `getconf GNU_LIBC_VERSION` returns something sensible. This happens + # during the build process (as stdenv.mkDerivation has enough of a glibc + # dev env to make it work) but doesn't happen on production deployments + # in which the environment is much more limited. This is regardless of + # actual glibc ABI presence wrt. to /nix/store vs. /usr/lib64 paths. + # + # This should be fixed in workadventure-nix. + path = [ + pkgs.getconf + ]; + environment = { + HTTP_PORT = toString instanceConfig.backend.httpPort; + GRPC_PORT = toString instanceConfig.backend.grpcPort; + } // envCommonConfig instanceConfig; + serviceConfig = { + User = "workadventure-backend"; + Group = "workadventure-backend"; + DynamicUser = true; # Note: this implies a lot of other security features. + ExecStart = "${instanceConfig.backend.package}/bin/workadventureback"; + Restart = "always"; + RestartSec = "10s"; + }; }; - }; - }) cfg.instances; + } + ) instances; - servicesPusher = mapAttrs' (instanceName: instanceConfig: { - name = "wa-pusher-${instanceName}"; - value = { - description = "WorkAdventure pusher ${instanceName}"; - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; + servicesPusher = mapAttrs' (instanceName: instanceConfig: + { + name = "wa-pusher-${instanceName}"; + value = mkIf instanceConfig.pusher.enable { + description = "WorkAdventure pusher ${instanceName}"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; - path = [ - pkgs.getconf - ]; - environment = { - PUSHER_HTTP_PORT = toString instanceConfig.pusher.port; - API_URL = "localhost:${toString instanceConfig.backend.grpcPort}"; - #ADMIN_API_URL = toString (urls instanceConfig).admin; - #ADMIN_API_TOKEN = "lalala"; - }; - serviceConfig = { - User = "workadventure-pusher"; - Group = "workadventure-pusher"; - DynamicUser = true; - ExecStart = "${instanceConfig.pusher.package}/bin/workadventurepusher"; - Restart = "always"; - RestartSec = "10s"; + path = [ + pkgs.getconf + ]; + environment = { + PUSHER_HTTP_PORT = toString instanceConfig.pusher.port; + API_URL = "localhost:${toString instanceConfig.backend.grpcPort}"; + } // envCommonConfig instanceConfig; + serviceConfig = { + User = "workadventure-pusher"; + Group = "workadventure-pusher"; + DynamicUser = true; + ExecStart = "${instanceConfig.pusher.package}/bin/workadventurepusher"; + Restart = "always"; + RestartSec = "10s"; + }; }; - }; - }) cfg.instances; + } + ) instances; frontPackage = mapAttrs (instanceName: instanceConfig: - instanceConfig.frontend.package.override { - settings = { - apiUrl = (urls instanceConfig).api; - uploaderUrl = (urls instanceConfig).uploader; - adminUrl = (urls instanceConfig).admin; - mapsUrl = (urls instanceConfig).maps; - } // instanceConfig.frontend.settings; + let fc = instanceConfig.frontend; + cc = instanceConfig.commonConfig; + in + fc.package.override { + environment = { + DEBUG_MODE = if fc.debugMode then "true" else "false"; # toString bool behaves weird + START_ROOM_URL = fc.startRoomUrl; + STUN_SERVER = cc.webrtc.stun.url; + TURN_SERVER = cc.webrtc.turn.url; + TURN_USER = cc.webrtc.turn.user; + TURN_PASSWORD = cc.webrtc.turn.password; + JITSI_URL = cc.jitsi.url; + JITSI_PRIVATE_MODE = if cc.jitsi.privateMode then "true" else "false"; + + API_URL = (urls instanceConfig).api; + UPDLOADER_URL = (urls instanceConfig).uploader; + ADMIN_URL = (urls instanceConfig).admin; + MAPS_URL = (urls instanceConfig).maps; + + RESOLUTION = fc.resolution; + ZOOM_LEVEL = fc.zoomLevel; + POSITION_DELAY = fc.positionDelay; + MAX_EXTRAPOLATION_TIME = fc.maxExtrapolationTime; + }; } - ) cfg.instances; + ) instances; virtualHosts = mapAttrs (instanceName: instanceConfig: - if instanceConfig.nginx.domain != null then { + mkIf instanceConfig.nginx.enable { default = instanceConfig.nginx.default; serverName = instanceConfig.nginx.domain; root = frontPackage.${instanceName} + "/dist"; @@ -108,46 +135,29 @@ let proxyWebsockets = true; }; - "/maps/" = mkIf instanceConfig.nginx.serveDefaultMaps { - alias = instanceConfig.maps.path; + "/maps/" = mkIf instanceConfig.nginx.maps.serve { + alias = instanceConfig.nginx.maps.path; }; }; - } else - # TODO: Configuration with separate domains is unsupported for now. - # Not sure if there's any interest in that anyway. - builtins.throw "Configurations with separate domains are not supported yet" - ) cfg.instances; -in { - options = { - services.workadventure = rec { - instances = mkOption { + } + ) instances; +in + { + options = { + services.workadventure = mkOption { type = types.attrsOf (types.submodule (import ./instance-options.nix { inherit config lib pkgs; })); default = {}; description = "Declarative WorkAdventure instance config"; }; - nginx = { - enable = mkOption { - default = true; - type = types.bool; - description = "Whether to enable nginx and configure it to serve the instances"; - }; - }; }; - }; - config = { - assertions = mapAttrsToList (name: instance: { - assertion = !cfg.nginx.enable - || (instance.nginx.domain != null && all (d: d == null) (attrValues instance.nginx.domains)) - || (instance.nginx.domain == null && all (d: d != null) (attrValues instance.nginx.domains)); - message = "In instance ${name}, you have to either define nginx.domain or all attributes of nginx.domains"; - }) cfg.instances; - systemd.services = servicesBack // servicesPusher; - services.nginx = mkIf cfg.nginx.enable { - inherit virtualHosts; - enable = mkDefault true; + config = { + systemd.services = servicesBack // servicesPusher; + services.nginx = mkIf (virtualHosts != {}) { + inherit virtualHosts; + enable = mkDefault true; + }; }; - }; -} + } |