main
  1{
  2  den,
  3  lib,
  4  ...
  5}: {
  6  den.aspects.services.mihomo = {
  7    settings.host = {
  8      autoStart = lib.mkEnableOption "Auto start mihomo service";
  9      tailscaleWebControl = lib.mkEnableOption "Allow connect external controller from tailscale subnet";
 10      interfaces = {
 11        wan = lib.mkOption {
 12          description = "The WAN interface to bind.";
 13          type = lib.types.str;
 14          default = null;
 15        };
 16        lan = lib.mkOption {
 17          description = "The LAN interface to bind.";
 18          type = lib.types.listOf lib.types.str;
 19          default = [];
 20        };
 21      };
 22      ports = {
 23        controller = lib.mkOption {
 24          type = lib.types.port;
 25          default = 7900;
 26        };
 27        dns = lib.mkOption {
 28          type = lib.types.port;
 29          default = 1053;
 30        };
 31        mixed = lib.mkOption {
 32          type = lib.types.port;
 33          default = 7890;
 34        };
 35        tproxy = lib.mkOption {
 36          type = lib.types.port;
 37          default = 7894;
 38        };
 39      };
 40    };
 41
 42    includes = with den.aspects.services.mihomo; [
 43      dns
 44      proxies
 45      proxy-groups
 46      rules
 47      sniffer
 48      grants
 49    ];
 50
 51    cache = {
 52      directories = [
 53        {
 54          directory = "/var/lib/mihomo";
 55          user = "mihomo";
 56          group = "mihomo";
 57        }
 58      ];
 59    };
 60
 61    nixos = {
 62      host,
 63      pkgs,
 64      ...
 65    }: let
 66      cfg = host.settings.services.mihomo;
 67    in {
 68      imports = [./_module.nix];
 69
 70      services.mihomo = {
 71        enable = true;
 72        webui = pkgs.metacubexd;
 73        processesInfo = lib.mkDefault true;
 74
 75        config = {
 76          mixed-port = cfg.ports.mixed;
 77          mode = "rule";
 78          ipv6 = false;
 79          find-process-mode = lib.mkDefault "strict";
 80          allow-lan = lib.mkDefault true;
 81          bind-address = lib.mkDefault "*";
 82          log-level = "warning";
 83          interface-name = cfg.interfaces.wan;
 84          unified-delay = true;
 85          tcp-concurrent = true;
 86          geodata-mode = true; # `.dat`
 87          geox-url = {
 88            geoip = "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat";
 89            geosite = "https://testingcf.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat";
 90          };
 91          external-controller = "${
 92            if cfg.tailscaleWebControl
 93            then host.address.ipv4.tailscale
 94            else "127.0.0.1"
 95          }:${toString cfg.ports.controller}";
 96          external-controller-cors = {
 97            allow-origins = ["*"];
 98            allow-private-network = true;
 99          };
100        };
101      };
102
103      systemd.services."mihomo" = {
104        wants = lib.optional (cfg.tailscaleWebControl) "tailscale-wait-online.service";
105        after = lib.optional (cfg.tailscaleWebControl) "tailscale-wait-online.service";
106        wantedBy = lib.mkIf (!cfg.autoStart) (lib.mkForce []);
107      };
108    };
109  };
110
111  den.aspects.services.mihomo.grants = {host, ...}: {
112    tailscaleGrant = lib.optional (host.settings.services.mihomo.tailscaleWebControl) {
113      src = ["autogroup:member"];
114      ip = ["tcp:${toString host.settings.services.mihomo.ports.controller}"];
115    };
116  };
117}