feat: refactored glance

This commit is contained in:
Nick 2025-05-25 18:28:37 -05:00
parent 3c230de4fd
commit 8d4ce8d2f9
17 changed files with 571 additions and 149 deletions

View file

@ -71,6 +71,7 @@ in
printManager printManager
finamp finamp
lingot lingot
keymapp
; ;
}; };
}; };

View file

@ -0,0 +1,11 @@
{
pkgs,
...
}:
{
home.packages = builtins.attrValues {
inherit (pkgs)
keymapp
;
};
}

View file

@ -23,15 +23,14 @@ let
"G, exec, scrcpy" "G, exec, scrcpy"
"R, exec, thunar" "R, exec, thunar"
"S, exec, steam" "S, exec, steam"
"E, exec, ghostty -e y"
"N, exec, signal-desktop" "N, exec, signal-desktop"
"T, exec, zeditor" "T, exec, zeditor"
"B, exec, floorp" "B, exec, floorp"
"Y, exec, freetube" "X, exec, freetube"
"V, exec, vesktop" "V, exec, vesktop"
"M, exec, element-desktop" "M, exec, element-desktop"
"D, exec, ghostty" "D, exec, ghostty"
"W, exec, bitwarden" "P, exec, bitwarden"
# Workspaces # Workspaces
"1, workspace, 1" "1, workspace, 1"
"2, workspace, 2" "2, workspace, 2"
@ -40,13 +39,18 @@ let
"5, workspace, 5" "5, workspace, 5"
# Windows # Windows
"Tab, killactive" "Tab, killactive"
"F, togglefloating"
"Backspace, layoutmsg, togglesplit" "Backspace, layoutmsg, togglesplit"
# Window Focus # Window Focus
"C, movefocus, l"
"A, movefocus, u"
"E, movefocus, d"
"I, movefocus, r"
"Left, movefocus, l" "Left, movefocus, l"
"Up, movefocus, u" "Up, movefocus, u"
"Down, movefocus, d" "Down, movefocus, d"
"Right, movefocus, r" "Right, movefocus, r"
"F, splitratio, -0.33"
"O, splitratio, 0.33"
"bracketleft, splitratio, -0.33" "bracketleft, splitratio, -0.33"
"bracketright, splitratio, 0.33" "bracketright, splitratio, 0.33"
# Audio # Audio
@ -64,7 +68,7 @@ let
# Super+shift binds # Super+shift binds
"S, exec, flameshot gui" "S, exec, flameshot gui"
"period, exec, emote" "period, exec, emote"
"Tab, fullscreen, 0" "T, fullscreen, 0"
# "Print, exec, grim -g \"$(slurp)\"" # "Print, exec, grim -g \"$(slurp)\""
]; ];
superCtrlBinds = builtins.map (x: "SUPER CTRL, " + x) [ superCtrlBinds = builtins.map (x: "SUPER CTRL, " + x) [
@ -73,6 +77,18 @@ let
altBinds = builtins.map (x: "CTRL ALT, " + x) [ altBinds = builtins.map (x: "CTRL ALT, " + x) [
# Alt binds # Alt binds
# "Tab, togglefloating"
# Window
"1, movetoworkspacesilent, 1"
"2, movetoworkspacesilent, 2"
"3, movetoworkspacesilent, 3"
"4, movetoworkspacesilent, 4"
"5, movetoworkspacesilent, 5"
# Window Move
"C, movewindow, l"
"A, movewindow, u"
"E, movewindow, d"
"I, movewindow, r"
]; ];
shiftBinds = builtins.map (x: "SHIFT, " + x) [ shiftBinds = builtins.map (x: "SHIFT, " + x) [
@ -85,13 +101,13 @@ let
ctrlShiftBinds = builtins.map (x: "CTRL SHIFT, " + x) [ ctrlShiftBinds = builtins.map (x: "CTRL SHIFT, " + x) [
# Ctrl+shift binds # Ctrl+shift binds
# Window # # Window
"Tab, togglefloating"
"1, movetoworkspacesilent, 1" "1, movetoworkspacesilent, 1"
"2, movetoworkspacesilent, 2" "2, movetoworkspacesilent, 2"
"3, movetoworkspacesilent, 3" "3, movetoworkspacesilent, 3"
"4, movetoworkspacesilent, 4" "4, movetoworkspacesilent, 4"
"5, movetoworkspacesilent, 5" "5, movetoworkspacesilent, 5"
# Window Move
"Left, movewindow, l" "Left, movewindow, l"
"Up, movewindow, u" "Up, movewindow, u"
"Down, movewindow, d" "Down, movewindow, d"

View file

@ -0,0 +1,8 @@
{
hide-footer = true;
# logo-url = /assets/logo.png;
# favicon-url = /assets/logo.png;
# app-name = "My Dashboard";
# app-icon-url = "/assets/app-icon.png";
# app-background-color = "#151519";
}

View file

@ -0,0 +1,40 @@
{ config, flake, ... }:
let
widgetsPath = ./widgets;
widgets = {
jellyfin = import (widgetsPath + /jellyfin) { inherit config flake; };
steam = import (widgetsPath + /steam);
podcasts = import (widgetsPath + /podcasts.nix);
calendar = import (widgetsPath + /calendar.nix);
clock = import (widgetsPath + /clock.nix);
weather = import (widgetsPath + /weather.nix);
};
in
[
{
columns = [
{
size = "full";
widgets = [
widgets.podcasts
];
}
{
size = "small";
widgets = [
widgets.jellyfin
widgets.steam
];
}
{
size = "small";
widgets = [
widgets.calendar
widgets.weather
widgets.clock
];
}
];
name = "Dashboard";
}
]

View file

@ -0,0 +1,9 @@
{ service, ... }:
{
# assets-path = "/home/${user0}/Files/Projects/dotfiles/modules/nixos/services/glance/assets";
port = service.ports.port0;
# auth = {
# secret-key = config.sops.secrets."${service.name}-key".path;
# users.${user0}.password = config.sops.secrets."${service.name}-${user0}-pass".path;
# };
}

View file

@ -0,0 +1,7 @@
{
background-color = "232 23 18";
contrast-multiplier = 1.2;
primary-color = "220 83 75";
positive-color = "105 48 72";
negative-color = "351 74 73";
}

View file

@ -0,0 +1,5 @@
{
type = "calendar";
title = "Calendar";
style = "vertical-cards";
}

View file

@ -0,0 +1,22 @@
{
type = "clock";
hour-format = "12h";
timezones = [
{
timezone = "America/Winnipeg";
label = "Winnipeg, MB";
}
{
timezone = "Europe/Berlin";
label = "Berlin, DE";
}
{
timezone = "Asia/Kolkata";
label = "Kolkata, IN";
}
{
timezone = "Asia/Tokyo";
label = "Tokyo, JP";
}
];
}

View file

@ -0,0 +1,326 @@
''
{{ $mediaServer := .Options.StringOr "media-server" "" }}
{{ $baseURL := .Options.StringOr "base-url" "" }}
{{ $apiKey := .Options.StringOr "api-key" "" }}
{{ $userName := .Options.StringOr "user-name" "" }}
{{ define "errorMsg" }}
<div class="widget-error-header">
<div class="color-negative size-h3">ERROR</div>
<svg class="widget-error-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"></path>
</svg>
</div>
<p class="break-all">{{ . }}</p>
{{ end }}
{{ if or
(eq $mediaServer "")
(eq $baseURL "")
(eq $apiKey "")
(and (eq $mediaServer "jellyfin") (eq $userName ""))
}}
{{ template "errorMsg" "Some required options are not set" }}
{{ else }}
{{ $historyLength := .Options.StringOr "history-length" "10" }}
{{ $mediaTypes := .Options.StringOr "media-types" "" }}
{{ if eq $mediaServer "tautulli" }}
{{ $mediaTypes = .Options.StringOr "media-types" "movie,episode,track" }}
{{ else if or (eq $mediaServer "jellyfin") (eq $mediaServer "emby") }}
{{ $mediaTypes = .Options.StringOr "media-types" "Movie,Episode,Audio" }}
{{ end }}
{{ $isSmallColumn := .Options.BoolOr "small-column" false }}
{{ $isCompact := .Options.BoolOr "compact" true }}
{{ $showThumbnail := .Options.BoolOr "show-thumbnail" false }}
{{ $thumbAspectRatio := .Options.StringOr "thumbnail-aspect-ratio" "" }}
{{ $showUser := .Options.BoolOr "show-user" true }}
{{ $timeAbsolute := .Options.BoolOr "time-absolute" false }}
{{ $timeFormat := .Options.StringOr "time-format" "Jan 02 15:04" }}
{{ $userID := "" }}
{{ $historyRequestURL := "" }}
{{ $usersRequestURL := "" }}
{{ $historyCall := "" }}
{{ $usersCall := "" }}
{{ $history := "" }}
{{ $users := "" }}
{{ if eq $mediaServer "plex" }}
{{ $historyRequestURL = concat $baseURL "/status/sessions/history/all" }}
{{ $historyCall = newRequest $historyRequestURL
| withParameter "limit" $historyLength
| withParameter "sort" "viewedAt:desc"
| withHeader "Accept" "application/json"
| withHeader "X-Plex-Token" $apiKey
| getResponse }}
{{ if $historyCall.JSON.Exists "MediaContainer" }}
{{ $history = $historyCall.JSON.Array "MediaContainer.Metadata" }}
{{ else }}
{{ template "errorMsg" (concat "Could not fetch " $mediaServer " API.") }}
{{ end }}
{{ $usersRequestURL = concat $baseURL "/accounts" }}
{{ $usersCall = newRequest $usersRequestURL
| withHeader "Accept" "application/json"
| withHeader "X-Plex-Token" $apiKey
| getResponse }}
{{ $users = $usersCall.JSON.Array "MediaContainer.Account" }}
{{ else if eq $mediaServer "tautulli" }}
{{ $historyRequestURL = concat $baseURL "/api/v2" }}
{{ $historyCall = newRequest $historyRequestURL
| withParameter "apikey" $apiKey
| withParameter "cmd" "get_history"
| withParameter "length" $historyLength
| withParameter "media_type" $mediaTypes
| withHeader "Accept" "application/json"
| getResponse }}
{{ if eq $historyCall.Response.StatusCode 200 }}
{{ $history = $historyCall.JSON.Array "response.data.data" }}
{{ else }}
{{ template "errorMsg" (concat "Could not fetch " $mediaServer " API.") }}
{{ end }}
{{ else if or (eq $mediaServer "jellyfin") (eq $mediaServer "emby") }}
{{ $usersRequestURL = concat $baseURL "/Users" }}
{{ $usersCall = newRequest $usersRequestURL
| withParameter "api_key" $apiKey
| withHeader "Accept" "application/json"
| getResponse }}
{{ $usersList := $usersCall.JSON.Array "" }}
{{ range $i, $user := $usersList }}
{{ if eq ($user.String "Name") $userName }}
{{ $userID = $user.String "Id" }}
{{ break }}
{{ end }}
{{ end }}
{{ if eq $userID "" }}
{{ template "errorMsg" (concat "User '" $userName "' not found.") }}
{{ end }}
{{ $historyRequestURL = concat $baseURL "/Users/" $userID "/Items" }}
{{ $historyCall = newRequest $historyRequestURL
| withParameter "api_key" $apiKey
| withParameter "Limit" $historyLength
| withParameter "IncludeItemTypes" $mediaTypes
| withParameter "Recursive" "true"
| withParameter "isPlayed" "true"
| withParameter "sortBy" "DatePlayed"
| withParameter "sortOrder" "Descending"
| withParameter "Fields" "UserDataLastPlayedDate"
| withHeader "Accept" "application/json"
| getResponse }}
{{ $history = $historyCall.JSON.Array "Items" }}
{{ end }}
{{ if and (eq $historyCall.Response.StatusCode 200) (eq (len $history) 0) }}
<p>Nothing has been played. Start streaming something!</p>
{{ else }}
<div class="carousel-container show-right-cutoff">
<div class="cards-horizontal carousel-items-container">
{{ range $n, $item := $history }}
{{ $mediaType := "" }}
{{ $isMovie := false }}
{{ $isShows := false }}
{{ $isMusic := false }}
{{ $title := "" }}
{{ $showTitle := "" }}
{{ $showSeason := "" }}
{{ $showEpisode := "" }}
{{ $artist := "" }}
{{ $albumTitle := "" }}
{{ $thumbURL := "" }}
{{ $playedAt := "" }}
{{ if eq $mediaServer "plex" }}
{{ $userID = $item.Int "accountID" }}
{{ range $n, $u := $users }}
{{ if eq $userID ($u.Int "id") }}
{{ $userName = $u.String "name" }}
{{ break }}
{{ end }}
{{ end }}
{{ $mediaType = $item.String "type" }}
{{ $isMovie = eq $mediaType "movie" }}
{{ $isShows = eq $mediaType "episode" }}
{{ $isMusic = eq $mediaType "track" }}
{{ $title = $item.String "title" }}
{{ if $isShows }}
{{ $showTitle = $item.String "grandparentTitle" }}
{{ $showSeason = $item.String "parentIndex" }}
{{ $showEpisode = $item.String "index" }}
{{ else if $isMusic }}
{{ $artist = $item.String "grandparentTitle" }}
{{ $albumTitle = $item.String "parentTitle" }}
{{ end }}
{{ $thumbID := $item.String "thumb" }}
{{ if or $isShows $isMusic}}
{{ $thumbID = $item.String "parentThumb" }}
{{ end }}
{{ $thumbURL = concat $baseURL $thumbID "?X-Plex-Token=" $apiKey }}
{{ $time := $item.String "viewedAt" }}
{{ if $timeAbsolute }}
{{ $playedAt = $time | parseLocalTime "unix" | formatTime $timeFormat }}
{{ else }}
{{ $playedAt = $time | parseRelativeTime "unix" }}
{{ end }}
{{ else if eq $mediaServer "tautulli" }}
{{ $userName = $item.String "user" }}
{{ $mediaType = $item.String "media_type" }}
{{ $isMovie = eq $mediaType "movie" }}
{{ $isShows = eq $mediaType "episode" }}
{{ $isMusic = eq $mediaType "track" }}
{{ $title = $item.String "title" }}
{{ if $isShows }}
{{ $showTitle = $item.String "grandparent_title" }}
{{ $showSeason = $item.String "parent_media_index" }}
{{ $showEpisode = $item.String "media_index" }}
{{ else if $isMusic }}
{{ $artist = $item.String "grandparent_title" }}
{{ $albumTitle = $item.String "parent_title" }}
{{ end }}
{{ $thumbID := $item.String "thumb" }}
{{ $thumbURL = concat $baseURL "/api/v2?apikey=" $apiKey "&cmd=pms_image_proxy&img=" $thumbID }}
{{ $time := $item.String "date" }}
{{ if $timeAbsolute }}
{{ $playedAt = $time | parseLocalTime "unix" | formatTime $timeFormat }}
{{ else }}
{{ $playedAt = $time | parseRelativeTime "unix" }}
{{ end }}
{{ else if or (eq $mediaServer "jellyfin") (eq $mediaServer "emby") }}
{{ $mediaType = $item.String "Type" }}
{{ $isMovie = eq $mediaType "Movie" }}
{{ $isShows = eq $mediaType "Episode" }}
{{ $isMusic = eq $mediaType "Audio" }}
{{ $title = $item.String "Name" }}
{{ if $isShows }}
{{ $showTitle = $item.String "SeriesName" }}
{{ $showSeason = $item.String "ParentIndexNumber" }}
{{ $showEpisode = $item.String "IndexNumber" }}
{{ else if $isMusic }}
{{ $artist = $item.String "AlbumArtist" }}
{{ $albumTitle = $item.String "Album" }}
{{ end }}
{{ $thumbID := $item.String "Id" }}
{{ if $isShows }}
{{ $thumbID = $item.String "SeasonId" }}
{{ end }}
{{ $thumbURL = concat $baseURL "/Items/" $thumbID "/Images/Primary?api_key=" $apiKey }}
{{ $time := $item.String "UserData.LastPlayedDate" }}
{{ if $timeAbsolute }}
{{ $playedAt = $time | parseLocalTime "rfc3339" | formatTime $timeFormat }}
{{ else }}
{{ $playedAt = $time | parseRelativeTime "rfc3339" }}
{{ end }}
{{ end }}
{{ $showInfoFormat := concat "Season " $showSeason " Episode " $showEpisode}}
{{ if $isCompact }}
{{ $showInfoFormat = concat "S" $showSeason "E" $showEpisode}}
{{ end }}
<div class="card widget-content-frame">
{{ if $showThumbnail }}
<img src="{{ $thumbURL | safeURL }}"
alt="{{ $title }} thumbnail"
loading="lazy"
class="media-server-thumbnail shrink-0"
style="
object-fit: cover;
border-radius: var(--border-radius) var(--border-radius) 0 0;
{{ if eq $thumbAspectRatio "square" }}
aspect-ratio: 1;
{{ else if eq $thumbAspectRatio "portrait" }}
aspect-ratio: 3/4;
{{ else if eq $thumbAspectRatio "landscape" }}
aspect-ratio: 4/3;
{{ else }}
aspect-ratio: initial;
{{ end }}
"
/>
{{ end }}
<div class="grow padding-inline-widget margin-top-10 margin-bottom-10">
<ul class="flex flex-column justify-evenly margin-bottom-3 {{if $isSmallColumn}}size-h6{{end}}" style="height: 100%;">
{{ if $isCompact }}
<ul class="list-horizontal-text flex-nowrap">
{{ if $showUser }}
<li class="color-primary text-truncate">{{ $userName }}</li>
{{ end }}
{{ if $timeAbsolute }}
<li class="text-truncate">{{ $playedAt }}</li>
{{ else }}
<li class="shrink-0">
<span {{ $playedAt }}></span>
{{ if not $showUser }}
<span> ago</span>
{{ end }}
</li>
{{ end }}
</ul>
{{ if $isShows }}
<ul class="list-horizontal-text flex-nowrap">
<li class="text-truncate">{{ $showInfoFormat }}</li>
<li class="text-truncate">{{ $showTitle }}</li>
</ul>
{{ else if $isMusic }}
<ul class="list-horizontal-text flex-nowrap">
<li class="text-truncate">{{ $artist }}</li>
<li class="text-truncate">{{ $albumTitle }}</li>
</ul>
{{ end }}
<li class="text-truncate">{{ $title }}</li>
{{ else }}
{{ if $showUser }}
<li class="color-primary text-truncate">{{ $userName }}</li>
{{ end }}
{{ if $timeAbsolute }}
<li class="text-truncate">{{ $playedAt }}</li>
{{ else }}
<li class="text-truncate">
<span {{ $playedAt }}></span>
<span> ago</span>
</li>
{{ end }}
{{ if $isShows }}
<li class="text-truncate">{{ $showTitle }}</li>
<li class="text-truncate">{{ $showInfoFormat }}</li>
{{ else if $isMusic }}
<li class="text-truncate">{{ $artist }}</li>
<li class="text-truncate">{{ $albumTitle }}</li>
{{ end }}
<li class="text-truncate">{{ $title }}</li>
{{ end }}
</ul>
</div>
</div>
{{ end }}
</div>
</div>
{{ end }}
{{ end }}
''

View file

@ -0,0 +1,30 @@
{ config, flake, ... }:
let
inherit (flake.config.people) user0;
inherit (flake.config.people.users.${user0}) name;
inherit (flake.config.services.instances) glance jellyfin web;
service = glance;
jellyfinUserName = name;
jellyfinHost = "https://${jellyfin.subdomain}.${web.domains.url0}";
in
{
type = "custom-api";
title = "Jellyfin History";
frameless = true;
cache = "5m";
options = {
media-server = "jellyfin";
base-url = jellyfinHost;
api-key = config.sops.secrets."${service.name}-${jellyfin.name}".path;
user-name = jellyfinUserName;
history-length = "10";
small-column = true;
compact = true;
show-thumbnail = false;
thumbnail-aspect-ratio = "default";
show-user = true;
time-absolute = false;
time-format = "Jan 02 15:04";
};
template = import ./config;
}

View file

@ -0,0 +1,20 @@
{
type = "rss";
title = "Podcasts";
style = "detailed-list";
collapse-after = 6;
feeds = [
{
url = "https://sigmanutrition.libsyn.com/rss/";
title = "Sigma Nutrition Radio";
}
{
url = "https://wakingup.libsyn.com/rss";
title = "Making Sense with Sam Harris";
}
{
url = "https://feeds.simplecast.com/uNKL_XD_";
title = "Docs Who Lift";
}
];
}

View file

@ -0,0 +1,14 @@
''
<ul class="list list-gap-10 collapsible-container" data-collapse-after="5">
{{ range .JSON.Array "specials.items" }}
<li>
<a class="size-h4 color-highlight block text-truncate" href="https://store.steampowered.com/app/{{ .Int "id" }}/">{{ .String "name" }}</a>
<ul class="list-horizontal-text">
<li>{{ .Int "final_price" | toFloat | mul 0.01 | printf "$%.2f" }}</li>
{{ $discount := .Int "discount_percent" }}
<li{{ if ge $discount 40 }} class="color-positive"{{ end }}>{{ $discount }}% off</li>
</ul>
</li>
{{ end }}
</ul>
''

View file

@ -0,0 +1,7 @@
{
type = "custom-api";
title = "Steam Specials";
cache = "12h";
url = "https://store.steampowered.com/api/featuredcategories?cc=ca";
template = import ./config;
}

View file

@ -0,0 +1,7 @@
{
type = "weather";
title = "Weather";
units = "metric";
hour-format = "12h";
location = "Winnipeg, Manitoba, Canada";
}

View file

@ -1,149 +1,52 @@
{ config, flake, ... }: { config, flake, ... }:
let let
inherit (flake.config.people) user0; inherit (flake.config.people) user0;
inherit (flake.config.services.instances) glance web; inherit (flake.config.services.instances) glance jellyfin web;
inherit (flake.config.machines.devices) ceres; inherit (flake.config.machines.devices) ceres;
service = glance; service = glance;
configPath = ./config;
configImports = {
server = import (configPath + /server.nix) { inherit service; };
branding = import (configPath + /branding.nix);
theme = import (configPath + /theme.nix);
pages = import (configPath + /pages.nix) { inherit config flake; };
};
in in
{ {
services = { services = {
glance = { glance = {
enable = true; enable = true;
settings = { settings = configImports;
server = {
# assets-path = "/home/${user0}/Files/Projects/dotfiles/modules/nixos/services/glance/assets";
port = service.ports.port0;
};
# auth = {
# secret-key = config.sops.secrets."${service.name}-key".path;
# users.${user0}.password = config.sops.secrets."${service.name}-${user0}-pass".path;
# };
branding = {
hide-footer = true;
# logo-url = /assets/logo.png;
# favicon-url = /assets/logo.png;
# app-name = "My Dashboard";
# app-icon-url = "/assets/app-icon.png";
# app-background-color = "#151519";
};
theme = {
background-color = "232 23 18";
contrast-multiplier = 1.2;
primary-color = "220 83 75";
positive-color = "105 48 72";
negative-color = "351 74 73";
};
pages = [
{
columns = [
{
size = "full";
widgets = [
{
type = "rss";
title = "Podcasts";
style = "detailed-list";
collapse-after = 6;
feeds = [
{
url = "https://sigmanutrition.libsyn.com/rss/";
title = "Sigma Nutrition Radio";
}
{
url = "https://wakingup.libsyn.com/rss";
title = "Making Sense with Sam Harris";
}
{
url = "https://feeds.simplecast.com/uNKL_XD_";
title = "Docs Who Lift";
}
];
}
];
}
{
size = "small";
widgets = [
{
type = "calendar";
title = "Calendar";
style = "vertical-cards";
}
{
type = "weather";
title = "Weather";
units = "metric";
hour-format = "12h";
location = "Winnipeg, Manitoba, Canada";
}
{
type = "clock";
hour-format = "12h";
timezones = [
{
timezone = "America/Winnipeg";
label = "Winnipeg, MB";
}
{
timezone = "Europe/Berlin";
label = "Berlin, DE";
}
{
timezone = "Asia/Kolkata";
label = "Kolkata, IN";
}
{
timezone = "Asia/Tokyo";
label = "Tokyo, JP";
}
];
}
];
}
];
name = "Dashboard";
}
];
};
}; };
# sops =
# let
# sopsPath = secret: {
# path = "${service.sops.path0}/${service.name}-${secret}";
# owner = service.name;
# mode = "600";
# };
# in
# {
# secrets = builtins.listToAttrs (
# map
# (secret: {
# name = "${service.name}-${secret}";
# value = sopsPath secret;
# })
# [
# "key"
# "${user0}-pass"
# ]
# );
# };
# fileSystems."/var/lib/${service.name}" = {
# device = service.paths.path0;
# fsType = "none";
# options = [
# "bind"
# ];
# depends = [
# ceres.storage0.mount
# ];
# };
# systemd.tmpfiles.rules = [
# "Z ${service.paths.path0} 755 ${service.name} ${service.name} -"
# "Z ${service.sops.path0} 755 ${service.name} ${service.name} -"
# ];
}; };
sops =
let
sopsPath = secret: {
path = "${service.sops.path0}/${service.name}-${secret}";
owner = "root";
mode = "600";
};
in
{
secrets = builtins.listToAttrs (
map
(secret: {
name = "${service.name}-${secret}";
value = sopsPath secret;
})
[
# "key"
# "${user0}-pass"
jellyfin.name
]
);
};
systemd.tmpfiles.rules = [
# "Z ${service.paths.path0} 755 ${service.name} ${service.name} -"
# "Z ${service.sops.path0} 755 root root -"
];
networking = { networking = {
firewall = { firewall = {
allowedTCPPorts = [ allowedTCPPorts = [

View file

@ -34,11 +34,8 @@ wireguard-CA363: ENC[AES256_GCM,data:iGiAjP5Dbw0kXR3iM50YTS8jBXODNr//W/0OPMAiu1G
wireguard-CA220: ENC[AES256_GCM,data:rNy/IMKqAOsgMUu5r8BZsjTCu0L5fDDDV3/g+pkhW1y44Y2rqhhsZgcXG5M=,iv:onyHBn4npqiwC/v37SOMJLLhdfcrtvPmKbMVTgxaSQg=,tag:OmXDL3oYCDPwH1yBsKAYKQ==,type:str] wireguard-CA220: ENC[AES256_GCM,data:rNy/IMKqAOsgMUu5r8BZsjTCu0L5fDDDV3/g+pkhW1y44Y2rqhhsZgcXG5M=,iv:onyHBn4npqiwC/v37SOMJLLhdfcrtvPmKbMVTgxaSQg=,tag:OmXDL3oYCDPwH1yBsKAYKQ==,type:str]
wireguard-CA358: ENC[AES256_GCM,data:/VewmiNfRc9/wSE7TT+z1F9LLIvr/5wPsQZ/zBwAh3dEi9yswOGyde2b/XQ=,iv:7U5dmqFiwhCoL1moGSfHprv85o5TdMr6T2sNk5gH82I=,tag:T1hqh8CiO2iBa+ksaiKCtA==,type:str] wireguard-CA358: ENC[AES256_GCM,data:/VewmiNfRc9/wSE7TT+z1F9LLIvr/5wPsQZ/zBwAh3dEi9yswOGyde2b/XQ=,iv:7U5dmqFiwhCoL1moGSfHprv85o5TdMr6T2sNk5gH82I=,tag:T1hqh8CiO2iBa+ksaiKCtA==,type:str]
wireguard-CA627: ENC[AES256_GCM,data:chmDsH2nE0nagjFRZWuxX08/Ykt+rIgCHYkMHd+7nIqihK5SebF7MJlrp84=,iv:NVOlGE7W70nQ0UM/i5WixJvDULO3Y4cLf8h+OAGHhQQ=,tag:L123ShCnr9+kIg1itIoqBA==,type:str] wireguard-CA627: ENC[AES256_GCM,data:chmDsH2nE0nagjFRZWuxX08/Ykt+rIgCHYkMHd+7nIqihK5SebF7MJlrp84=,iv:NVOlGE7W70nQ0UM/i5WixJvDULO3Y4cLf8h+OAGHhQQ=,tag:L123ShCnr9+kIg1itIoqBA==,type:str]
glance-jellyfin: ENC[AES256_GCM,data:3ZR8OOgysWNqkJGqKjdDg/0UUgMBbrrgdHbuRQcM+po=,iv:EDG4DEBWmwu//qf+K+V0HqTYc8q78fF9dDb+37zHQIk=,tag:fSok//JeM635gWZ37+cItw==,type:str]
sops: sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age: age:
- recipient: age19dpncsdphdt2tmknjs99eghk527pvdrw0m29qjn2z2gg3et5tdtqycqhl0 - recipient: age19dpncsdphdt2tmknjs99eghk527pvdrw0m29qjn2z2gg3et5tdtqycqhl0
enc: | enc: |
@ -49,8 +46,7 @@ sops:
bXBOa1VSakoyaWxpODJEOU11QUZCaUEK8Ch9Ten3DdrPHF1DTH2qei85AlHUOaLD bXBOa1VSakoyaWxpODJEOU11QUZCaUEK8Ch9Ten3DdrPHF1DTH2qei85AlHUOaLD
aNfzakake7ej+MxJYdKEU0bcWofNMKzIlZa2uM10KZSENDP8d8qlig== aNfzakake7ej+MxJYdKEU0bcWofNMKzIlZa2uM10KZSENDP8d8qlig==
-----END AGE ENCRYPTED FILE----- -----END AGE ENCRYPTED FILE-----
lastmodified: "2025-04-01T03:33:13Z" lastmodified: "2025-05-25T21:49:29Z"
mac: ENC[AES256_GCM,data:zGnWN7U7d2+REQ1Iy2JEY92hWtS3Lxl6uqG4/kVGwE5fxj65gv1cv/38ulNUhCGY9BEiOpDzQBgoAy9WmvsKathHb7z9NEXrHpVtvNgRJVfVjuduZgGvrAFRLFXV1iLfQXk8wl64/e5YXD1Cbs80+ky9kmA4nl/rM0rlEkK+WOo=,iv:YL+Jv6yfe7/EASfDNkdFhOw29iXRS3rdPAplEE3i1hE=,tag:7NLlenTFk0hIyf+FEa3oJg==,type:str] mac: ENC[AES256_GCM,data:7JD1leZhMyHg7twZXf/0chy2PbGVS6IE4wayXwzHemFccS0j64JY7/T1IWnL5bCj1V2aszocUWvwZrLhPnYinF6rjDRzLnRCtZ5Nb88S0WyTpDOBAj6GDEgs+e08e+RjCwfWdIr068HD4b/B+9M8S8qJtEZ++Uib7awzIsQphgQ=,iv:TY2wisEJiv2TODvaLl4qjILuSclzKyGVRMjrbrLCvms=,tag:+C9DW8443OOOo7a1Cbl9fQ==,type:str]
pgp: []
unencrypted_suffix: _unencrypted unencrypted_suffix: _unencrypted
version: 3.9.4 version: 3.10.2