Compare commits

..
28 Commits
Author SHA1 Message Date
Svilen Markov 85b8ee493e Update dependencies
Create release / release (push) Has been cancelled
2024-12-26 13:24:40 +00:00
Svilen Markov e546488aeb Fix broken reddit video & nsfw post thumbnails 2024-12-26 13:20:03 +00:00
Svilen Markov f18620d890 Add note about extension URL query parameters 2024-12-22 14:32:56 +00:00
Svilen Markov 24f93d8258 Merge pull request #275 from glanceapp/release/v0.6.3
Create release / release (push) Has been cancelled
Release/v0.6.3
2024-12-11 18:56:48 +00:00
Svilen Markov 01016e08d2 Merge branch 'main' into release/v0.6.3 2024-12-11 18:53:40 +00:00
Svilen Markov 949fde1517 Fix edge case in weather widget graph 2024-12-11 18:50:34 +00:00
Svilen Markov 804cf9916b Change simple icons provider and always use latest version 2024-12-11 18:47:30 +00:00
Svilen Markov ef24680baf Fix overlap issues in weather and dns-stats widgets 2024-12-11 18:38:09 +00:00
Svilen Markov 448c795982 Properly close code block in docs 2024-12-07 16:47:58 +00:00
Svilen Markov ee1d7599d5 Add note to preconfigured pages doc 2024-12-07 09:28:36 +00:00
Svilen Markov d90d39933a Merge pull request #228 from Dakhtara/patch-1
Fix typo on configuration for search autofocus property description
2024-10-01 15:28:42 +01:00
Anthony 7e38ab624a Update configuration.md 2024-10-01 08:59:11 +02:00
Svilen Markov 9a36187333 Add /api/healthz endpoint
Create release / release (push) Has been cancelled
2024-09-30 00:42:19 +01:00
Svilen Markov b0c4d70628 Fix bug with collapsible grids inside of group widget
Also move utils to new file
2024-09-30 00:42:19 +01:00
Svilen Markov d5fa6424c1 Fix bookmark icons shrinking when text wraps 2024-09-30 00:42:19 +01:00
Svilen Markov 07fe5b3cb1 Merge pull request #224 from SimJunYou/piholePrivacy
DNS Stats widget - fix Pihole privacy edge case
2024-09-30 00:31:00 +01:00
Svilen Markov 672547cd07 Switch to using custom type for pihole's top blocked domains 2024-09-30 00:24:44 +01:00
SimJunYou 28167403a4 Cover edge case when Pihole's privacy settings are enabled 2024-09-19 17:43:43 +08:00
Svilen Markov eaf08d42dd Merge pull request #220 from paricbat/main
Add important details about the server.base-url property
2024-09-16 08:35:33 +01:00
Veronika Bušová bae95a5e07 Add important details about the server.base-url property 2024-09-15 00:29:46 +02:00
Svilen Markov db1ed9e257 Fix & refactor adguard stats
Create release / release (push) Has been cancelled
2024-09-10 05:38:14 +01:00
Svilen Markov adef35049f Add warning when pihole doesn't return expected data points 2024-09-10 05:38:14 +01:00
Svilen Markov bd9abf2e52 Fix mobile nav links wrapping 2024-09-10 05:38:14 +01:00
Svilen Markov aa74c00fd4 Merge pull request #210 from micash545/main
releases: Add support for Codeberg
2024-09-08 19:36:20 +01:00
Svilen Markov a68a907fa7 Update SVG 2024-09-08 19:35:01 +01:00
Svilen Markov 1a05d6d03a Don't export function 2024-09-08 19:34:43 +01:00
micash 60f4183057 add codeberg releases 2024-09-08 16:59:24 +02:00
Svilen Markov 04c3afc850 Document center-vertically property 2024-09-08 06:25:06 +01:00
17 changed files with 201 additions and 73 deletions
+15 -5
View File
@@ -138,6 +138,10 @@ A number between 1 and 65,535, so long as that port isn't already used by anythi
#### `base-url`
The base URL that Glance is hosted under. No need to specify this unless you're using a reverse proxy and are hosting Glance under a directory. If that's the case then you can set this value to `/glance` or whatever the directory is called. Note that the forward slash (`/`) in the beginning is required unless you specify the full domain and path.
> [!IMPORTANT]
> You need to strip the `base-url` prefix before forwarding the request to the Glance server.
> In Caddy you can do this using [`handle_path`](https://caddyserver.com/docs/caddyfile/directives/handle_path) or [`uri strip_prefix`](https://caddyserver.com/docs/caddyfile/directives/uri).
#### `assets-path`
The path to a directory that will be served by the server under the `/assets/` path. This is handy for widgets like the Monitor where you have to specify an icon URL and you want to self host all the icons rather than pointing to an external source.
@@ -277,6 +281,7 @@ theme:
> .widget-type-rss a {
> font-size: 1.5rem;
> }
> ```
>
> In addition, you can also use the `css-class` property which is available on every widget to set custom class names for individual widgets.
@@ -307,6 +312,7 @@ pages:
| title | string | yes | |
| slug | string | no | |
| width | string | no | |
| center-vertically | boolean | no | false |
| hide-desktop-navigation | boolean | no | false |
| show-mobile-header | boolean | no | false |
| columns | array | yes | |
@@ -328,6 +334,8 @@ The maximum width of the page on desktop. Possible values are `slim` and `wide`.
>
> When using `slim`, the maximum number of columns allowed for that page is `2`.
#### `center-vertically`
When set to `true`, vertically centers the content on the page. Has no effect if the content is taller than the height of the viewport.
#### `hide-desktop-navigation`
Whether to show the navigation links at the top of the page on desktop.
@@ -845,7 +853,7 @@ Either a value from the table below or a URL to a custom search engine. Use `{QU
##### `new-tab`
When set to `true`, swaps the shortcuts for showing results in the same or new tab, defaulting to showing results in a new tab.
##### `new-tab`
##### `autofocus`
When set to `true`, automatically focuses the search input on page load.
##### `bangs`
@@ -945,7 +953,7 @@ Display a widget provided by an external source (3rd party). If you want to lear
| parameters | key & value | no | |
##### `url`
The URL of the extension.
The URL of the extension. **Note that the query gets stripped from this URL and the one defined by `parameters` gets used instead.**
##### `allow-potentially-dangerous-html`
Whether to allow the extension to display HTML.
@@ -1111,7 +1119,7 @@ Whether to ignore invalid/self-signed certificates.
Whether to open the link in the same or a new tab.
### Releases
Display a list of latest releases for specific repositories on Github, GitLab or Docker Hub.
Display a list of latest releases for specific repositories on Github, GitLab, Codeberg or Docker Hub.
Example:
@@ -1122,6 +1130,7 @@ Example:
- go-gitea/gitea
- jellyfin/jellyfin
- glanceapp/glance
- codeberg:redict/redict
- gitlab:fdroid/fdroidclient
- dockerhub:gotify/server
```
@@ -1142,12 +1151,13 @@ Preview:
| collapse-after | integer | no | 5 |
##### `repositories`
A list of repositores to fetch the latest release for. Only the name/repo is required, not the full URL. A prefix can be specified for repositories hosted elsewhere such as GitLab and Docker Hub. Example:
A list of repositores to fetch the latest release for. Only the name/repo is required, not the full URL. A prefix can be specified for repositories hosted elsewhere such as GitLab, Codeberg and Docker Hub. Example:
```yaml
repositories:
- gitlab:inkscape/inkscape
- dockerhub:glanceapp/glance
- codeberg:redict/redict
```
Official images on Docker Hub can be specified by ommiting the owner:
@@ -1169,7 +1179,7 @@ repositories:
##### `show-source-icon`
Shows an icon of the source (GitHub/GitLab/Docker Hub) next to the repository name when set to `true`.
Shows an icon of the source (GitHub/GitLab/Codeberg/Docker Hub) next to the repository name when set to `true`.
##### `token`
Without authentication Github allows for up to 60 requests per hour. You can easily exceed this limit and start seeing errors if you're tracking lots of repositories or your cache time is low. To circumvent this you can [create a read only token from your Github account](https://github.com/settings/personal-access-tokens/new) and provide it here.
+4
View File
@@ -4,6 +4,10 @@ Don't want to spend time configuring pages from scratch? No problem! Simply copy
Pull requests with your page configurations are welcome!
> [!NOTE]
>
> Pages must be placed under a top level `pages:` key, you can read more about that [here](configuration.md#pages).
## Startpage
![](images/startpage-preview.png)
+2 -2
View File
@@ -4,7 +4,7 @@ go 1.22.5
require (
github.com/mmcdole/gofeed v1.3.0
golang.org/x/text v0.16.0
golang.org/x/text v0.21.0
gopkg.in/yaml.v3 v3.0.1
)
@@ -15,5 +15,5 @@ require (
github.com/mmcdole/goxpp v1.1.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/net v0.33.0 // indirect
)
+4 -4
View File
@@ -33,8 +33,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -54,8 +54,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M11.955.49A12 12 0 0 0 0 12.49a12 12 0 0 0 1.832 6.373L11.838 5.928a.187.14 0 0 1 .324 0l10.006 12.935A12 12 0 0 0 24 12.49a12 12 0 0 0-12-12 12 12 0 0 0-.045 0zm.375 6.467l4.416 16.553a12 12 0 0 0 5.137-4.213z"/></svg>

After

Width:  |  Height:  |  Size: 300 B

+8 -29
View File
@@ -1,27 +1,5 @@
import { setupPopovers } from './popover.js';
function throttledDebounce(callback, maxDebounceTimes, debounceDelay) {
let debounceTimeout;
let timesDebounced = 0;
return function () {
if (timesDebounced == maxDebounceTimes) {
clearTimeout(debounceTimeout);
timesDebounced = 0;
callback();
return;
}
clearTimeout(debounceTimeout);
timesDebounced++;
debounceTimeout = setTimeout(() => {
timesDebounced = 0;
callback();
}, debounceDelay);
};
};
import { throttledDebounce, isElementVisible } from './utils.js';
async function fetchPageContent(pageData) {
// TODO: handle non 200 status codes/time outs
@@ -427,7 +405,7 @@ function setupCollapsibleGrids() {
const button = attachExpandToggleButton(gridElement);
let cardsPerRow = 2;
let cardsPerRow;
const resolveCollapsibleItems = () => {
const hideItemsAfterIndex = cardsPerRow * collapseAfterRows;
@@ -457,12 +435,11 @@ function setupCollapsibleGrids() {
}
};
afterContentReady(() => {
cardsPerRow = getCardsPerRow();
resolveCollapsibleItems();
});
const observer = new ResizeObserver(() => {
if (!isElementVisible(gridElement)) {
return;
}
window.addEventListener("resize", () => {
const newCardsPerRow = getCardsPerRow();
if (cardsPerRow == newCardsPerRow) {
@@ -472,6 +449,8 @@ function setupCollapsibleGrids() {
cardsPerRow = newCardsPerRow;
resolveCollapsibleItems();
});
afterContentReady(() => observer.observe(gridElement));
}
}
+25
View File
@@ -0,0 +1,25 @@
export function throttledDebounce(callback, maxDebounceTimes, debounceDelay) {
let debounceTimeout;
let timesDebounced = 0;
return function () {
if (timesDebounced == maxDebounceTimes) {
clearTimeout(debounceTimeout);
timesDebounced = 0;
callback();
return;
}
clearTimeout(debounceTimeout);
timesDebounced++;
debounceTimeout = setTimeout(() => {
timesDebounced = 0;
callback();
}, debounceDelay);
};
};
export function isElementVisible(element) {
return !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length);
}
+8 -3
View File
@@ -514,6 +514,7 @@ kbd:active {
list-style: none;
position: relative;
display: flex;
z-index: 1;
}
.details[open] .summary {
@@ -535,6 +536,10 @@ kbd:active {
opacity: 1;
}
.details:not([open]) .list-with-transition {
display: none;
}
.summary::after {
content: "◀";
font-size: 1.2em;
@@ -707,6 +712,7 @@ details[open] .summary::after {
justify-content: space-between;
position: relative;
margin-bottom: 1.8rem;
z-index: 1;
}
.widget-error-header::before {
@@ -851,6 +857,7 @@ details[open] .summary::after {
border-bottom: 2px solid transparent;
transition: color .3s, border-color .3s;
font-size: var(--font-size-h3);
flex-shrink: 0;
}
.nav-item:not(.nav-item-current):hover {
@@ -1049,6 +1056,7 @@ details[open] .summary::after {
border-radius: var(--border-radius);
padding: 0.5rem;
opacity: 0.7;
flex-shrink: 0;
}
.bookmarks-icon {
@@ -1093,7 +1101,6 @@ details[open] .summary::after {
.dns-stats-graph-gridlines-container {
position: absolute;
z-index: -1;
inset: 0;
}
@@ -1120,7 +1127,6 @@ details[open] .summary::after {
content: '';
position: absolute;
inset: 1px 0;
z-index: -1;
opacity: 0;
background: var(--color-text-base);
transition: opacity .2s;
@@ -1262,7 +1268,6 @@ details[open] .summary::after {
overflow: hidden;
mask-image: linear-gradient(0deg, transparent 40%, #000);
-webkit-mask-image: linear-gradient(0deg, transparent 40%, #000);
z-index: -1;
}
.weather-column-rain::before {
+39 -18
View File
@@ -31,10 +31,13 @@ func FetchAdguardStats(instanceURL, username, password string) (*DNSStats, error
return nil, err
}
var topBlockedDomainsCount = min(len(responseJson.TopBlockedDomains), 5)
stats := &DNSStats{
TotalQueries: responseJson.TotalQueries,
BlockedQueries: responseJson.BlockedQueries,
ResponseTime: int(responseJson.ResponseTime * 1000),
TotalQueries: responseJson.TotalQueries,
BlockedQueries: responseJson.BlockedQueries,
ResponseTime: int(responseJson.ResponseTime * 1000),
TopBlockedDomains: make([]DNSStatsBlockedDomain, 0, topBlockedDomainsCount),
}
if stats.TotalQueries <= 0 {
@@ -43,8 +46,6 @@ func FetchAdguardStats(instanceURL, username, password string) (*DNSStats, error
stats.BlockedPercent = int(float64(responseJson.BlockedQueries) / float64(responseJson.TotalQueries) * 100)
var topBlockedDomainsCount = min(len(responseJson.TopBlockedDomains), 5)
for i := 0; i < topBlockedDomainsCount; i++ {
domain := responseJson.TopBlockedDomains[i]
var firstDomain string
@@ -59,31 +60,51 @@ func FetchAdguardStats(instanceURL, username, password string) (*DNSStats, error
}
stats.TopBlockedDomains = append(stats.TopBlockedDomains, DNSStatsBlockedDomain{
Domain: firstDomain,
PercentBlocked: int(float64(domain[firstDomain]) / float64(responseJson.BlockedQueries) * 100),
Domain: firstDomain,
})
if stats.BlockedQueries > 0 {
stats.TopBlockedDomains[i].PercentBlocked = int(float64(domain[firstDomain]) / float64(responseJson.BlockedQueries) * 100)
}
}
// Adguard _should_ return data for the last 24 hours in a 1 hour interval
if len(responseJson.QueriesSeries) != 24 || len(responseJson.BlockedSeries) != 24 {
return stats, nil
queriesSeries := responseJson.QueriesSeries
blockedSeries := responseJson.BlockedSeries
const bars = 8
const hoursSpan = 24
const hoursPerBar int = hoursSpan / bars
if len(queriesSeries) > hoursSpan {
queriesSeries = queriesSeries[len(queriesSeries)-hoursSpan:]
} else if len(queriesSeries) < hoursSpan {
queriesSeries = append(make([]int, hoursSpan-len(queriesSeries)), queriesSeries...)
}
if len(blockedSeries) > hoursSpan {
blockedSeries = blockedSeries[len(blockedSeries)-hoursSpan:]
} else if len(blockedSeries) < hoursSpan {
blockedSeries = append(make([]int, hoursSpan-len(blockedSeries)), blockedSeries...)
}
maxQueriesInSeries := 0
for i := 0; i < 8; i++ {
for i := 0; i < bars; i++ {
queries := 0
blocked := 0
for j := 0; j < 3; j++ {
queries += responseJson.QueriesSeries[i*3+j]
blocked += responseJson.BlockedSeries[i*3+j]
for j := 0; j < hoursPerBar; j++ {
queries += queriesSeries[i*hoursPerBar+j]
blocked += blockedSeries[i*hoursPerBar+j]
}
stats.Series[i] = DNSStatsSeries{
Queries: queries,
Blocked: blocked,
PercentBlocked: int(float64(blocked) / float64(queries) * 100),
Queries: queries,
Blocked: blocked,
}
if queries > 0 {
stats.Series[i].PercentBlocked = int(float64(blocked) / float64(queries) * 100)
}
if queries > maxQueriesInSeries {
@@ -91,7 +112,7 @@ func FetchAdguardStats(instanceURL, username, password string) (*DNSStats, error
}
}
for i := 0; i < 8; i++ {
for i := 0; i < bars; i++ {
stats.Series[i].PercentTotal = int(float64(stats.Series[i].Queries) / float64(maxQueriesInSeries) * 100)
}
+39
View File
@@ -0,0 +1,39 @@
package feed
import (
"fmt"
"net/http"
)
type codebergReleaseResponseJson struct {
TagName string `json:"tag_name"`
PublishedAt string `json:"published_at"`
HtmlUrl string `json:"html_url"`
}
func fetchLatestCodebergRelease(request *ReleaseRequest) (*AppRelease, error) {
httpRequest, err := http.NewRequest(
"GET",
fmt.Sprintf(
"https://codeberg.org/api/v1/repos/%s/releases/latest",
request.Repository,
),
nil,
)
if err != nil {
return nil, err
}
response, err := decodeJsonFromRequest[codebergReleaseResponseJson](defaultClient, httpRequest)
if err != nil {
return nil, err
}
return &AppRelease{
Source: ReleaseSourceCodeberg,
Name: request.Repository,
Version: normalizeVersionFormat(response.TagName),
NotesUrl: response.HtmlUrl,
TimeReleased: parseRFC3339Time(response.PublishedAt),
}, nil
}
+8 -1
View File
@@ -189,12 +189,19 @@ func FetchWeatherForPlace(place *PlaceJson, units string) (*Weather, error) {
minT := slices.Min(temperatures)
maxT := slices.Max(temperatures)
temperaturesRange := float64(maxT - minT)
for i := 0; i < 12; i++ {
bars = append(bars, weatherColumn{
Temperature: temperatures[i],
Scale: float64(temperatures[i]-minT) / float64(maxT-minT),
HasPrecipitation: precipitations[i],
})
if temperaturesRange > 0 {
bars[i].Scale = float64(temperatures[i]-minT) / temperaturesRange
} else {
bars[i].Scale = 1
}
}
}
+34 -7
View File
@@ -1,20 +1,42 @@
package feed
import (
"encoding/json"
"errors"
"log/slog"
"net/http"
"sort"
"strings"
)
type piholeStatsResponse struct {
TotalQueries int `json:"dns_queries_today"`
QueriesSeries map[int64]int `json:"domains_over_time"`
BlockedQueries int `json:"ads_blocked_today"`
BlockedSeries map[int64]int `json:"ads_over_time"`
BlockedPercentage float64 `json:"ads_percentage_today"`
TopBlockedDomains map[string]int `json:"top_ads"`
DomainsBlocked int `json:"domains_being_blocked"`
TotalQueries int `json:"dns_queries_today"`
QueriesSeries map[int64]int `json:"domains_over_time"`
BlockedQueries int `json:"ads_blocked_today"`
BlockedSeries map[int64]int `json:"ads_over_time"`
BlockedPercentage float64 `json:"ads_percentage_today"`
TopBlockedDomains piholeTopBlockedDomains `json:"top_ads"`
DomainsBlocked int `json:"domains_being_blocked"`
}
// If user has some level of privacy enabled on Pihole, `json:"top_ads"` is an empty array
// Use custom unmarshal behavior to avoid not getting the rest of the valid data when unmarshalling
type piholeTopBlockedDomains map[string]int
func (p *piholeTopBlockedDomains) UnmarshalJSON(data []byte) error {
// NOTE: do not change to piholeTopBlockedDomains type here or it will cause a stack overflow
// because of the UnmarshalJSON method getting called recursively
temp := make(map[string]int)
err := json.Unmarshal(data, &temp)
if err != nil {
*p = make(piholeTopBlockedDomains)
} else {
*p = temp
}
return nil
}
func FetchPiholeStats(instanceURL, token string) (*DNSStats, error) {
@@ -63,6 +85,11 @@ func FetchPiholeStats(instanceURL, token string) (*DNSStats, error) {
// Pihole _should_ return data for the last 24 hours in a 10 minute interval, 6*24 = 144
if len(responseJson.QueriesSeries) != 144 || len(responseJson.BlockedSeries) != 144 {
slog.Warn(
"DNS stats for pihole: did not get expected 144 data points",
"len(queries)", len(responseJson.QueriesSeries),
"len(blocked)", len(responseJson.BlockedSeries),
)
return stats, nil
}
+2 -2
View File
@@ -111,8 +111,8 @@ func FetchSubredditPosts(subreddit, sort, topPeriod, search, commentsUrlTemplate
TimePosted: time.Unix(int64(post.Time), 0),
}
if post.Thumbnail != "" && post.Thumbnail != "self" && post.Thumbnail != "default" {
forumPost.ThumbnailUrl = post.Thumbnail
if post.Thumbnail != "" && post.Thumbnail != "self" && post.Thumbnail != "default" && post.Thumbnail != "nsfw" {
forumPost.ThumbnailUrl = html.UnescapeString(post.Thumbnail)
}
if !post.IsSelf {
+3
View File
@@ -9,6 +9,7 @@ import (
type ReleaseSource string
const (
ReleaseSourceCodeberg ReleaseSource = "codeberg"
ReleaseSourceGithub ReleaseSource = "github"
ReleaseSourceGitlab ReleaseSource = "gitlab"
ReleaseSourceDockerHub ReleaseSource = "dockerhub"
@@ -57,6 +58,8 @@ func FetchLatestReleases(requests []*ReleaseRequest) (AppReleases, error) {
func fetchLatestReleaseTask(request *ReleaseRequest) (*AppRelease, error) {
switch request.Source {
case ReleaseSourceCodeberg:
return fetchLatestCodebergRelease(request)
case ReleaseSourceGithub:
return fetchLatestGithubRelease(request)
case ReleaseSourceGitlab:
+3
View File
@@ -275,6 +275,9 @@ func (a *Application) Serve() error {
mux.HandleFunc("GET /api/pages/{page}/content/{$}", a.HandlePageContentRequest)
mux.HandleFunc("/api/widgets/{widget}/{path...}", a.HandleWidgetRequest)
mux.HandleFunc("GET /api/healthz", func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
})
mux.Handle(
fmt.Sprintf("GET /static/%s/{path...}", a.Config.Server.AssetsHash),
+1 -1
View File
@@ -162,7 +162,7 @@ func toSimpleIconIfPrefixed(icon string) (string, bool) {
}
icon = strings.TrimPrefix(icon, "si:")
icon = "https://cdnjs.cloudflare.com/ajax/libs/simple-icons/11.14.0/" + icon + ".svg"
icon = "https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/" + icon + ".svg"
return icon, true
}
+5 -1
View File
@@ -40,7 +40,6 @@ func (widget *Releases) Initialize() error {
for _, repository := range widget.Repositories {
parts := strings.SplitN(repository, ":", 2)
var request *feed.ReleaseRequest
if len(parts) == 1 {
request = &feed.ReleaseRequest{
Source: feed.ReleaseSourceGithub,
@@ -65,6 +64,11 @@ func (widget *Releases) Initialize() error {
Source: feed.ReleaseSourceDockerHub,
Repository: parts[1],
}
} else if parts[0] == string(feed.ReleaseSourceCodeberg) {
request = &feed.ReleaseRequest{
Source: feed.ReleaseSourceCodeberg,
Repository: parts[1],
}
} else {
return errors.New("invalid repository source " + parts[0])
}