auto-update-daily-20260202
1{
2 lib,
3 config,
4 globals,
5 ...
6}:
7let
8 rheaIPs = globals.machines.rhea.net.ips;
9 rheaVPNIPs = globals.machines.rhea.net.vpn.ips;
10 rheaDomains = globals.machines.rhea.net.names;
11
12 # Build allowed hosts list from all rhea addresses
13 allowedHosts = lib.concatStringsSep "," (
14 rheaDomains
15 ++ rheaIPs
16 ++ rheaVPNIPs
17 ++ [
18 "127.0.0.1"
19 "localhost"
20 "homepage.sbr.pm"
21 ]
22 );
23
24 # Extract syncthing machines from globals
25 syncthingMachines = lib.filterAttrs (
26 _name: machine: machine ? syncthing && machine.syncthing ? folders
27 ) globals.machines;
28
29 # Generate syncthing service entries
30 syncthingServices = lib.mapAttrsToList (name: _machine: {
31 "Syncthing (${name})" = {
32 description = "Syncthing on ${name}";
33 href = "https://syncthing.sbr.pm/${name}/";
34 icon = "syncthing.png";
35 ping = "https://syncthing.sbr.pm/${name}/";
36 statusStyle = "dot";
37 };
38 }) syncthingMachines;
39in
40{
41 # Homepage Dashboard - Homelab Services Overview
42 # https://gethomepage.dev
43 #
44 # This module provides a centralized dashboard for monitoring homelab services.
45 # Services are configured with widgets for real-time statistics where supported.
46 #
47 # TODO: Migrate Homepage to aion when that server is set up for web services
48
49 services.homepage-dashboard = {
50 enable = true;
51 listenPort = 3001;
52
53 settings = {
54 title = "Homelab";
55 favicon = "https://gethomepage.dev/img/favicon.ico";
56 theme = "dark";
57 color = "slate";
58
59 layout = [
60 {
61 Media = {
62 style = "row";
63 columns = 3;
64 };
65 }
66 {
67 "Download Management" = {
68 style = "row";
69 columns = 3;
70 };
71 }
72 {
73 "Torrent Clients" = {
74 style = "row";
75 columns = 2;
76 };
77 }
78 {
79 Synchronization = {
80 style = "row";
81 columns = 4;
82 };
83 }
84 {
85 Infrastructure = {
86 style = "row";
87 columns = 2;
88 };
89 }
90 ];
91 };
92
93 services = [
94 {
95 Media = [
96 {
97 Jellyfin = {
98 description = "Media Server";
99 href = "https://jellyfin.sbr.pm";
100 icon = "jellyfin.png";
101 ping = "https://jellyfin.sbr.pm";
102 statusStyle = "dot";
103 };
104 }
105 {
106 Jellyseerr = {
107 description = "Media Requests";
108 href = "https://jellyseerr.sbr.pm";
109 icon = "jellyseerr.png";
110 ping = "https://jellyseerr.sbr.pm";
111 statusStyle = "dot";
112 };
113 }
114 {
115 Navidrome = {
116 description = "Music Streaming";
117 href = "https://music.sbr.pm";
118 icon = "navidrome.png";
119 ping = "https://music.sbr.pm";
120 statusStyle = "dot";
121 };
122 }
123 {
124 Audiobookshelf = {
125 description = "Podcasts & Audiobooks";
126 href = "https://podcasts.sbr.pm";
127 icon = "audiobookshelf.png";
128 ping = "https://podcasts.sbr.pm";
129 statusStyle = "dot";
130 };
131 }
132 {
133 Immich = {
134 description = "Photo Management";
135 href = "https://immich.sbr.pm";
136 icon = "immich.png";
137 ping = "https://immich.sbr.pm";
138 statusStyle = "dot";
139 };
140 }
141 ];
142 }
143 {
144 "Download Management" = [
145 {
146 Sonarr = {
147 description = "TV Shows";
148 href = "https://sonarr.sbr.pm";
149 icon = "sonarr.png";
150 ping = "https://sonarr.sbr.pm";
151 statusStyle = "dot";
152 widget = {
153 type = "sonarr";
154 url = "https://sonarr.sbr.pm";
155 key = "{{HOMEPAGE_FILE_SONARR_KEY}}";
156 };
157 };
158 }
159 {
160 Radarr = {
161 description = "Movies";
162 href = "https://radarr.sbr.pm";
163 icon = "radarr.png";
164 ping = "https://radarr.sbr.pm";
165 statusStyle = "dot";
166 widget = {
167 type = "radarr";
168 url = "https://radarr.sbr.pm";
169 key = "{{HOMEPAGE_FILE_RADARR_KEY}}";
170 };
171 };
172 }
173 {
174 Lidarr = {
175 description = "Music";
176 href = "https://lidarr.sbr.pm";
177 icon = "lidarr.png";
178 ping = "https://lidarr.sbr.pm";
179 statusStyle = "dot";
180 widget = {
181 type = "lidarr";
182 url = "https://lidarr.sbr.pm";
183 key = "{{HOMEPAGE_FILE_LIDARR_KEY}}";
184 };
185 };
186 }
187 {
188 Bazarr = {
189 description = "Subtitles";
190 href = "https://bazarr.sbr.pm";
191 icon = "bazarr.png";
192 ping = "https://bazarr.sbr.pm";
193 statusStyle = "dot";
194 };
195 }
196 {
197 Prowlarr = {
198 description = "Indexer Manager";
199 href = "https://prowlarr.sbr.pm";
200 icon = "prowlarr.png";
201 ping = "https://prowlarr.sbr.pm";
202 statusStyle = "dot";
203 };
204 }
205 ];
206 }
207 {
208 "Torrent Clients" = [
209 {
210 Transmission = {
211 description = "General Downloads";
212 href = "https://transmission.sbr.pm";
213 icon = "transmission.png";
214 ping = "https://transmission.sbr.pm";
215 statusStyle = "dot";
216 };
217 }
218 {
219 "Transmission Music" = {
220 description = "Music Downloads";
221 href = "https://transmission-music.sbr.pm";
222 icon = "transmission.png";
223 ping = "https://transmission-music.sbr.pm";
224 statusStyle = "dot";
225 };
226 }
227 ];
228 }
229 {
230 Synchronization = syncthingServices;
231 }
232 {
233 Infrastructure = [
234 {
235 Grafana = {
236 description = "Monitoring";
237 href = "https://grafana.sbr.pm";
238 icon = "grafana.png";
239 ping = "https://grafana.sbr.pm";
240 statusStyle = "dot";
241 };
242 }
243 {
244 "Home Assistant" = {
245 description = "Home Automation";
246 href = "https://home.sbr.pm";
247 icon = "home-assistant.png";
248 ping = "https://home.sbr.pm";
249 statusStyle = "dot";
250 };
251 }
252 {
253 Traefik = {
254 description = "Reverse Proxy";
255 href = "https://traefik.sbr.pm";
256 icon = "traefik.png";
257 ping = "https://traefik.sbr.pm";
258 statusStyle = "dot";
259 };
260 }
261 ];
262 }
263 {
264 "Other Services" = [
265 {
266 Paperless = {
267 description = "Document Management";
268 href = "https://paperless.sbr.pm";
269 icon = "paperless.png";
270 ping = "https://paperless.sbr.pm";
271 statusStyle = "dot";
272 };
273 }
274 {
275 N8N = {
276 description = "Workflow Automation";
277 href = "https://n8n.sbr.pm";
278 icon = "n8n.png";
279 ping = "https://n8n.sbr.pm";
280 statusStyle = "dot";
281 };
282 }
283 {
284 Kiwix = {
285 description = "Offline Wikipedia";
286 href = "https://kiwix.sbr.pm";
287 icon = "kiwix.png";
288 ping = "https://kiwix.sbr.pm";
289 statusStyle = "dot";
290 };
291 }
292 ];
293 }
294 ];
295
296 bookmarks = [
297 {
298 Development = [
299 {
300 GitHub = [
301 {
302 abbr = "GH";
303 href = "https://github.com/vdemeester";
304 }
305 ];
306 }
307 {
308 "SourceHut" = [
309 {
310 abbr = "SH";
311 href = "https://sr.ht/~vdemeester";
312 }
313 ];
314 }
315 {
316 Codeberg = [
317 {
318 abbr = "CB";
319 href = "https://codeberg.org/vdemeester";
320 }
321 ];
322 }
323 ];
324 }
325 {
326 NixOS = [
327 {
328 "NixOS Search" = [
329 {
330 abbr = "NS";
331 href = "https://search.nixos.org";
332 }
333 ];
334 }
335 {
336 "NixOS Packages" = [
337 {
338 abbr = "NP";
339 href = "https://search.nixos.org/packages";
340 }
341 ];
342 }
343 {
344 "NixOS Options" = [
345 {
346 abbr = "NO";
347 href = "https://search.nixos.org/options";
348 }
349 ];
350 }
351 {
352 "Home Manager Options" = [
353 {
354 abbr = "HM";
355 href = "https://home-manager-options.extranix.com";
356 }
357 ];
358 }
359 {
360 "NixOS Wiki" = [
361 {
362 abbr = "NW";
363 href = "https://wiki.nixos.org";
364 }
365 ];
366 }
367 ];
368 }
369 {
370 Tools = [
371 {
372 "Claude Code" = [
373 {
374 abbr = "CC";
375 href = "https://claude.ai/code";
376 }
377 ];
378 }
379 {
380 "QMK Configurator" = [
381 {
382 abbr = "QMK";
383 href = "https://config.qmk.fm";
384 }
385 ];
386 }
387 {
388 "QMK Docs" = [
389 {
390 abbr = "QD";
391 href = "https://docs.qmk.fm";
392 }
393 ];
394 }
395 {
396 "Keymap Drawer" = [
397 {
398 abbr = "KD";
399 href = "https://caksoylar.github.io/keymap-drawer";
400 }
401 ];
402 }
403 ];
404 }
405 {
406 Documentation = [
407 {
408 "Homepage Docs" = [
409 {
410 abbr = "HP";
411 href = "https://gethomepage.dev";
412 }
413 ];
414 }
415 {
416 "Traefik Docs" = [
417 {
418 abbr = "TR";
419 href = "https://doc.traefik.io/traefik/";
420 }
421 ];
422 }
423 {
424 "Tekton Docs" = [
425 {
426 abbr = "TK";
427 href = "https://tekton.dev/docs/";
428 }
429 ];
430 }
431 ];
432 }
433 ];
434
435 widgets = [
436 {
437 resources = {
438 cpu = true;
439 memory = true;
440 disk = "/";
441 };
442 }
443 {
444 search = {
445 provider = "duckduckgo";
446 target = "_blank";
447 };
448 }
449 ];
450 };
451
452 # Create homepage group for secret file access
453 users.groups.homepage = { };
454
455 # Open firewall for local access
456 networking.firewall.allowedTCPPorts = [ 3001 ];
457
458 # Allow requests from all rhea domains and IPs (from globals.nix)
459 systemd.services.homepage-dashboard = {
460 environment = {
461 HOMEPAGE_ALLOWED_HOSTS = lib.mkForce allowedHosts;
462 HOMEPAGE_FILE_SONARR_KEY = config.age.secrets."exportarr-sonarr-apikey".path;
463 HOMEPAGE_FILE_RADARR_KEY = config.age.secrets."exportarr-radarr-apikey".path;
464 HOMEPAGE_FILE_LIDARR_KEY = config.age.secrets."exportarr-lidarr-apikey".path;
465 };
466 serviceConfig = {
467 SupplementaryGroups = [ "homepage" ];
468 };
469 };
470}