at 25.11-pre 8.8 kB view raw
1{ 2 config, 3 pkgs, 4 lib, 5 ... 6}: 7 8let 9 inherit (lib) 10 mkOption 11 mkEnableOption 12 mkIf 13 types 14 ; 15 16 cfg = config.services.lavalink; 17 18 format = pkgs.formats.yaml { }; 19in 20 21{ 22 options.services.lavalink = { 23 enable = mkEnableOption "Lavalink"; 24 25 package = lib.mkPackageOption pkgs "lavalink" { }; 26 27 password = mkOption { 28 type = types.nullOr types.str; 29 default = null; 30 example = "s3cRe!p4SsW0rD"; 31 description = '' 32 The password for Lavalink's authentication in plain text. 33 ''; 34 }; 35 36 port = mkOption { 37 type = types.port; 38 default = 2333; 39 example = 4567; 40 description = '' 41 The port that Lavalink will use. 42 ''; 43 }; 44 45 address = mkOption { 46 type = types.str; 47 default = "0.0.0.0"; 48 example = "127.0.0.1"; 49 description = '' 50 The network address to bind to. 51 ''; 52 }; 53 54 openFirewall = mkOption { 55 type = types.bool; 56 default = false; 57 example = true; 58 description = '' 59 Whether to expose the port to the network. 60 ''; 61 }; 62 63 user = mkOption { 64 type = types.str; 65 default = "lavalink"; 66 example = "root"; 67 description = '' 68 The user of the service. 69 ''; 70 }; 71 72 group = mkOption { 73 type = types.str; 74 default = "lavalink"; 75 example = "medias"; 76 description = '' 77 The group of the service. 78 ''; 79 }; 80 81 home = mkOption { 82 type = types.str; 83 default = "/var/lib/lavalink"; 84 example = "/home/lavalink"; 85 description = '' 86 The home directory for lavalink. 87 ''; 88 }; 89 90 enableHttp2 = mkEnableOption "HTTP/2 support"; 91 92 jvmArgs = mkOption { 93 type = types.str; 94 default = "-Xmx4G"; 95 example = "-Djava.io.tmpdir=/var/lib/lavalink/tmp -Xmx6G"; 96 description = '' 97 Set custom JVM arguments. 98 ''; 99 }; 100 101 environmentFile = mkOption { 102 type = types.nullOr types.str; 103 default = null; 104 example = "/run/secrets/lavalink/passwordEnvFile"; 105 description = '' 106 Add custom environment variables from a file. 107 See <https://lavalink.dev/configuration/index.html#example-environment-variables> for the full documentation. 108 ''; 109 }; 110 111 plugins = mkOption { 112 type = types.listOf ( 113 types.submodule { 114 options = { 115 dependency = mkOption { 116 type = types.str; 117 example = "dev.lavalink.youtube:youtube-plugin:1.8.0"; 118 description = '' 119 The coordinates of the plugin. 120 ''; 121 }; 122 123 repository = mkOption { 124 type = types.str; 125 example = "https://maven.example.com/releases"; 126 default = "https://maven.lavalink.dev/releases"; 127 description = '' 128 The plugin repository. Defaults to the lavalink releases repository. 129 130 To use the snapshots repository, use <https://maven.lavalink.dev/snapshots> instead 131 ''; 132 }; 133 134 hash = mkOption { 135 type = types.str; 136 example = lib.fakeHash; 137 description = '' 138 The hash of the plugin. 139 ''; 140 }; 141 142 configName = mkOption { 143 type = types.nullOr types.str; 144 example = "youtube"; 145 default = null; 146 description = '' 147 The name of the plugin to use as the key for the plugin configuration. 148 ''; 149 }; 150 151 extraConfig = mkOption { 152 type = types.submodule { freeformType = format.type; }; 153 default = { }; 154 description = '' 155 The configuration for the plugin. 156 157 The {option}`services.lavalink.plugins.*.configName` option must be set. 158 ''; 159 }; 160 }; 161 } 162 ); 163 default = [ ]; 164 165 example = lib.literalExpression '' 166 [ 167 { 168 dependency = "dev.lavalink.youtube:youtube-plugin:1.8.0"; 169 repository = "https://maven.lavalink.dev/snapshots"; 170 hash = lib.fakeHash; 171 configName = "youtube"; 172 extraConfig = { 173 enabled = true; 174 allowSearch = true; 175 allowDirectVideoIds = true; 176 allowDirectPlaylistIds = true; 177 }; 178 } 179 ] 180 ''; 181 182 description = '' 183 A list of plugins for lavalink. 184 ''; 185 }; 186 187 extraConfig = mkOption { 188 type = types.submodule { freeformType = format.type; }; 189 190 description = '' 191 Configuration to write to {file}`application.yml`. 192 See <https://lavalink.dev/configuration/#example-applicationyml> for the full documentation. 193 194 Individual configuration parameters can be overwritten using environment variables. 195 See <https://lavalink.dev/configuration/#example-environment-variables> for more information. 196 ''; 197 198 default = { }; 199 200 example = lib.literalExpression '' 201 { 202 lavalink.server = { 203 sources.twitch = true; 204 205 filters.volume = true; 206 }; 207 208 logging.file.path = "./logs/"; 209 } 210 ''; 211 }; 212 }; 213 214 config = 215 let 216 pluginSymlinks = lib.concatStringsSep "\n" ( 217 map ( 218 pluginCfg: 219 let 220 pluginParts = lib.match ''^(.*?:(.*?):)([0-9]+\.[0-9]+\.[0-9]+)$'' pluginCfg.dependency; 221 222 pluginWebPath = lib.replaceStrings [ "." ":" ] [ "/" "/" ] (lib.elemAt pluginParts 0); 223 224 pluginFileName = lib.elemAt pluginParts 1; 225 pluginVersion = lib.elemAt pluginParts 2; 226 227 pluginFile = "${pluginFileName}-${pluginVersion}.jar"; 228 pluginUrl = "${pluginCfg.repository}/${pluginWebPath}${pluginVersion}/${pluginFile}"; 229 230 plugin = pkgs.fetchurl { 231 url = pluginUrl; 232 inherit (pluginCfg) hash; 233 }; 234 in 235 "ln -sf ${plugin} ${cfg.home}/plugins/${pluginFile}" 236 ) cfg.plugins 237 ); 238 239 pluginExtraConfigs = builtins.listToAttrs ( 240 builtins.map ( 241 pluginConfig: lib.attrsets.nameValuePair pluginConfig.configName pluginConfig.extraConfig 242 ) (lib.lists.filter (pluginCfg: pluginCfg.configName != null) cfg.plugins) 243 ); 244 245 config = lib.attrsets.recursiveUpdate cfg.extraConfig { 246 server = { 247 inherit (cfg) port address; 248 http2.enabled = cfg.enableHttp2; 249 }; 250 251 plugins = pluginExtraConfigs; 252 lavalink.plugins = ( 253 builtins.map ( 254 pluginConfig: 255 builtins.removeAttrs pluginConfig [ 256 "name" 257 "extraConfig" 258 "hash" 259 ] 260 ) cfg.plugins 261 ); 262 }; 263 264 configWithPassword = lib.attrsets.recursiveUpdate config ( 265 lib.attrsets.optionalAttrs (cfg.password != null) { lavalink.server.password = cfg.password; } 266 ); 267 268 configFile = format.generate "application.yml" configWithPassword; 269 in 270 mkIf cfg.enable { 271 assertions = [ 272 { 273 assertion = 274 !(lib.lists.any ( 275 pluginCfg: pluginCfg.extraConfig != { } && pluginCfg.configName == null 276 ) cfg.plugins); 277 message = "Plugins with extra configuration need to have the `configName` attribute defined"; 278 } 279 ]; 280 281 networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ]; 282 283 users.groups = mkIf (cfg.group == "lavalink") { lavalink = { }; }; 284 users.users = mkIf (cfg.user == "lavalink") { 285 lavalink = { 286 inherit (cfg) home; 287 group = "lavalink"; 288 description = "The user for the Lavalink server"; 289 isSystemUser = true; 290 }; 291 }; 292 293 systemd.tmpfiles.settings."10-lavalink" = 294 let 295 dirConfig = { 296 inherit (cfg) user group; 297 mode = "0700"; 298 }; 299 in 300 { 301 "${cfg.home}/plugins".d = mkIf (cfg.plugins != [ ]) dirConfig; 302 ${cfg.home}.d = dirConfig; 303 }; 304 305 systemd.services.lavalink = { 306 description = "Lavalink Service"; 307 308 wantedBy = [ "multi-user.target" ]; 309 after = [ 310 "syslog.target" 311 "network.target" 312 ]; 313 314 script = '' 315 ${pluginSymlinks} 316 317 ln -sf ${configFile} ${cfg.home}/application.yml 318 export _JAVA_OPTIONS="${cfg.jvmArgs}" 319 320 ${lib.getExe cfg.package} 321 ''; 322 323 serviceConfig = { 324 User = cfg.user; 325 Group = cfg.group; 326 327 Type = "simple"; 328 Restart = "on-failure"; 329 330 EnvironmentFile = cfg.environmentFile; 331 WorkingDirectory = cfg.home; 332 }; 333 }; 334 }; 335}