{ config, containerCfg, pkgs, lib, builder, name, ... }: let version = "latest"; serverCfg = config.syscfg.server; mediaCfg = config.syscfg.media; homepageExtra = containerCfg.extra or {}; backgroundImage = if mediaCfg.banner.png != null then mediaCfg.banner.png else mediaCfg.bg; backgroundFileName = builtins.baseNameOf (toString backgroundImage); backgroundMount = "/app/public/media/${backgroundFileName}"; latitude = if homepageExtra ? latitude then homepageExtra.latitude else if homepageExtra ? lat then homepageExtra.lat else 47.3769; longitude = if homepageExtra ? longitude then homepageExtra.longitude else if homepageExtra ? lon then homepageExtra.lon else 8.5417; extraBookmarks = homepageExtra.bookmarks or []; extraServices = homepageExtra.services or []; settings = pkgs.writers.writeYAML "settings.yaml" { title = "Home"; description = ""; startUrl = "https://${containerCfg.subdomain}.${serverCfg.domain}"; background = { image = "/media/${backgroundFileName}"; brightness = 50; opacity = 0.5; blur = ""; }; cardBlur = "xs"; favicon = "https://${containerCfg.subdomain}.${serverCfg.domain}/favicon.ico"; theme = "dark"; color = "slate"; fullWidth = true; useEqualHeights = true; pwa = { }; layout = { Admin = { initiallyCollapsed = true; style = "row"; columns = 4; }; }; bookmarksStyle = "icons"; providers = { finnhub = "{{HOMEPAGE_VAR_FINNHUB}}"; }; headerStyle = "clean"; hideVersion = true; disableUpdateCheck = true; showStats = false; statusStyle = "dot"; hideErrors = true; }; widgets = pkgs.writers.writeYAML "widgets.yaml" [ { openmeteo = { latitude = toString latitude; longitude = toString longitude; timezone = config.time.timeZone; units = "metric"; cache = "15"; }; } { search = { provider = "custom"; focus = true; showSearchSuggestions = true; target = "_blank"; } // (lib.optionalAttrs (serverCfg.containers ? searxng) { url = "https://${serverCfg.containers.searxng.subdomain}.${serverCfg.domain}/search?q="; suggestionUrl = "https://${serverCfg.containers.searxng.subdomain}.${serverCfg.domain}/autocompleter?q="; }); } { stocks = { provider = "finnhub"; color = true; cache = 15; watchlist = homepageExtra.stocks or []; }; } ]; bookmarks = pkgs.writers.writeYAML "bookmarks.yaml" (extraBookmarks); services = pkgs.writers.writeYAML "services.yaml" ([ { Media = lib.flatten [ (lib.optional (serverCfg.containers ? jellyfin) { Jellyfin = { icon = "jellyfin.png"; href = "https://${serverCfg.containers.jellyfin.subdomain}.${serverCfg.domain}"; siteMonitor = "http://jellyfin-server:8096"; widget = { type = "jellyfin"; url = "http://jellyfin-server:8096"; key = "{{HOMEPAGE_VAR_JELLYFIN_API}}"; }; }; }) (lib.optional (serverCfg.containers ? invidious) { Invidious = { icon = "invidious.png"; href = "https://${serverCfg.containers.invidious.subdomain}.${serverCfg.domain}"; siteMonitor = "http://invidious-server:3000"; }; }) (lib.optional (serverCfg.containers ? calibre) { Calibre = { icon = "calibre.png"; href = "https://${serverCfg.containers.calibre.subdomain}.${serverCfg.domain}"; siteMonitor = "http://calibre-server:8080"; # widget = { # type = "calibreweb"; # url = "http://calibre-server:8080"; # username = "?"; # password = "?"; # }; }; }) (lib.optional (serverCfg.containers ? freshrss) { FreshRSS = { icon = "freshrss.png"; href = "https://${serverCfg.containers.freshrss.subdomain}.${serverCfg.domain}"; siteMonitor = "http://freshrss-server:8080"; # widget = { # type = "freshrss"; # url = "http://freshrss-server:8080"; # username = "?"; # password = "?"; # }; }; }; }) (lib.optional (serverCfg.containers ? miniflux) { Miniflux = { icon = "miniflux.png"; href = "https://${serverCfg.containers.miniflux.subdomain}.${serverCfg.domain}"; siteMonitor = "http://miniflux-server:80"; widget = { type = "miniflux"; url = "http://miniflux-server"; key = "{{HOMEPAGE_VAR_MINIFLUX_API}}"; }; }; }) (lib.optional (serverCfg.containers ? suwayomi) { Suwayomi = { icon = "suwayomi.png"; href = "https://${serverCfg.containers.suwayomi.subdomain}.${serverCfg.domain}"; siteMonitor = "http://suwayomi-server:8080"; widget = { type = "suwayomi"; url = "http://suwayomi-server:8080"; }; }; }) ]; } { Cloud = lib.flatten [ (lib.optional (serverCfg.containers ? nextcloud) { Nextcloud = { icon = "nextcloud.png"; href = "https://${serverCfg.containers.nextcloud.subdomain}.${serverCfg.domain}"; siteMonitor = "http://nextcloud-server:80"; widget = { type = "nextcloud"; url = "http://nextcloud-server:80"; key = "{{HOMEPAGE_VAR_NEXTCLOUD_API}}"; }; }; }) (lib.optional (serverCfg.containers ? ethercalc) { Ethercalc = { icon = "ethercalc.png"; href = "https://${serverCfg.containers.ethercalc.subdomain}.${serverCfg.domain}"; siteMonitor = "http://ethercalc-server:8080"; }; }) (lib.optional (serverCfg.containers ? etherpad) { Etherpad = { icon = "etherpad.png"; href = "https://${serverCfg.containers.etherpad.subdomain}.${serverCfg.domain}"; siteMonitor = "http://etherpad-server:8080"; }; }) (lib.optional (serverCfg.containers ? collabora && false) { Collabora = { icon = "microsoft-office.png"; href = "https://${serverCfg.containers.collabora.subdomain}.${serverCfg.domain}"; siteMonitor = "http://collabora-server:9980"; }; }) (lib.optional (serverCfg.containers ? immich) { Immich = { icon = "immich.png"; href = "https://${serverCfg.containers.immich.subdomain}.${serverCfg.domain}"; siteMonitor = "http://immich-server:80"; widget = { type = "immich"; url = "http://immich-server:80"; key = "{{HOMEPAGE_VAR_IMMICH_API}}"; version = "2"; }; }; }) ]; } { Home = lib.flatten [ (lib.optional (serverCfg.containers ? homeassistant) { "Home Assistant" = { icon = "home-assistant.png"; href = "https://${serverCfg.containers.homeassistant.subdomain}.${serverCfg.domain}"; siteMonitor = "http://${builder.hostIp}:8123"; }; }) (lib.optional (serverCfg.containers ? openhab) { openHAB = { icon = "openhab.png"; href = "https://${serverCfg.containers.openhab.subdomain}.${serverCfg.domain}"; siteMonitor = "http://openhab-server:8080"; }; }) (lib.optional (serverCfg.containers ? frigate) { Frigate = { icon = "frigate.png"; href = "https://${serverCfg.containers.frigate.subdomain}.${serverCfg.domain}"; siteMonitor = "http://frigate-server:5000"; }; }) ]; } { Dev = lib.flatten [ (lib.optional (serverCfg.containers ? gitea) { Gitea = { icon = "gitea.png"; href = "https://${serverCfg.containers.gitea.subdomain}.${serverCfg.domain}"; siteMonitor = "http://gitea-server:3000"; widget = { type = "gitea"; url = "http://gitea-server:3000"; key = "{{HOMEPAGE_VAR_GITEA_API}}"; }; }; }) (lib.optional (serverCfg.containers ? trmnl) { TRMNL = { icon = "terminal.png"; href = "https://${serverCfg.containers.trmnl.subdomain}.${serverCfg.domain}"; siteMonitor = "http://trmnl-server:8080"; }; }) ]; } { Admin = lib.flatten [ (lib.optional (serverCfg.containers ? traefik) { Traefik = { icon = "traefik.png"; href = "https://${serverCfg.containers.traefik.subdomain}.${serverCfg.domain}"; siteMonitor = "http://traefik-server:8080"; widget = { type = "traefik"; url = "http://traefik-server:8080"; }; }; }) (lib.optional (serverCfg.containers ? authentik) { Authentik = { icon = "authentik.png"; href = "https://${serverCfg.containers.authentik.subdomain}.${serverCfg.domain}"; siteMonitor = "http://authentik-server:9000"; widget = { type = "authentik"; url = "http://authentik-server:9000"; key = "{{HOMEPAGE_VAR_AUTHENTIK_API}}"; version = "2"; }; }; }) (lib.optional (serverCfg.containers ? umami) { Umami = { icon = "umami.png"; href = "https://${serverCfg.containers.umami.subdomain}.${serverCfg.domain}"; siteMonitor = "http://umami-server:3000"; }; }) (lib.optional (serverCfg.containers ? influx) { Influx = { icon = "grafana.png"; href = "https://${serverCfg.containers.influx.subdomain}.${serverCfg.domain}"; siteMonitor = "http://influx-ui:3000"; }; }) (lib.optional (serverCfg.containers ? handbrake) { Handbrake = { icon = "handbrake.png"; href = "https://${serverCfg.containers.handbrake.subdomain}.${serverCfg.domain}"; siteMonitor = "http://handbrake-server:5800"; }; }) (lib.optional (serverCfg.containers ? transmission) { Transmission = { icon = "transmission.png"; href = "https://${serverCfg.containers.transmission.subdomain}.${serverCfg.domain}/transmission"; siteMonitor = "http://transmission-server:9091"; widget = { type = "transmission"; url = "http://transmission-server:9091"; rpcUrl = "/transmission/"; }; }; }) (lib.optional (serverCfg.containers ? selfmark) { Selfmark = { icon = "link.png"; href = "https://${serverCfg.containers.selfmark.subdomain}.${serverCfg.domain}"; siteMonitor = "http://selfmark-server:8080"; }; }) (lib.optional (serverCfg.containers ? servarr) ( let modules = serverCfg.containers.servarr.extra.modules or [ "prowlarr" "sonarr" "radarr" "flaresolverr" ]; in (lib.optional (builtins.elem "sonarr" modules) { Sonarr = { icon = "sonarr.png"; href = "https://${serverCfg.containers.servarr.subdomain}.${serverCfg.domain}/sonarr"; siteMonitor = "http://servarr-sonarr:8989"; widget = { type = "sonarr"; url = "http://servarr-sonarr:8989"; key = "{{HOMEPAGE_VAR_SONARR_API}}"; }; }; }) ++ (lib.optional (builtins.elem "radarr" modules) { Radarr = { icon = "radarr.png"; href = "https://${serverCfg.containers.servarr.subdomain}.${serverCfg.domain}/radarr"; siteMonitor = "http://servarr-radarr:8989"; widget = { type = "radarr"; url = "http://servarr-radarr:8989"; key = "{{HOMEPAGE_VAR_RADARR_API}}"; }; }; }) ++ (lib.optional (builtins.elem "lidarr" modules) { Lidarr = { icon = "lidarr.png"; href = "https://${serverCfg.containers.servarr.subdomain}.${serverCfg.domain}/lidarr"; siteMonitor = "http://servarr-lidarr:8989"; widget = { type = "lidarr"; url = "http://servarr-lidarr:8989"; key = "{{HOMEPAGE_VAR_LIDARR_API}}"; }; }; }) ++ (lib.optional (builtins.elem "prowlarr" modules) { Prowlarr = { icon = "prowlarr.png"; href = "https://${serverCfg.containers.servarr.subdomain}.${serverCfg.domain}/prowlarr"; siteMonitor = "http://servarr-prowlarr:8989"; widget = { type = "prowlarr"; url = "http://servarr-prowlarr:8989"; key = "{{HOMEPAGE_VAR_PROWLARR_API}}"; }; }; }) )) ]; } ] ++ extraServices); in { runtime = { containers = { server = builder.mkContainer { subdomain = containerCfg.subdomain; image = "ghcr.io/gethomepage/homepage:${version}"; port = 3000; extraEnv = { HOMEPAGE_VAR_TITLE = "${serverCfg.domain}"; HOMEPAGE_ALLOWED_HOSTS = "${containerCfg.subdomain}.${serverCfg.domain},${builder.host}"; }; extraLabels = { "traefik.http.routers.${containerCfg.subdomain}.service" = "${containerCfg.subdomain}"; }; overrides = { environmentFiles = [ config.sops.secrets."CUSTOM".path ]; volumes = [ "${settings}:/app/config/settings.yaml:ro" "${services}:/app/config/services.yaml:ro" "${widgets}:/app/config/widgets.yaml:ro" "${bookmarks}:/app/config/bookmarks.yaml:ro" "${backgroundImage}:${backgroundMount}:ro" ]; }; }; }; }; }