Allow inserting env variables anywhere in the config

This commit is contained in:
Svilen Markov 2024-12-16 23:59:25 +00:00
parent 8d2639b349
commit dbcc13a5cf
13 changed files with 90 additions and 101 deletions

View File

@ -2,7 +2,6 @@ package glance
import (
"fmt"
"os"
"regexp"
"strconv"
"strings"
@ -112,61 +111,6 @@ func (d *durationField) UnmarshalYAML(node *yaml.Node) error {
return nil
}
var optionalEnvFieldPattern = regexp.MustCompile(`(^|.)\$\{([A-Z0-9_]+)\}`)
type optionalEnvField string
func (f *optionalEnvField) UnmarshalYAML(node *yaml.Node) error {
var value string
err := node.Decode(&value)
if err != nil {
return err
}
replaced := optionalEnvFieldPattern.ReplaceAllStringFunc(value, func(match string) string {
if err != nil {
return ""
}
groups := optionalEnvFieldPattern.FindStringSubmatch(match)
if len(groups) != 3 {
return match
}
prefix, key := groups[1], groups[2]
if prefix == `\` {
if len(match) >= 2 {
return match[1:]
} else {
return ""
}
}
value, found := os.LookupEnv(key)
if !found {
err = fmt.Errorf("environment variable %s not found", key)
return ""
}
return prefix + value
})
if err != nil {
return err
}
*f = optionalEnvField(replaced)
return nil
}
func (f *optionalEnvField) String() string {
return string(*f)
}
type customIconField struct {
URL string
IsFlatIcon bool

View File

@ -69,10 +69,15 @@ type page struct {
}
func newConfigFromYAML(contents []byte) (*config, error) {
contents, err := parseConfigEnvVariables(contents)
if err != nil {
return nil, err
}
config := &config{}
config.Server.Port = 8080
err := yaml.Unmarshal(contents, config)
err = yaml.Unmarshal(contents, config)
if err != nil {
return nil, err
}
@ -94,6 +99,46 @@ func newConfigFromYAML(contents []byte) (*config, error) {
return config, nil
}
var configEnvVariablePattern = regexp.MustCompile(`(^|.)\$\{([A-Z0-9_]+)\}`)
func parseConfigEnvVariables(contents []byte) ([]byte, error) {
var err error
replaced := configEnvVariablePattern.ReplaceAllFunc(contents, func(match []byte) []byte {
if err != nil {
return nil
}
groups := configEnvVariablePattern.FindSubmatch(match)
if len(groups) != 3 {
return match
}
prefix, key := string(groups[1]), string(groups[2])
if prefix == `\` {
if len(match) >= 2 {
return match[1:]
} else {
return nil
}
}
value, found := os.LookupEnv(key)
if !found {
err = fmt.Errorf("environment variable %s not found", key)
return nil
}
return []byte(prefix + value)
})
if err != nil {
return nil, err
}
return replaced, nil
}
func formatWidgetInitError(err error, w widget) error {
return fmt.Errorf("%s widget: %v", w.GetType(), err)
}

View File

@ -13,7 +13,7 @@
<img class="bookmarks-icon{{ if .Icon.IsFlatIcon }} flat-icon{{ end }}" src="{{ .Icon.URL }}" alt="" loading="lazy">
</div>
{{ end }}
<a href="{{ .URL.String | safeURL }}" class="bookmarks-link {{ if .HideArrow }}bookmarks-link-no-arrow {{ end }}color-highlight size-h4" {{ if not .SameTab }}target="_blank"{{ end }} rel="noreferrer">{{ .Title }}</a>
<a href="{{ .URL | safeURL }}" class="bookmarks-link {{ if .HideArrow }}bookmarks-link-no-arrow {{ end }}color-highlight size-h4" {{ if not .SameTab }}target="_blank"{{ end }} rel="noreferrer">{{ .Title }}</a>
</li>
{{ end }}
</ul>

View File

@ -21,7 +21,7 @@
{{ end }}
{{ define "site" }}
<a class="size-title-dynamic color-highlight text-truncate block grow" href="{{ .URL.String | safeURL }}" {{ if not .SameTab }}target="_blank"{{ end }} rel="noreferrer">{{ .Title }}</a>
<a class="size-title-dynamic color-highlight text-truncate block grow" href="{{ .URL | safeURL }}" {{ if not .SameTab }}target="_blank"{{ end }} rel="noreferrer">{{ .Title }}</a>
{{ if not .Status.TimedOut }}<div>{{ .Status.ResponseTime.Milliseconds | formatNumber }}ms</div>{{ end }}
{{ if eq .StatusStyle "ok" }}
<div class="monitor-site-status-icon-compact" title="{{ .Status.Code }}">

View File

@ -25,7 +25,7 @@
<img class="monitor-site-icon{{ if .Icon.IsFlatIcon }} flat-icon{{ end }}" src="{{ .Icon.URL }}" alt="" loading="lazy">
{{ end }}
<div class="min-width-0">
<a class="size-h3 color-highlight text-truncate block" href="{{ .URL.String | safeURL }}" {{ if not .SameTab }}target="_blank"{{ end }} rel="noreferrer">{{ .Title }}</a>
<a class="size-h3 color-highlight text-truncate block" href="{{ .URL | safeURL }}" {{ if not .SameTab }}target="_blank"{{ end }} rel="noreferrer">{{ .Title }}</a>
<ul class="list-horizontal-text">
{{ if not .Status.Error }}
<li title="{{ .Status.Code }}">{{ .StatusText }}</li>

View File

@ -13,11 +13,11 @@ type bookmarksWidget struct {
Title string `yaml:"title"`
Color *hslColorField `yaml:"color"`
Links []struct {
Title string `yaml:"title"`
URL optionalEnvField `yaml:"url"`
Icon customIconField `yaml:"icon"`
SameTab bool `yaml:"same-tab"`
HideArrow bool `yaml:"hide-arrow"`
Title string `yaml:"title"`
URL string `yaml:"url"`
Icon customIconField `yaml:"icon"`
SameTab bool `yaml:"same-tab"`
HideArrow bool `yaml:"hide-arrow"`
} `yaml:"links"`
} `yaml:"groups"`
}

View File

@ -18,7 +18,7 @@ type changeDetectionWidget struct {
ChangeDetections changeDetectionWatchList `yaml:"-"`
WatchUUIDs []string `yaml:"watches"`
InstanceURL string `yaml:"instance-url"`
Token optionalEnvField `yaml:"token"`
Token string `yaml:"token"`
Limit int `yaml:"limit"`
CollapseAfter int `yaml:"collapse-after"`
}

View File

@ -18,13 +18,13 @@ var customAPIWidgetTemplate = mustParseTemplate("custom-api.html", "widget-base.
type customAPIWidget struct {
widgetBase `yaml:",inline"`
URL optionalEnvField `yaml:"url"`
Template string `yaml:"template"`
Frameless bool `yaml:"frameless"`
Headers map[string]optionalEnvField `yaml:"headers"`
APIRequest *http.Request `yaml:"-"`
compiledTemplate *template.Template `yaml:"-"`
CompiledHTML template.HTML `yaml:"-"`
URL string `yaml:"url"`
Template string `yaml:"template"`
Frameless bool `yaml:"frameless"`
Headers map[string]string `yaml:"headers"`
APIRequest *http.Request `yaml:"-"`
compiledTemplate *template.Template `yaml:"-"`
CompiledHTML template.HTML `yaml:"-"`
}
func (widget *customAPIWidget) initialize() error {
@ -45,13 +45,13 @@ func (widget *customAPIWidget) initialize() error {
widget.compiledTemplate = compiledTemplate
req, err := http.NewRequest(http.MethodGet, widget.URL.String(), nil)
req, err := http.NewRequest(http.MethodGet, widget.URL, nil)
if err != nil {
return err
}
for key, value := range widget.Headers {
req.Header.Add(key, value.String())
req.Header.Add(key, value)
}
widget.APIRequest = req

View File

@ -20,13 +20,13 @@ type dnsStatsWidget struct {
TimeLabels [8]string `yaml:"-"`
Stats *dnsStats `yaml:"-"`
HourFormat string `yaml:"hour-format"`
Service string `yaml:"service"`
AllowInsecure bool `yaml:"allow-insecure"`
URL optionalEnvField `yaml:"url"`
Token optionalEnvField `yaml:"token"`
Username optionalEnvField `yaml:"username"`
Password optionalEnvField `yaml:"password"`
HourFormat string `yaml:"hour-format"`
Service string `yaml:"service"`
AllowInsecure bool `yaml:"allow-insecure"`
URL string `yaml:"url"`
Token string `yaml:"token"`
Username string `yaml:"username"`
Password string `yaml:"password"`
}
func makeDNSWidgetTimeLabels(format string) [8]string {

View File

@ -109,9 +109,9 @@ func statusCodeToStyle(status int, altStatusCodes []int) string {
}
type SiteStatusRequest struct {
URL optionalEnvField `yaml:"url"`
CheckURL optionalEnvField `yaml:"check-url"`
AllowInsecure bool `yaml:"allow-insecure"`
URL string `yaml:"url"`
CheckURL string `yaml:"check-url"`
AllowInsecure bool `yaml:"allow-insecure"`
}
type siteStatus struct {
@ -123,10 +123,10 @@ type siteStatus struct {
func fetchSiteStatusTask(statusRequest *SiteStatusRequest) (siteStatus, error) {
var url string
if statusRequest.CheckURL.String() != "" {
url = statusRequest.CheckURL.String()
if statusRequest.CheckURL != "" {
url = statusRequest.CheckURL
} else {
url = statusRequest.URL.String()
url = statusRequest.URL
}
request, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {

View File

@ -20,8 +20,8 @@ type releasesWidget struct {
Releases appReleaseList `yaml:"-"`
releaseRequests []*releaseRequest `yaml:"-"`
Repositories []string `yaml:"repositories"`
Token optionalEnvField `yaml:"token"`
GitLabToken optionalEnvField `yaml:"gitlab-token"`
Token string `yaml:"token"`
GitLabToken string `yaml:"gitlab-token"`
Limit int `yaml:"limit"`
CollapseAfter int `yaml:"collapse-after"`
ShowSourceIcon bool `yaml:"show-source-icon"`
@ -38,8 +38,8 @@ func (widget *releasesWidget) initialize() error {
widget.CollapseAfter = 5
}
var tokenAsString = widget.Token.String()
var gitLabTokenAsString = widget.GitLabToken.String()
var tokenAsString = widget.Token
var gitLabTokenAsString = widget.GitLabToken
for _, repository := range widget.Repositories {
parts := strings.SplitN(repository, ":", 2)

View File

@ -14,12 +14,12 @@ var repositoryWidgetTemplate = mustParseTemplate("repository.html", "widget-base
type repositoryWidget struct {
widgetBase `yaml:",inline"`
RequestedRepository string `yaml:"repository"`
Token optionalEnvField `yaml:"token"`
PullRequestsLimit int `yaml:"pull-requests-limit"`
IssuesLimit int `yaml:"issues-limit"`
CommitsLimit int `yaml:"commits-limit"`
Repository repository `yaml:"-"`
RequestedRepository string `yaml:"repository"`
Token string `yaml:"token"`
PullRequestsLimit int `yaml:"pull-requests-limit"`
IssuesLimit int `yaml:"issues-limit"`
CommitsLimit int `yaml:"commits-limit"`
Repository repository `yaml:"-"`
}
func (widget *repositoryWidget) initialize() error {

View File

@ -139,7 +139,7 @@ func shortenFeedDescriptionLen(description string, maxLen int) string {
}
type rssFeedRequest struct {
URL optionalEnvField `yaml:"url"`
URL string `yaml:"url"`
Title string `yaml:"title"`
HideCategories bool `yaml:"hide-categories"`
HideDescription bool `yaml:"hide-description"`
@ -161,7 +161,7 @@ func (f rssFeedItemList) sortByNewest() rssFeedItemList {
var feedParser = gofeed.NewParser()
func fetchItemsFromRSSFeedTask(request rssFeedRequest) ([]rssFeedItem, error) {
req, err := http.NewRequest("GET", request.URL.String(), nil)
req, err := http.NewRequest("GET", request.URL, nil)
if err != nil {
return nil, err
}
@ -206,7 +206,7 @@ func fetchItemsFromRSSFeedTask(request rssFeedRequest) ([]rssFeedItem, error) {
} else {
parsedUrl, err := url.Parse(feed.Link)
if err != nil {
parsedUrl, err = url.Parse(request.URL.String())
parsedUrl, err = url.Parse(request.URL)
}
if err == nil {