101 lines
2.3 KiB
Go
101 lines
2.3 KiB
Go
package motd
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/portainer/portainer/api/http/client"
|
|
"github.com/portainer/portainer/pkg/libcrypto"
|
|
libclient "github.com/portainer/portainer/pkg/libhttp/client"
|
|
"github.com/portainer/portainer/pkg/schedule"
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"github.com/segmentio/encoding/json"
|
|
)
|
|
|
|
const refreshInterval = 6 * time.Hour
|
|
|
|
// Motd holds the processed message of the day data.
|
|
type Motd struct {
|
|
Title string `json:"Title"`
|
|
Message string `json:"Message"`
|
|
ContentLayout map[string]string `json:"ContentLayout"`
|
|
Style string `json:"Style"`
|
|
Hash []byte `json:"Hash"`
|
|
}
|
|
|
|
// Service fetches and caches the MOTD from an external URL.
|
|
type Service struct {
|
|
cached atomic.Pointer[Motd]
|
|
motdURL string
|
|
}
|
|
|
|
// NewService creates a new MOTD service that fetches from motdURL.
|
|
func NewService(motdURL string) *Service {
|
|
return &Service{
|
|
motdURL: motdURL,
|
|
}
|
|
}
|
|
|
|
// Start warms the cache immediately and refreshes it every refreshInterval.
|
|
func (s *Service) Start(ctx context.Context) {
|
|
if err := libclient.ExternalRequestDisabled(s.motdURL); err != nil {
|
|
return
|
|
}
|
|
|
|
go s.refresh()
|
|
go schedule.RunOnInterval(ctx, refreshInterval, s.refresh, nil)
|
|
}
|
|
|
|
// GetCached returns the cached MOTD
|
|
func (s *Service) GetCached() Motd {
|
|
motd := s.cached.Load()
|
|
if motd == nil {
|
|
return Motd{}
|
|
}
|
|
|
|
return *motd
|
|
}
|
|
|
|
type motdData struct {
|
|
Title string `json:"title"`
|
|
Message []string `json:"message"`
|
|
ContentLayout map[string]string `json:"contentLayout"`
|
|
Style string `json:"style"`
|
|
}
|
|
|
|
func (s *Service) refresh() {
|
|
if err := libclient.ExternalRequestDisabled(s.motdURL); err != nil {
|
|
log.Debug().Err(err).Msg("External request disabled: MOTD")
|
|
|
|
s.cached.Store(nil)
|
|
|
|
return
|
|
}
|
|
|
|
raw, err := client.Get(s.motdURL, 0)
|
|
if err != nil {
|
|
log.Debug().Err(err).Msg("Failed to fetch MOTD")
|
|
return
|
|
}
|
|
|
|
var data motdData
|
|
if err := json.Unmarshal(raw, &data); err != nil {
|
|
log.Debug().Err(err).Msg("Failed to parse MOTD")
|
|
return
|
|
}
|
|
|
|
message := strings.Join(data.Message, "\n")
|
|
hash := libcrypto.InsecureHashFromBytes([]byte(message))
|
|
|
|
s.cached.Store(&Motd{
|
|
Title: data.Title,
|
|
Message: message,
|
|
Hash: hash,
|
|
ContentLayout: data.ContentLayout,
|
|
Style: data.Style,
|
|
})
|
|
}
|