{ config, lib, pkgs, ... }: let cfg = config.services.rsshub-oci; images = { # https://github.com/DIYgod/RSSHub/pkgs/container/rsshub rsshub = pkgs.dockerTools.pullImage { imageName = "ghcr.io/diygod/rsshub"; imageDigest = "sha256:67e23d9afc6081753c1a51a71f5a58f57d43b12ff927c2263a9f767112ec1819"; hash = "sha256-vEQi12NCY35dGJ3vKUjwocfRn8xOBp/2inIqg8DNTRE="; finalImageName = "ghcr.io/diygod/rsshub"; finalImageTag = "2026-03-07"; }; # https://github.com/browserless/browserless/pkgs/container/chromium browserless = pkgs.dockerTools.pullImage { imageName = "ghcr.io/browserless/chromium"; imageDigest = "sha256:71ae7fa09bd1bf78efaa2803d75c837ab5a56e0d7637695bff3077d87248e642"; hash = "sha256-adu+fvWK1X/nVt2cFLDO0Czq6GA+46VvL1wy9KQ+/tI="; finalImageName = "ghcr.io/browserless/chromium"; finalImageTag = "v2.42.0"; }; # https://github.com/hyoban/puppeteer-real-browser-hono/pkgs/container/puppeteer-real-browser-hono real-browser = pkgs.dockerTools.pullImage { imageName = "ghcr.io/hyoban/puppeteer-real-browser-hono"; imageDigest = "sha256:21bb4cd27144a61ca2f85eb3b4f555a074165bc6beaee318873ced0eb4046a04"; hash = "sha256-O8qV8Wfh75rhtEnSAlUBzBUVNGpypNF1sNY2F4zVqZE="; finalImageName = "ghcr.io/hyoban/puppeteer-real-browser-hono"; finalImageTag = "sha-6c6cbbc"; }; # https://hub.docker.com/_/redis redis = pkgs.dockerTools.pullImage { imageName = "redis"; imageDigest = "sha256:2afba59292f25f5d1af200496db41bea2c6c816b059f57ae74703a50a03a27d0"; hash = "sha256-t3SFoeUME8Ntz5QdMfJnJ3QYRNAaHHpXngnBvR8LOlg="; finalImageName = "redis"; finalImageTag = "8.6.1-alpine"; }; }; defaultEnv = { NODE_ENV = "production"; CACHE_TYPE = "redis"; REDIS_URL = "redis://127.0.0.1:6379/"; PUPPETEER_WS_ENDPOINT = "ws://127.0.0.1:3000"; PUPPETEER_REAL_BROWSER_SERVICE = "http://127.0.0.1:3001"; }; inherit (lib) mkEnableOption mkIf mkOption mkOverride optional types ; in { options.services.rsshub-oci = { enable = mkEnableOption "RSSHub service stack with Podman"; port = mkOption { type = types.port; default = 1200; description = "The port RSSHub will listen on."; }; environment = mkOption { type = types.attrsOf types.str; default = { }; description = "Extra environment variables for RSSHub."; }; environmentFile = mkOption { type = types.nullOr types.path; default = null; description = "Environment file for secrets."; }; }; config = mkIf cfg.enable { virtualisation.podman = { enable = true; autoPrune.enable = true; dockerCompat = true; }; networking.firewall.interfaces = let matchAll = if !config.networking.nftables.enable then "podman+" else "podman*"; in { "${matchAll}".allowedUDPPorts = [ 53 ]; }; virtualisation.oci-containers.backend = "podman"; virtualisation.oci-containers.containers = { rsshub-browserless = { image = with images.browserless; imageName + ":" + imageTag; imageFile = images.browserless; log-driver = "journald"; extraOptions = [ "--network=host" ]; }; rsshub-real-browser = { image = with images.real-browser; imageName + ":" + imageTag; imageFile = images.real-browser; log-driver = "journald"; environment = { PORT = "3001"; }; extraOptions = [ "--network=host" ]; }; rsshub-redis = { image = with images.redis; imageName + ":" + imageTag; imageFile = images.redis; volumes = [ "rsshub_redis-data:/data:rw" ]; log-driver = "journald"; extraOptions = [ "--network=host" ]; }; rsshub-rsshub = { image = with images.rsshub; imageName + ":" + imageTag; imageFile = images.rsshub; log-driver = "journald"; environment = defaultEnv // cfg.environment // { PORT = "${toString cfg.port}"; }; environmentFiles = optional (cfg.environmentFile != null) cfg.environmentFile; extraOptions = [ "--network=host" ]; }; }; systemd.services = let commonServiceCfg = { serviceConfig.Restart = mkOverride 90 "always"; partOf = [ "podman-compose-rsshub-root.target" ]; wantedBy = [ "podman-compose-rsshub-root.target" ]; }; in { podman-rsshub-browserless = commonServiceCfg; podman-rsshub-real-browser = commonServiceCfg; podman-rsshub-redis = commonServiceCfg // { after = [ "podman-volume-rsshub_redis-data.service" ]; requires = [ "podman-volume-rsshub_redis-data.service" ]; }; podman-rsshub-rsshub = commonServiceCfg // { after = [ "podman-rsshub-redis.service" "podman-rsshub-browserless.service" "podman-rsshub-real-browser.service" ]; }; podman-volume-rsshub_redis-data = { path = [ pkgs.podman ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; script = '' podman volume inspect rsshub_redis-data || podman volume create rsshub_redis-data ''; partOf = [ "podman-compose-rsshub-root.target" ]; wantedBy = [ "podman-compose-rsshub-root.target" ]; }; }; systemd.targets."podman-compose-rsshub-root" = { unitConfig.Description = "Root target for RSSHub services."; wantedBy = [ "multi-user.target" ]; }; }; }