commit 73abb199ed4c5ea8559ff67d55bd5cdaa38e9aaa Author: ayrisdev Date: Mon Feb 16 00:41:55 2026 +0300 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/assets/global.css b/assets/global.css new file mode 100644 index 0000000..6212406 --- /dev/null +++ b/assets/global.css @@ -0,0 +1,48 @@ +@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&display=swap'); + +:root { + --color-info: hsl(43, 50%, 70%); + --color-warning: hsl(43, 50%, 70%); + /* Widget background color with alpha */ + --bga: 70%; + --color-widget-background: hsl( + var(--color-widget-background-hsl-values), + var(--bga) + ); +} + +* { + box-sizing: border-box; +} +body { + font-family: 'Outfit', sans-serif; + background-image: url(https://images.pexels.com/photos/2098428/pexels-photo-2098428.jpeg); + /* backdrop-filter: blur(2px); */ + background-repeat: no-repeat; + background-size: cover; +} +/* Blur effect for widget backgrounds */ +.widget-content:not(.widget-content-frameless), +.widget-content-frame { + backdrop-filter: blur(5px); + box-shadow: + rgba(0, 0, 0, 0.02) 0px 1px 3px 0px, + rgba(27, 31, 35, 0.15) 0px 0px 0px 1px; +} + +/* Make widgets same height */ +.widget-type-dns-stats { + height: 100%; +} +.widget-type-dns-stats .widget-content { + height: calc(100% - var(--widget-gap)); +} +.widget-type-dns-stats .widget-content .dns-stats { + height: 100%; + display: flex; + flex-direction: column; + justify-content: space-around; +} +.widget-type-prowlarr-custom .widget-content { + height: calc(100% - var(--widget-gap)); +} diff --git a/assets/images/cloudflare.png b/assets/images/cloudflare.png new file mode 100644 index 0000000..ddbaba1 Binary files /dev/null and b/assets/images/cloudflare.png differ diff --git a/config/bookmarks.yml b/config/bookmarks.yml new file mode 100644 index 0000000..b48a955 --- /dev/null +++ b/config/bookmarks.yml @@ -0,0 +1,33 @@ +- type: bookmarks + groups: + - links: + - title: Gmail + url: https://mail.google.com/mail/u/0/ + - title: Amazon + url: https://www.amazon.com/ + - title: Github + url: https://github.com/ + - title: Wikipedia + url: https://en.wikipedia.org/ + - title: Entertainment + color: 10 70 50 + links: + - title: Netflix + url: https://www.netflix.com/ + - title: Disney+ + url: https://www.disneyplus.com/ + - title: YouTube + url: https://www.youtube.com/ + - title: Prime Video + url: https://www.primevideo.com/ + - title: Social + color: 200 50 50 + links: + - title: Reddit + url: https://www.reddit.com/ + - title: Twitter + url: https://twitter.com/ + - title: Instagram + url: https://www.instagram.com/ + + diff --git a/config/glance.yml b/config/glance.yml new file mode 100644 index 0000000..e82187f --- /dev/null +++ b/config/glance.yml @@ -0,0 +1,78 @@ +server: + assets-path: /app/assets + +branding: + hide-footer: true + +theme: + background-color: 203 27 9 + contrast-multiplier: 1.2 + primary-color: 165 78 51 + positive-color: 165 78 51 + negative-color: 360 100 71 + disable-picker: true + custom-css-file: /global.css + +pages: + - name: Home + # Optionally, if you only have a single page you can hide the desktop navigation for a cleaner look + hide-desktop-navigation: true + columns: + - size: small + widgets: + - type: calendar + hide-header: true + first-day-of-week: sunday + + - $include: bookmarks.yml + + + + + + - size: full + widgets: + - type: split-column + max-columns: 4 + widgets: + - $include: plausible.yml + css-class: widget-type-plausible-prowlarr-custom + - $include: uptime.yml + css-class: widget-type-uptime-custom + - $include: services.yml + - $include: raindrop.yml + css-class: widget-type-raindrop-custom + + + - size: small + widgets: + - type: clock + hide-header: true + time-format: 24h + date-format: d MMMM yyyy + show-seconds: true + show-timezone: true + timezone: Europe/Istanbul + + - type: weather + hide-header: true + location: ${WEATHER_LOCATION} + units: metric + hour-format: 24h + + + + # Add more pages here: + # - name: Your page name + # columns: + # - size: small + # widgets: + # # Add widgets here + + # - size: full + # widgets: + # # Add widgets here + + # - size: small + # widgets: + # # Add widgets here diff --git a/config/media-server.yml b/config/media-server.yml new file mode 100644 index 0000000..f35dcd2 --- /dev/null +++ b/config/media-server.yml @@ -0,0 +1,343 @@ +- type: custom-api + title: Media Server History + frameless: true + cache: 5m + options: + media-server: '${MEDIA_SERVER_TYPE}' + base-url: ${JELLYFIN_URL} + api-key: ${JELLYFIN_TOKEN} + user-name: ${JELLYFIN_USER} + media-types: '${MEDIA_SERVER_TYPES}' + history-length: '${MEDIA_SERVER_HISTORY_LENGTH}' + small-column: true + compact: true + show-thumbnail: true + thumbnail-aspect-ratio: 'default' + show-user: false + time-absolute: false + time-format: 'Jan 02 15:04' + template: | + {{ $mediaServer := .Options.StringOr "media-server" "" }} + {{ $baseURL := .Options.StringOr "base-url" "" }} + {{ $apiKey := .Options.StringOr "api-key" "" }} + {{ $userName := .Options.StringOr "user-name" "" }} + + {{ define "errorMsg" }} +
+
ERROR
+ + + +
+

{{ . }}

+ {{ 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) }} +

Nothing has been played. Start streaming something!

+ {{ else }} + + {{ end }} + {{ end }} diff --git a/config/plausible.yml b/config/plausible.yml new file mode 100644 index 0000000..6919149 --- /dev/null +++ b/config/plausible.yml @@ -0,0 +1,260 @@ +- type: custom-api + title: Site Statistics + hide-header: true + cache: 1m + subrequests: + # aveminakarabudak.com + a1: + url: ${PLAUSIBLE_BASE_URL}/api/v1/stats/realtime/visitors?site_id=aveminakarabudak.com + headers: { Authorization: "Bearer ${PLAUSIBLE_TOKEN}" } + s1: + url: ${PLAUSIBLE_BASE_URL}/api/v1/stats/aggregate?site_id=aveminakarabudak.com&period=day&metrics=visitors,pageviews,visits + headers: { Authorization: "Bearer ${PLAUSIBLE_TOKEN}" } + # ayrisapart.com + a2: + url: ${PLAUSIBLE_BASE_URL}/api/v1/stats/realtime/visitors?site_id=ayrisapart.com + headers: { Authorization: "Bearer ${PLAUSIBLE_TOKEN}" } + s2: + url: ${PLAUSIBLE_BASE_URL}/api/v1/stats/aggregate?site_id=ayrisapart.com&period=day&metrics=visitors,pageviews,visits + headers: { Authorization: "Bearer ${PLAUSIBLE_TOKEN}" } + # ayris.tech + a3: + url: ${PLAUSIBLE_BASE_URL}/api/v1/stats/realtime/visitors?site_id=ayris.tech + headers: { Authorization: "Bearer ${PLAUSIBLE_TOKEN}" } + s3: + url: ${PLAUSIBLE_BASE_URL}/api/v1/stats/aggregate?site_id=ayris.tech&period=day&metrics=visitors,pageviews,visits + headers: { Authorization: "Bearer ${PLAUSIBLE_TOKEN}" } + # irisiptv.online + a4: + url: ${PLAUSIBLE_BASE_URL}/api/v1/stats/realtime/visitors?site_id=irisiptv.online + headers: { Authorization: "Bearer ${PLAUSIBLE_TOKEN}" } + s4: + url: ${PLAUSIBLE_BASE_URL}/api/v1/stats/aggregate?site_id=irisiptv.online&period=day&metrics=visitors,pageviews,visits + headers: { Authorization: "Bearer ${PLAUSIBLE_TOKEN}" } + # screencapr.com + a5: + url: ${PLAUSIBLE_BASE_URL}/api/v1/stats/realtime/visitors?site_id=screencapr.com + headers: { Authorization: "Bearer ${PLAUSIBLE_TOKEN}" } + s5: + url: ${PLAUSIBLE_BASE_URL}/api/v1/stats/aggregate?site_id=screencapr.com&period=day&metrics=visitors,pageviews,visits + headers: { Authorization: "Bearer ${PLAUSIBLE_TOKEN}" } + template: | +
+ + + {{ $sites := . }} + + + {{ $a1 := .Subrequest "a1" }} + {{ $s1 := .Subrequest "s1" }} +
+
+ +
+
+ aveminakarabudak.com +
+ {{ if eq $s1.Response.StatusCode 200 }} + Visitors: {{ $s1.JSON.Get "results.visitors.value" }} + Views: {{ $s1.JSON.Get "results.pageviews.value" }} + {{ end }} +
+
+
+ {{ if eq $a1.Response.StatusCode 200 }} + {{ $c1 := $a1.JSON.Raw }} + {{ if ne $c1 "0" }} +
+ + {{ $c1 }} Active +
+ {{ end }} + {{ end }} +
+
+ + + {{ $a2 := .Subrequest "a2" }} + {{ $s2 := .Subrequest "s2" }} +
+
+ +
+
+ ayrisapart.com +
+ {{ if eq $s2.Response.StatusCode 200 }} + Visitors: {{ $s2.JSON.Get "results.visitors.value" }} + Views: {{ $s2.JSON.Get "results.pageviews.value" }} + {{ end }} +
+
+
+ {{ if eq $a2.Response.StatusCode 200 }} + {{ $c2 := $a2.JSON.Raw }} + {{ if ne $c2 "0" }} +
+ + {{ $c2 }} Active +
+ {{ end }} + {{ end }} +
+
+ + + {{ $a3 := .Subrequest "a3" }} + {{ $s3 := .Subrequest "s3" }} +
+
+ +
+
+ ayris.tech +
+ {{ if eq $s3.Response.StatusCode 200 }} + Visitors: {{ $s3.JSON.Get "results.visitors.value" }} + Views: {{ $s3.JSON.Get "results.pageviews.value" }} + {{ end }} +
+
+
+ {{ if eq $a3.Response.StatusCode 200 }} + {{ $c3 := $a3.JSON.Raw }} + {{ if ne $c3 "0" }} +
+ + {{ $c3 }} Active +
+ {{ end }} + {{ end }} +
+
+ + + {{ $a4 := .Subrequest "a4" }} + {{ $s4 := .Subrequest "s4" }} +
+
+ +
+
+ irisiptv.online +
+ {{ if eq $s4.Response.StatusCode 200 }} + Visitors: {{ $s4.JSON.Get "results.visitors.value" }} + Views: {{ $s4.JSON.Get "results.pageviews.value" }} + {{ end }} +
+
+
+ {{ if eq $a4.Response.StatusCode 200 }} + {{ $c4 := $a4.JSON.Raw }} + {{ if ne $c4 "0" }} +
+ + {{ $c4 }} Active +
+ {{ end }} + {{ end }} +
+
+ + + {{ $a5 := .Subrequest "a5" }} + {{ $s5 := .Subrequest "s5" }} +
+
+ +
+
+ screencapr.com +
+ {{ if eq $s5.Response.StatusCode 200 }} + Visitors: {{ $s5.JSON.Get "results.visitors.value" }} + Views: {{ $s5.JSON.Get "results.pageviews.value" }} + {{ end }} +
+
+
+ {{ if eq $a5.Response.StatusCode 200 }} + {{ $c5 := $a5.JSON.Raw }} + {{ if ne $c5 "0" }} +
+ + {{ $c5 }} Active +
+ {{ end }} + {{ end }} +
+
+
diff --git a/config/raindrop.yml b/config/raindrop.yml new file mode 100644 index 0000000..0f2ebf1 --- /dev/null +++ b/config/raindrop.yml @@ -0,0 +1,103 @@ +- type: custom-api + title: Raindrop Latest Links + hide-header: true + cache: 1h + url: https://api.raindrop.io/rest/v1/raindrops/0?perpage=50 + headers: { Authorization: "Bearer ${RAINDROP_TOKEN}" } + template: | + {{ if .JSON.Bool "result" }} +
+ + + +
+ {{ else }} +
+ No bookmarks found or API error. +
+ {{ end }} \ No newline at end of file diff --git a/config/services.yml b/config/services.yml new file mode 100644 index 0000000..43d251d --- /dev/null +++ b/config/services.yml @@ -0,0 +1,51 @@ +- type: custom-api + title: Services + hide-header: true + cache: 1m + url: ${SERVICES_JSON_URL} + template: | +
+ + + {{ range .JSON.Raw | fromJson }} + +
+ +
+ {{ .title }} +
+ {{ end }} +
\ No newline at end of file diff --git a/config/uptime.yml b/config/uptime.yml new file mode 100644 index 0000000..8610e58 --- /dev/null +++ b/config/uptime.yml @@ -0,0 +1,139 @@ +- type: custom-api + title: Uptime Status + title-url: ${UPTIME_KUMA_URL} + hide-header: true + url: ${UPTIME_KUMA_URL}/api/status-page/${UPTIME_KUMA_STATUS_SLUG} + subrequests: + heartbeats: + url: ${UPTIME_KUMA_URL}/api/status-page/heartbeat/${UPTIME_KUMA_STATUS_SLUG} + cache: 5m + template: | + {{ $hb := .Subrequest "heartbeats" }} + + {{ if not (.JSON.Exists "publicGroupList") }} +

Error reading response

+ {{ else if eq (len (.JSON.Array "publicGroupList")) 0 }} +

No monitors found

+ {{ else }} +
+ + + {{ range .JSON.Array "publicGroupList" }} + {{ range .Array "monitorList" }} + {{ $id := .String "id" }} + {{ $name := .String "name" }} + {{ $hbPath := concat "heartbeatList." $id }} + {{ $hbArray := $hb.JSON.Array $hbPath }} + + {{ $latest := "" }} + {{ range $hbArray }}{{ $latest = . }}{{ end }} + +
+
+ {{ if eq $name "AvEmy" }} + {{ else if eq $name "ayris.tech" }} + {{ else if eq $name "AyrisApart" }} + {{ else if eq $name "irisiptv.online" }} + {{ else if eq $name "screencapr.com" }} + {{ else if eq $name "Portainer" }} + {{ else if eq $name "Backrest" }} + {{ else }} + + {{ end }} +
+ +
+ + {{ $name }} + +
+ {{ if $latest }} + {{ if eq ($latest.String "status") "1" }} + OK + + {{ $latest.Int "ping" }}ms + {{ else }} + DOWN + {{ if $latest.Exists "msg" }} + + {{ $latest.String "msg" }} + {{ end }} + {{ end }} + {{ else }} + No data + {{ end }} +
+
+ +
+ {{ if $latest }} + {{ if eq ($latest.String "status") "1" }} + + + + {{ else }} + + + + {{ end }} + {{ else }} + + + + {{ end }} +
+
+ {{ end }} + {{ end }} +
+ {{ end }} diff --git a/services.json b/services.json new file mode 100644 index 0000000..4793dc6 --- /dev/null +++ b/services.json @@ -0,0 +1,32 @@ +[ + { + "title": "Dashboard", + "url": "https://dash.ayris.tech", + "icon": "https://raw.githubusercontent.com/glance-project/glance/main/assets/logo.png" + }, + { + "title": "Mail", + "url": "https://mail.ayris.tech", + "icon": "https://stalwart.io/img/logo.png" + }, + { + "title": "n8n Automation", + "url": "https://auto.ayris.tech", + "icon": "https://n8n.io/images/press/n8n-logo.png" + }, + { + "title": "Vaultwarden", + "url": "https://vault.ayris.tech", + "icon": "https://raw.githubusercontent.com/dani-garcia/vaultwarden/main/resources/vaultwarden-icon.png" + }, + { + "title": "Analytics", + "url": "https://analytics.ayris.tech", + "icon": "https://plausible.io/assets/images/icon/plausible_logo.png" + }, + { + "title": "Uptime", + "url": "http://uptimekuma-uko00s44cs8cokos4cgwk8oc.65.109.236.58.sslip.io/dashboard", + "icon": "https://uptime.kuma.pet/img/icon.png" + } +] \ No newline at end of file