Files
nix-config/modules/nixos/services/glance/default.nix
2026-04-05 19:10:23 -05:00

440 lines
13 KiB
Nix

{
config,
lib,
namespace,
...
}:
with lib;
let
name = "glance";
cfg = config.${namespace}.services.${name};
hostedServiceSites =
let
servicesCfg = config.${namespace}.services;
serviceNames = attrNames servicesCfg;
in
concatMap (
serviceName:
let
serviceCfg = servicesCfg.${serviceName};
hosted = serviceCfg.hostedService or null;
in
if hosted != null && hosted.enable then
[
(
{
inherit (hosted) title url icon;
}
// optionalAttrs hosted.basicAuth {
basic-auth = {
username = "\${ARR_USER}";
password = "\${ARR_PASS}";
};
}
)
]
else
[ ]
) serviceNames;
hostedServicesByGroup = groupBy (svc: svc.hostedService.group) (
filter (svc: svc.hostedService != null && svc.hostedService.enable) (
map (
serviceName:
let
serviceCfg = config.${namespace}.services.${serviceName};
in
{
hostedService = serviceCfg.hostedService or null;
}
) (attrNames config.${namespace}.services)
)
);
makeMonitorWidget = title: sites: {
type = "monitor";
cache = "1m";
inherit title;
inherit sites;
};
glanceConfig = lib.${namespace}.mkModule {
inherit config name;
description = "glance";
options = {
nasPoolPath = lib.mkOption {
type = lib.types.str;
default = cfg.dataDir;
description = "Path to the NAS pool mount to display in server-stats.";
};
enableHostedServices = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Auto-discover services with hostedService.enable and add them to the monitor widget";
};
hostedServiceGroups = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Create separate monitor widgets for each hostedService group instead of one combined widget";
};
extraSites = lib.mkOption {
type = lib.types.listOf (
lib.types.submodule {
options = {
title = lib.mkOption {
type = lib.types.str;
description = "Display title";
};
url = lib.mkOption {
type = lib.types.str;
description = "Service URL";
};
icon = lib.mkOption {
type = lib.types.str;
default = "si:glance";
description = "Icon identifier (e.g. si:servicename)";
};
allow-insecure = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Allow insecure connections";
};
basic-auth = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Require basic auth (uses ARR credentials)";
};
};
}
);
default = [ ];
description = "Extra sites to display in the monitor widget";
};
weather = lib.mkOption {
type = lib.types.submodule {
options = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Enable weather widget";
};
location = lib.mkOption {
type = lib.types.str;
default = "Saint Paul, Minnesota, United States";
description = "Weather location";
};
units = lib.mkOption {
type = lib.types.enum [
"imperial"
"metric"
];
default = "imperial";
description = "Temperature units";
};
hour-format = lib.mkOption {
type = lib.types.enum [
"12h"
"24h"
];
default = "12h";
description = "Hour format";
};
};
};
default = { };
description = "Weather widget settings";
};
bookmarks = lib.mkOption {
type = lib.types.listOf (
lib.types.submodule {
options = {
title = lib.mkOption {
type = lib.types.str;
description = "Group title";
};
links = lib.mkOption {
type = lib.types.listOf (
lib.types.submodule {
options = {
title = lib.mkOption {
type = lib.types.str;
description = "Link title";
};
url = lib.mkOption {
type = lib.types.str;
description = "Link URL";
};
};
}
);
description = "List of links";
};
};
}
);
default = [
{
title = "General";
links = [
{
title = "Gmail";
url = "https://mail.google.com/mail/u/0/";
}
{
title = "Proton Mail";
url = "https://mail.proton.me/u/0/inbox";
}
{
title = "MyNixOS";
url = "https://www.mynixos.com/";
}
{
title = "Github";
url = "https://github.com/";
}
];
}
{
title = "Entertainment";
links = [
{
title = "YouTube";
url = "https://www.youtube.com/";
}
{
title = "Prime Video";
url = "https://www.primevideo.com/";
}
{
title = "Disney+";
url = "https://www.disneyplus.com/";
}
];
}
{
title = "Social";
links = [
{
title = "Reddit";
url = "https://www.reddit.com/";
}
{
title = "Twitter";
url = "https://twitter.com/";
}
{
title = "Instagram";
url = "https://www.instagram.com/";
}
];
}
];
description = "Bookmark groups";
};
reddit = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [
"hockey"
"formula1"
];
description = "Subreddits to show in Reddit widgets";
};
search = lib.mkOption {
type = lib.types.listOf (
lib.types.submodule {
options = {
title = lib.mkOption {
type = lib.types.str;
description = "Search engine title";
};
shortcut = lib.mkOption {
type = lib.types.str;
description = "Bang shortcut (e.g. !yt)";
};
url = lib.mkOption {
type = lib.types.str;
description = "Search URL with {QUERY} placeholder";
};
};
}
);
default = [
{
title = "YouTube";
shortcut = "!yt";
url = "https://www.youtube.com/results?search_query={QUERY}";
}
];
description = "Custom search engine bangs";
};
servers = lib.mkOption {
type = lib.types.listOf (
lib.types.submodule {
options = {
name = lib.mkOption {
type = lib.types.str;
description = "Server name";
};
cpu-temp-sensor = lib.mkOption {
type = lib.types.str;
default = "";
description = "CPU temp sensor path";
};
mountpoints = lib.mkOption {
type = lib.types.attrsOf (
lib.types.submodule {
options = {
name = lib.mkOption {
type = lib.types.str;
description = "Display name for mountpoint";
};
};
}
);
description = "Mountpoints to display";
};
};
}
);
default = [
{
name = "Jallen-NAS";
cpu-temp-sensor = "/sys/devices/pci0000:00/0000:00:08.1/0000:cd:00.0/hwmon/hwmon*/temp1_input";
mountpoints = {
"/home" = {
name = "Home";
};
"${cfg.nasPoolPath}" = {
name = "nas_pool";
};
};
}
];
description = "Servers for server-stats widget";
};
};
moduleConfig = {
services.glance = {
enable = true;
openFirewall = true;
environmentFile = config.sops.templates."glance.env".path;
settings = {
server = {
host = "0.0.0.0";
inherit (cfg) port;
};
pages = [
{
name = "Startpage";
width = "default";
hide-desktop-navigation = true;
center-vertically = true;
columns = [
{
size = "small";
widgets = [
{
type = "calendar";
first-day-of-week = "sunday";
}
]
++ lib.optional cfg.weather.enable {
type = "weather";
units = cfg.weather.units;
hour-format = cfg.weather.hour-format;
location = cfg.weather.location;
}
++ lib.optional (cfg.servers != [ ]) {
type = "server-stats";
servers = cfg.servers;
};
}
{
size = "full";
widgets = [
{
type = "search";
autofocus = true;
search-engine = "google";
bangs = cfg.search;
}
]
++ lib.optionals cfg.hostedServiceGroups (
map (
groupName:
makeMonitorWidget groupName (
map (svc: {
inherit (svc.hostedService) title url icon;
}) (hostedServicesByGroup.${groupName} or [ ])
)
) (attrNames hostedServicesByGroup)
)
++ lib.optionals (!cfg.hostedServiceGroups && cfg.enableHostedServices) [
(makeMonitorWidget "Services" hostedServiceSites)
]
++ lib.optionals (cfg.extraSites != [ ]) (
map (site: {
type = "monitor";
cache = "1m";
inherit (site) title;
sites = [
(
{
inherit (site) title url icon;
}
// optionalAttrs site.allow-insecure { allow-insecure = true; }
)
];
}) cfg.extraSites
)
++ lib.optional (cfg.bookmarks != [ ]) {
type = "bookmarks";
groups = cfg.bookmarks;
}
++ lib.optionals (cfg.reddit != [ ]) (
map (subreddit: {
type = "reddit";
inherit subreddit;
}) cfg.reddit
);
}
];
}
];
};
};
};
};
in
{
imports = [
glanceConfig
{
config = lib.mkIf cfg.enable (
lib.${namespace}.mkSopsEnvFile {
name = "glance.env";
restartUnit = "glance.service";
secrets = {
"jallen-nas/glance/arr-username" = { };
"jallen-nas/glance/arr-password" = { };
};
content = ''
ARR_USER=${config.sops.placeholder."jallen-nas/glance/arr-username"}
ARR_PASS=${config.sops.placeholder."jallen-nas/glance/arr-password"}
'';
}
);
}
];
}