{ lib, config, options, pkgs, ... }: let cfg = config.services.retro-aim-server; in { options.services.retro-aim-server = with lib; { enable = mkEnableOption "Enable the retro-aim-server service"; API_HOST = mkOption { type = types.str; default = "127.0.0.1"; example = "example.com"; description = "Specifies the IP address or hostname that the management API binds to for incoming connections (127.0.0.1 restricts to same machine only)."; }; API_PORT = mkOption { type = types.port; default = 8080; description = "The port that the management API service binds to."; }; KERBEROS_PORT = mkOption { type = types.port; default = 1088; description = "The port that the Kerberos server binds to."; }; ALERT_PORT = mkOption { type = types.port; default = 5194; description = "The port that the Alert service binds to."; }; AUTH_PORT = mkOption { type = types.port; default = 5190; description = "The port that the auth service binds to."; }; BART_PORT = mkOption { type = types.port; default = 5195; description = "The port that the BART service binds to."; }; BOS_PORT = mkOption { type = types.port; default = 5191; description = "The port that the BOS service binds to."; }; CHAT_NAV_PORT = mkOption { type = types.port; default = 5193; description = "The port that the chat nav service binds to."; }; CHAT_PORT = mkOption { type = types.port; default = 5192; description = "The port that the chat service binds to."; }; ADMIN_PORT = mkOption { type = types.port; default = 5196; description = "The port that the admin service binds to."; }; ODIR_PORT = mkOption { type = types.port; default = 5197; description = "The port that the ODir service binds to."; }; DB_PATH = mkOption { type = types.str; default = "oscar.sqlite"; example = "/var/lib/ras/oscar.sqlite"; description = "The path to the SQLite database file. The file and DB schema are auto-created if they doesn't exist."; }; DISABLE_AUTH = mkOption { type = types.bool; default = true; example = false; description = '' Disable password check and auto-create new users at login time. Useful for quickly creating new accounts during development without having to register new users via the management API. ''; }; LOG_LEVEL = mkOption { type = types.enum [ "trace" "debug" "info" "warn" "error" ]; default = "info"; example = "debug"; description = "Set logging granularity. Possible values: 'trace', 'debug', 'info', 'warn', 'error'."; }; OSCAR_HOST = mkOption { type = types.str; default = "127.0.0.1"; example = "example.com"; description = '' The hostname that AIM clients connect to in order to reach OSCAR services (auth, BOS, BUCP, etc). Make sure the hostname is reachable by all clients. For local development, the default loopback address should work provided the server and AIM client(s) are running on the same machine. For LAN-only clients, a private IP address (e.g. 192.168..) or hostname should suffice. For clients connecting over the Internet, specify your public IP address and ensure that TCP ports 5190-5197 are open on your firewall. ''; }; TOC_HOST = mkOption { type = types.str; default = "0.0.0.0"; example = "example.com"; description = "Specifies the IP address or hostname that the TOC service binds to for incoming connections (0.0.0.0 listens on all interfaces)."; }; TOC_PORT = mkOption { type = types.port; default = 9898; description = "The port that the TOC service binds to."; }; extraConfig = mkOption { type = types.lines; default = ""; description = "Extra server configuration, see https://github.com/mk6i/retro-aim-server/blob/main/config/settings.env for more information"; }; user = mkOption { type = types.str; default = "ras"; description = "User account under which retro-aim-server service runs. If left default, user will be automatically created."; }; group = mkOption { type = types.str; default = "ras"; description = "Group account under which retro-aim-server service runs. If left default, group will be automatically created."; }; }; config = with lib; mkIf cfg.enable { systemd = { packages = [ pkgs.retro-aim-server ]; services.retro-aim-server = { serviceConfig = { User = cfg.user; Group = cfg.group; StateDirectory = [ cfg.user ]; WorkingDirectory = [ "/var/lib/${cfg.user}" ]; ExecStart = "${pkgs.retro-aim-server}/bin/server -config /etc/ras/settings.env"; }; restartTriggers = [ config.environment.etc."ras/settings.env".source ]; wantedBy = [ "multi-user.target" ]; }; }; environment.etc."ras/settings.env".text = let toString = value: if builtins.isBool value then lib.boolToString value else builtins.toString value; in (lib.concatLines ( lib.attrsets.mapAttrsToList (name: value: if lib.hasInfix "_" name then "export ${name}=${toString value}" else "") cfg)) + cfg.extraConfig; users.users = lib.optionalAttrs (cfg.user == options.services.retro-aim-server.user.default) { ${cfg.user} = { isSystemUser = true; group = cfg.group; description = "retro-aim-server management user"; }; }; users.groups = lib.optionalAttrs (cfg.group == options.services.retro-aim-server.group.default) { "ras" = { }; }; networking.firewall = { allowedTCPPorts = builtins.filter (x: ! builtins.isNull x) (lib.attrsets.mapAttrsToList (name: value: if options.services.retro-aim-server.${name}.type == types.port then value else null) cfg); }; }; }