mirror of
https://github.com/danbulant/dotfiles
synced 2026-05-19 04:18:55 +00:00
improved glance layout
This commit is contained in:
parent
c51430f9c1
commit
0f14dfd36c
7 changed files with 543 additions and 71 deletions
|
|
@ -211,6 +211,7 @@ in
|
|||
};
|
||||
pages = import ./glance-pages.nix;
|
||||
};
|
||||
environmentFile = "/etc/secrets/glance.env";
|
||||
};
|
||||
|
||||
caddy = {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,29 @@
|
|||
let
|
||||
jellyfinConfig =
|
||||
{
|
||||
title,
|
||||
library-name ? null,
|
||||
mode,
|
||||
}:
|
||||
{
|
||||
inherit title;
|
||||
type = "custom-api";
|
||||
# url = "http://jellyfin.eisen.danbulant.cloud";
|
||||
frameless = true;
|
||||
cache = "5m";
|
||||
options = {
|
||||
inherit library-name mode;
|
||||
base-url = "http://jellyfin.eisen.danbulant.cloud";
|
||||
api-key = "\${JELLYFIN_KEY}";
|
||||
user-name = "dan";
|
||||
item-count = "10";
|
||||
small-column = false;
|
||||
show-thumbnail = true;
|
||||
thumbnail-aspect-ratio = "default";
|
||||
};
|
||||
template = builtins.readFile ./glance/jellyfin-latest;
|
||||
};
|
||||
in
|
||||
[
|
||||
{
|
||||
name = "Home";
|
||||
|
|
@ -17,6 +43,24 @@
|
|||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
|
||||
# - type: custom-api
|
||||
# title: "Jellyfin/Emby Stats"
|
||||
# base-url: ${JELLYFIN_URL}
|
||||
# options:
|
||||
# url: ${JELLYFIN_URL}
|
||||
# key: ${JELLYFIN_API_KEY}
|
||||
#
|
||||
type = "custom-api";
|
||||
title = "Jellyfin Stats";
|
||||
# url = "http://jellyfin.eisen.danbulant.cloud";
|
||||
options = {
|
||||
url = "http://jellyfin.eisen.danbulant.cloud";
|
||||
key = "\${JELLYFIN_KEY}";
|
||||
};
|
||||
template = builtins.readFile ./glance/jellyfin-stats;
|
||||
}
|
||||
{
|
||||
type = "custom-api";
|
||||
title = "Uptime Kuma";
|
||||
|
|
@ -28,61 +72,24 @@
|
|||
};
|
||||
};
|
||||
cache = "10m";
|
||||
template = ''
|
||||
{{ $hb := .Subrequest "heartbeats" }}
|
||||
|
||||
{{ if not (.JSON.Exists "publicGroupList") }}
|
||||
<p class="color-negative">Error reading response</p>
|
||||
{{ else if eq (len (.JSON.Array "publicGroupList")) 0 }}
|
||||
<p>No monitors found</p>
|
||||
{{ else }}
|
||||
|
||||
<ul class="dynamic-columns list-gap-8">
|
||||
{{ range .JSON.Array "publicGroupList" }}
|
||||
{{ range .Array "monitorList" }}
|
||||
{{ $id := .String "id" }}
|
||||
{{ $hbArray := $hb.JSON.Array (print "heartbeatList." $id) }}
|
||||
<div class="flex items-center gap-12">
|
||||
<a class="size-title-dynamic color-highlight text-truncate block grow" href="http://status.eisen/dashboard/{{ $id }}"
|
||||
target="_blank" rel="noreferrer">
|
||||
{{ .String "name" }} </a>
|
||||
|
||||
{{ if gt (len $hbArray) 0 }}
|
||||
{{ $latest := index $hbArray (sub (len $hbArray) 1) }}
|
||||
{{ if eq ($latest.Int "status") 1 }}
|
||||
<div>{{ $latest.Int "ping" }}ms</div>
|
||||
<div class="monitor-site-status-icon-compact" title="OK">
|
||||
<svg fill="var(--color-positive)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm3.857-9.809a.75.75 0 0 0-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 1 0-1.06 1.061l2.5 2.5a.75.75 0 0 0 1.137-.089l4-5.5Z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
{{ else }}
|
||||
<div><span class="color-negative">DOWN</span></div>
|
||||
<div class="monitor-site-status-icon-compact" title="{{ if $latest.Exists "msg" }}{{ $latest.String "msg" }}{{ else
|
||||
}}Error{{ end }}">
|
||||
<svg fill="var(--color-negative)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd"
|
||||
d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495ZM10 5a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 10 5Zm0 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<div><span class="color-negative">No data</span></div>
|
||||
<div class="monitor-site-status-icon-compact" title="No data available">
|
||||
<svg fill="var(--color-negative)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path d="M10 18a8 8 0 1 1 0-16 8 8 0 0 1 0 16zm0-2a6 6 0 1 0 0-12 6 6 0 0 0 0 12zm-.75-8a.75.75 0 0 1 1.5 0v3a.75.75 0 0 1-1.5 0V8zm.75 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ end }}
|
||||
'';
|
||||
template = builtins.readFile ./glance/uptime-kuma;
|
||||
}
|
||||
{
|
||||
type = "custom-api";
|
||||
title = "Tailscale";
|
||||
title-url = "https://login.tailscale.com/admin/machines";
|
||||
url = "https://api.tailscale.com/api/v2/tailnet/-/devices";
|
||||
headers = {
|
||||
Authorization = "Bearer \${TAILSCALE_API_KEY}";
|
||||
};
|
||||
cache = "10m";
|
||||
options = {
|
||||
# collapseAfter: 4
|
||||
# disableOfflineIndicator: true
|
||||
# disableUpdateIndicator: true
|
||||
# prioritiseTags: true
|
||||
};
|
||||
template = builtins.readFile ./glance/tailscale;
|
||||
}
|
||||
];
|
||||
}
|
||||
|
|
@ -149,6 +156,25 @@
|
|||
}
|
||||
];
|
||||
}
|
||||
(jellyfinConfig {
|
||||
title = "Movies";
|
||||
mode = "nextup";
|
||||
})
|
||||
{
|
||||
type = "group";
|
||||
widgets = [
|
||||
(jellyfinConfig {
|
||||
title = "Shows";
|
||||
library-name = "Shows";
|
||||
mode = "latest";
|
||||
})
|
||||
(jellyfinConfig {
|
||||
title = "Movies";
|
||||
library-name = "Movies";
|
||||
mode = "latest";
|
||||
})
|
||||
];
|
||||
}
|
||||
# {
|
||||
# type = "docker-containers";
|
||||
# }
|
||||
|
|
@ -163,17 +189,27 @@
|
|||
units = "metric";
|
||||
"hour-format" = "24h";
|
||||
}
|
||||
# {
|
||||
# type = "releases";
|
||||
# cache = "1d";
|
||||
# repositories = [
|
||||
# "glanceapp/glance"
|
||||
# "go-gitea/gitea"
|
||||
# "immich-app/immich"
|
||||
# "syncthing/syncthing"
|
||||
# "9001/copyparty"
|
||||
# "caddyserver/caddy"
|
||||
# ];
|
||||
# }
|
||||
{
|
||||
type = "releases";
|
||||
cache = "1d";
|
||||
repositories = [
|
||||
"glanceapp/glance"
|
||||
"go-gitea/gitea"
|
||||
"immich-app/immich"
|
||||
"syncthing/syncthing"
|
||||
"9001/copyparty"
|
||||
"caddyserver/caddy"
|
||||
];
|
||||
type = "custom-api";
|
||||
title = "xkcd";
|
||||
cache = "10m";
|
||||
url = "https://xkcd.com/info.0.json";
|
||||
template = ''
|
||||
<body> {{ .JSON.String "title" }}</body>
|
||||
<img src="{{ .JSON.String "img" }}"></img>
|
||||
'';
|
||||
}
|
||||
{
|
||||
type = "custom-api";
|
||||
|
|
|
|||
231
servers/eisen/glance/jellyfin-latest
Normal file
231
servers/eisen/glance/jellyfin-latest
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
|
||||
{{/* Required config options */}}
|
||||
{{ $baseURL := .Options.StringOr "base-url" "" }}
|
||||
{{ $apiKey := .Options.StringOr "api-key" "" }}
|
||||
{{ $userName := .Options.StringOr "user-name" "" }}
|
||||
|
||||
{{/* Required config options for "latest" mode */}}
|
||||
{{ $libraryName := .Options.StringOr "library-name" "" }}
|
||||
|
||||
{{/* Optional config options */}}
|
||||
{{ $mode := .Options.StringOr "mode" "latest" }}
|
||||
{{ $itemCount := .Options.StringOr "item-count" "10" }}
|
||||
{{ $mediaTypes := .Options.StringOr "media-types" "Movie,Episode,MusicAlbum" }}
|
||||
{{ $thumbAspectRatio := .Options.StringOr "thumbnail-aspect-ratio" "" }}
|
||||
{{ $isSmallColumn:= .Options.BoolOr "small-column" false }}
|
||||
{{ $showThumbnail := .Options.BoolOr "show-thumbnail" false }}
|
||||
{{ $showProgressBar := .Options.BoolOr "progress-bar" true }}
|
||||
|
||||
{{/* Error message template */}}
|
||||
{{ 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 }}
|
||||
|
||||
{{/* Check required fields */}}
|
||||
{{ if or (eq $baseURL "") (eq $apiKey "") (eq $userName "") (eq $mode "") (and (eq $mode "latest") (eq $libraryName "")) }}
|
||||
{{ template "errorMsg" "Some required options are not set." }}
|
||||
{{ else }}
|
||||
|
||||
{{/* Fetch user ID */}}
|
||||
{{ $userID := "" }}
|
||||
{{ $usersCall := newRequest (print $baseURL "/Users")
|
||||
| withParameter "api_key" $apiKey
|
||||
| withHeader "Accept" "application/json"
|
||||
| getResponse }}
|
||||
|
||||
{{ range $i, $user := $usersCall.JSON.Array "" }}
|
||||
{{ if eq ($user.String "Name") $userName }}
|
||||
{{ $userID = $user.String "Id" }}
|
||||
{{ break }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if eq $userID "" }}
|
||||
{{ template "errorMsg" (printf "User '%s' not found." $userName) }}
|
||||
{{ else }}
|
||||
|
||||
{{ $items := "" }}
|
||||
|
||||
{{ if eq $mode "latest" }}
|
||||
|
||||
{{/* Fetch library ID */}}
|
||||
{{ $libraryID := "" }}
|
||||
{{ $userViewsCall := newRequest (print $baseURL "/UserViews")
|
||||
| withParameter "api_key" $apiKey
|
||||
| withParameter "userId" $userID
|
||||
| withHeader "Accept" "application/json"
|
||||
| getResponse }}
|
||||
|
||||
{{ range $i, $item := $userViewsCall.JSON.Array "Items" }}
|
||||
{{ if eq ($item.String "Name") $libraryName }}
|
||||
{{ $libraryID = $item.String "Id" }}
|
||||
{{ break }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ if eq $libraryID "" }}
|
||||
{{ template "errorMsg" (printf "Library '%s' not found." $libraryName) }}
|
||||
{{ else }}
|
||||
{{/* Fetch latest items */}}
|
||||
{{ $latestCall := newRequest (print $baseURL "/Users/" $userID "/Items/Latest")
|
||||
| withParameter "api_key" $apiKey
|
||||
| withParameter "Limit" $itemCount
|
||||
| withParameter "ParentId" $libraryID
|
||||
| withParameter "IncludeItemTypes" $mediaTypes
|
||||
| withParameter "GroupItems" "true"
|
||||
| withHeader "Accept" "application/json"
|
||||
| getResponse }}
|
||||
{{ $items = $latestCall.JSON.Array "" }}
|
||||
{{ end }}
|
||||
|
||||
{{ else if eq $mode "nextup" }}
|
||||
|
||||
{{/* Fetch next up items */}}
|
||||
{{ $nextUpCall := newRequest (print $baseURL "/Shows/NextUp")
|
||||
| withParameter "api_key" $apiKey
|
||||
| withParameter "UserId" $userID
|
||||
| withParameter "Limit" $itemCount
|
||||
| withParameter "EnableResumable" "true"
|
||||
| withHeader "Accept" "application/json"
|
||||
| getResponse }}
|
||||
{{ $items = $nextUpCall.JSON.Array "Items" }}
|
||||
|
||||
{{ else }}
|
||||
{{ template "errorMsg" "Unknown mode, expected 'latest' or 'nextup'" }}
|
||||
{{ end }}
|
||||
|
||||
{{ if eq (len $items) 0 }}
|
||||
<p>No items found, start streaming something!</p>
|
||||
{{ else }}
|
||||
|
||||
{{/* Display the item carousel */}}
|
||||
<div class="carousel-container show-right-cutoff">
|
||||
<div class="cards-horizontal carousel-items-container">
|
||||
{{ range $n, $item := $items }}
|
||||
{{/* Common item variables */}}
|
||||
{{ $mediaType := $item.String "Type" }}
|
||||
{{ $title := $item.String "Name" }}
|
||||
{{ $itemID := $item.String "Id" }}
|
||||
|
||||
{{/* Media type specific variables */}}
|
||||
{{ $seriesTitle := "" }}
|
||||
{{ $artist := "" }}
|
||||
{{ $seriesID := "" }}
|
||||
{{ $season := "" }}
|
||||
{{ $episode := "" }}
|
||||
{{ $playPercentage := "" }}
|
||||
{{ $unwatchedEpisodeCount := "" }}
|
||||
|
||||
{{ if eq $mediaType "Movie" }}
|
||||
{{ else if eq $mediaType "Series" }}
|
||||
{{ $unwatchedEpisodeCount = $item.Int "UserData.UnplayedItemCount" }}
|
||||
{{ else if eq $mediaType "Episode" }}
|
||||
{{ $unwatchedEpisodeCount = 1 }}
|
||||
{{ $seriesTitle = $item.String "SeriesName" }}
|
||||
{{ $seriesID = $item.String "SeriesId" }}
|
||||
{{ $season = $item.Int "ParentIndexNumber" }}
|
||||
{{ $episode = $item.Int "IndexNumber" }}
|
||||
|
||||
{{ if $item.Exists "UserData.PlayedPercentage" }}
|
||||
{{ $playPercentage = $item.String "UserData.PlayedPercentage" }}
|
||||
{{ end }}
|
||||
|
||||
{{/* For latest always refer to the series not individual episodes */}}
|
||||
{{ if eq $mode "latest" }}
|
||||
{{ $itemID = $seriesID }}
|
||||
{{ $title = $seriesTitle }}
|
||||
{{ end }}
|
||||
{{ else if eq $mediaType "MusicAlbum" }}
|
||||
{{ $artist = $item.String "AlbumArtist" }}
|
||||
{{ end }}
|
||||
|
||||
{{ $linkURL := print $baseURL "/web/#/details?id=" $itemID }}
|
||||
{{ $thumbURL := "" }}
|
||||
{{ if not (eq $playPercentage "") }}
|
||||
{{/* $thumbURL = concat $baseURL "/Items/" $itemID "/Images/Primary?api_key=" $apiKey "&percentPlayed=" $playPercentage */}}
|
||||
{{ $thumbURL = concat $baseURL "/Items/" $itemID "/Images/Primary?api_key=" $apiKey }}
|
||||
{{ else }}
|
||||
{{ $thumbURL = concat $baseURL "/Items/" $itemID "/Images/Primary?api_key=" $apiKey }}
|
||||
{{ end }}
|
||||
|
||||
<a class="card widget-content-frame" href="{{ $linkURL | safeURL }}">
|
||||
{{ if $showThumbnail }}
|
||||
<div style="position: relative;">
|
||||
<img src="{{ $thumbURL | safeURL }}"
|
||||
alt="{{ $title }} thumbnail"
|
||||
loading="lazy"
|
||||
class="media-server-thumbnail shrink-0"
|
||||
style="
|
||||
object-fit: fill;
|
||||
border-radius: var(--border-radius) var(--border-radius) 0 0;
|
||||
width: 100%;
|
||||
display: block;
|
||||
{{ if eq $thumbAspectRatio "square" }}aspect-ratio: 1;
|
||||
{{ else if eq $thumbAspectRatio "portrait" }}aspect-ratio: 2/3;
|
||||
{{ else if eq $thumbAspectRatio "landscape" }}aspect-ratio: 16/9;
|
||||
{{ else }}aspect-ratio: initial;
|
||||
{{ end }}
|
||||
"
|
||||
/>
|
||||
|
||||
{{ if and ($showProgressBar) (not (eq $playPercentage "")) }}
|
||||
<div style="
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
height: 6px;
|
||||
border-radius: var(--border-radius);
|
||||
overflow: hidden;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
">
|
||||
<div style="
|
||||
width: {{ print $playPercentage "%" }};
|
||||
height: 100%;
|
||||
border-radius: var(--border-radius) 0 0 var(--border-radius);
|
||||
background-color: var(--color-primary)
|
||||
"></div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ 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 eq $mode "latest" }}
|
||||
{{ if or (eq $mediaType "Series") (eq $mediaType "Episode") }}
|
||||
<ul class="list-horizontal-text flex-nowrap">
|
||||
<li class="color-primary shrink-0">{{ $unwatchedEpisodeCount }}</li>
|
||||
<li class="text-truncate">{{ $title }}</li>
|
||||
</ul>
|
||||
{{ else if eq $mediaType "MusicAlbum" }}
|
||||
<ul class="list-horizontal-text flex-nowrap">
|
||||
<li class="color-primary text-truncate">{{ $artist }}</li>
|
||||
<li class="text-truncate">{{ $title }}</li>
|
||||
</ul>
|
||||
{{ else }}
|
||||
<li class="text-truncate">{{ $title }}</li>
|
||||
{{ end }}
|
||||
{{ else if eq $mode "nextup" }}
|
||||
<ul class="list-horizontal-text flex-nowrap">
|
||||
<li class="color-primary shrink-0">S{{ $season }}E{{ $episode }}</li>
|
||||
<li class="text-truncate">{{ $seriesTitle }}</li>
|
||||
</ul>
|
||||
<li class="text-truncate">{{ $title }}</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</div>
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
43
servers/eisen/glance/jellyfin-stats
Normal file
43
servers/eisen/glance/jellyfin-stats
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
{{ $url := .Options.StringOr "url" "" }}
|
||||
{{ $key := .Options.StringOr "key" "" }}
|
||||
|
||||
{{- if or (eq $url "") (eq $key "") -}}
|
||||
|
||||
<p>Error: The URL or API Key was not configured in the widget options.</p>
|
||||
|
||||
{{- else -}}
|
||||
|
||||
{{- $requestUrl := printf "%s/emby/Items/Counts?api_key=%s" $url $key -}}
|
||||
{{- $jellyfinData := newRequest $requestUrl | getResponse -}}
|
||||
|
||||
{{- if eq $jellyfinData.Response.StatusCode 200 -}}
|
||||
<div class="flex flex-column gap-5">
|
||||
<div class="flex justify-between text-center">
|
||||
|
||||
<div>
|
||||
<div class="color-highlight size-h3">{{ $jellyfinData.JSON.Int "MovieCount" | formatNumber }}</div>
|
||||
<div class="size-h5 uppercase">Movies</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="color-highlight size-h3">{{ $jellyfinData.JSON.Int "SeriesCount" | formatNumber }}</div>
|
||||
<div class="size-h5 uppercase">TV Shows</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="color-highlight size-h3">{{ $jellyfinData.JSON.Int "EpisodeCount" | formatNumber }}</div>
|
||||
<div class="size-h5 uppercase">Episodes</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="color-highlight size-h3">{{ $jellyfinData.JSON.Int "SongCount" | formatNumber }}</div>
|
||||
<div class="size-h5 uppercase">Songs</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{{- else -}}
|
||||
<p>Failed: {{ $jellyfinData.Response.Status }}</p>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
106
servers/eisen/glance/tailscale
Normal file
106
servers/eisen/glance/tailscale
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
<style>
|
||||
.device-info-container-tailscale {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
.device-info-tailscale {
|
||||
display: flex;
|
||||
transition: transform 0.2s ease, opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.device-ip-tailscale {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform: translateY(-100%);
|
||||
opacity: 0;
|
||||
transition: transform 0.2s ease, opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.device-info-container-tailscale:hover .device-info-tailscale {
|
||||
transform: translateY(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.device-info-container-tailscale:hover .device-ip-tailscale {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.update-indicator-tailscale {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--color-primary);
|
||||
display: inline-block;
|
||||
margin-left: 4px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.offline-indicator-tailscale {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--color-negative);
|
||||
display: inline-block;
|
||||
margin-left: 4px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.device-name-container-tailscale {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.indicators-container-tailscale {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
</style>
|
||||
<ul class="list list-gap-10 collapsible-container" data-collapse-after="{{ .Options.IntOr "collapseAfter" 3 }}">
|
||||
{{ range .JSON.Array "devices" }}
|
||||
<li>
|
||||
<div class="flex items-center gap-10">
|
||||
<div class="device-name-container-tailscale grow">
|
||||
<span class="size-h4 block text-truncate color-primary">
|
||||
{{ findMatch "^([^.]+)" (.String "name") }}
|
||||
</span>
|
||||
<div class="indicators-container-tailscale">
|
||||
{{ if and (not ($.Options.BoolOr "disableUpdateIndicator" false)) (.Bool "updateAvailable") }}
|
||||
<span class="update-indicator-tailscale" data-popover-type="text" data-popover-text="Update Available"></span>
|
||||
{{ end }}
|
||||
|
||||
{{ if not ($.Options.BoolOr "disableOfflineIndicator" false) }}
|
||||
{{ $lastSeen := .String "lastSeen" | parseTime "rfc3339" }}
|
||||
{{ if not ($lastSeen.After (offsetNow "-10s")) }}
|
||||
{{ $lastSeenTimezoned := $lastSeen.In now.Location }}
|
||||
<span class="offline-indicator-tailscale" data-popover-type="text"
|
||||
data-popover-text="Offline - Last seen {{ $lastSeenTimezoned.Format " Jan 2 3:04pm" }}"></span>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="device-info-container-tailscale">
|
||||
<ul class="list-horizontal-text device-info-tailscale">
|
||||
<li>{{ .String "os" }}</li>
|
||||
<li>
|
||||
{{ if and ($.Options.BoolOr "prioritiseTags" false) (.Exists "tags.0") }}
|
||||
{{ trimPrefix "tag:" (.String "tags.0") }}
|
||||
{{ else }}
|
||||
{{ .String "user" }}
|
||||
{{ end }}
|
||||
</li>
|
||||
</ul>
|
||||
<div class="device-ip-tailscale">
|
||||
{{ .String "addresses.0"}}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
54
servers/eisen/glance/uptime-kuma
Normal file
54
servers/eisen/glance/uptime-kuma
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
|
||||
{{ $hb := .Subrequest "heartbeats" }}
|
||||
|
||||
{{ if not (.JSON.Exists "publicGroupList") }}
|
||||
<p class="color-negative">Error reading response</p>
|
||||
{{ else if eq (len (.JSON.Array "publicGroupList")) 0 }}
|
||||
<p>No monitors found</p>
|
||||
{{ else }}
|
||||
|
||||
<ul class="dynamic-columns list-gap-8">
|
||||
{{ range .JSON.Array "publicGroupList" }}
|
||||
{{ range .Array "monitorList" }}
|
||||
{{ $id := .String "id" }}
|
||||
{{ $hbArray := $hb.JSON.Array (print "heartbeatList." $id) }}
|
||||
<div class="flex items-center gap-12">
|
||||
<a class="size-title-dynamic color-highlight text-truncate block grow" href="http://status.eisen/dashboard/{{ $id }}"
|
||||
target="_blank" rel="noreferrer">
|
||||
{{ .String "name" }} </a>
|
||||
|
||||
{{ if gt (len $hbArray) 0 }}
|
||||
{{ $latest := index $hbArray (sub (len $hbArray) 1) }}
|
||||
{{ if eq ($latest.Int "status") 1 }}
|
||||
<div>{{ $latest.Int "ping" }}ms</div>
|
||||
<div class="monitor-site-status-icon-compact" title="OK">
|
||||
<svg fill="var(--color-positive)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm3.857-9.809a.75.75 0 0 0-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 1 0-1.06 1.061l2.5 2.5a.75.75 0 0 0 1.137-.089l4-5.5Z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
{{ else }}
|
||||
<div><span class="color-negative">DOWN</span></div>
|
||||
<div class="monitor-site-status-icon-compact" title="{{ if $latest.Exists "msg" }}{{ $latest.String "msg" }}{{ else
|
||||
}}Error{{ end }}">
|
||||
<svg fill="var(--color-negative)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd"
|
||||
d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495ZM10 5a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 10 5Zm0 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<div><span class="color-negative">No data</span></div>
|
||||
<div class="monitor-site-status-icon-compact" title="No data available">
|
||||
<svg fill="var(--color-negative)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path d="M10 18a8 8 0 1 1 0-16 8 8 0 0 1 0 16zm0-2a6 6 0 1 0 0-12 6 6 0 0 0 0 12zm-.75-8a.75.75 0 0 1 1.5 0v3a.75.75 0 0 1-1.5 0V8zm.75 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ end }}
|
||||
|
|
@ -71,6 +71,7 @@
|
|||
nvitop
|
||||
basalt-monado
|
||||
cudaPackages.cuda_nvcc
|
||||
# llama-cpp
|
||||
(llama-cpp.overrideAttrs (prevAttrs: {
|
||||
cmakeFlags = with lib; [
|
||||
# -march=native is non-deterministic; override with platform-specific flags if needed
|
||||
|
|
@ -80,18 +81,18 @@
|
|||
(cmakeBool "LLAMA_BUILD_TESTS" false)
|
||||
(cmakeBool "LLAMA_OPENSSL" true)
|
||||
(cmakeBool "BUILD_SHARED_LIBS" true)
|
||||
(cmakeBool "GGML_BLAS" false)
|
||||
# (cmakeBool "GGML_BLAS" false)
|
||||
(cmakeBool "GGML_LTO" true)
|
||||
(cmakeBool "GGML_CLBLAST" true)
|
||||
(cmakeBool "GGML_CUDA" true)
|
||||
(cmakeBool "GGML_CUDA_GRAPHS" true)
|
||||
(cmakeBool "GGML_CUDA_F16" true)
|
||||
(cmakeBool "GGML_CUDA_FA_ALL_QUANTS" true)
|
||||
(cmakeBool "GGML_HIP" false)
|
||||
(cmakeBool "GGML_METAL" false)
|
||||
(cmakeBool "GGML_RPC" false)
|
||||
(cmakeBool "GGML_VULKAN" false)
|
||||
(cmakeFeature "LLAMA_BUILD_NUMBER" "8770")
|
||||
# (cmakeBool "GGML_HIP" false)
|
||||
# (cmakeBool "GGML_METAL" false)
|
||||
# (cmakeBool "GGML_RPC" false)
|
||||
# (cmakeBool "GGML_VULKAN" false)
|
||||
(cmakeFeature "LLAMA_BUILD_NUMBER" "8667")
|
||||
(cmakeFeature "CMAKE_CUDA_ARCHITECTURES" "120")
|
||||
];
|
||||
}))
|
||||
|
|
|
|||
Loading…
Reference in a new issue