mirror of
https://github.com/Xevion/glance.git
synced 2025-12-17 12:12:06 -06:00
Merge branch 'release/v0.6.0' into main
This commit is contained in:
@@ -33,6 +33,16 @@ func NewConfigFromYml(contents io.Reader) (*Config, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for p := range config.Pages {
|
||||
for c := range config.Pages[p].Columns {
|
||||
for w := range config.Pages[p].Columns[c].Widgets {
|
||||
if err := config.Pages[p].Columns[c].Widgets[w].Initialize(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
@@ -54,12 +64,22 @@ func configIsValid(config *Config) error {
|
||||
return fmt.Errorf("Page %d has no title", i+1)
|
||||
}
|
||||
|
||||
if config.Pages[i].Width != "" && (config.Pages[i].Width != "wide" && config.Pages[i].Width != "slim") {
|
||||
return fmt.Errorf("Page %d: width can only be either wide or slim", i+1)
|
||||
}
|
||||
|
||||
if len(config.Pages[i].Columns) == 0 {
|
||||
return fmt.Errorf("Page %d has no columns", i+1)
|
||||
}
|
||||
|
||||
if len(config.Pages[i].Columns) > 3 {
|
||||
return fmt.Errorf("Page %d has more than 3 columns: %d", i+1, len(config.Pages[i].Columns))
|
||||
if config.Pages[i].Width == "slim" {
|
||||
if len(config.Pages[i].Columns) > 2 {
|
||||
return fmt.Errorf("Page %d is slim and cannot have more than 2 columns", i+1)
|
||||
}
|
||||
} else {
|
||||
if len(config.Pages[i].Columns) > 3 {
|
||||
return fmt.Errorf("Page %d has more than 3 columns: %d", i+1, len(config.Pages[i].Columns))
|
||||
}
|
||||
}
|
||||
|
||||
columnSizesCount := make(map[string]int)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -24,6 +25,7 @@ type Application struct {
|
||||
Version string
|
||||
Config Config
|
||||
slugToPage map[string]*Page
|
||||
widgetByID map[uint64]widget.Widget
|
||||
}
|
||||
|
||||
type Theme struct {
|
||||
@@ -38,10 +40,12 @@ type Theme struct {
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
Host string `yaml:"host"`
|
||||
Port uint16 `yaml:"port"`
|
||||
AssetsPath string `yaml:"assets-path"`
|
||||
StartedAt time.Time `yaml:"-"`
|
||||
Host string `yaml:"host"`
|
||||
Port uint16 `yaml:"port"`
|
||||
AssetsPath string `yaml:"assets-path"`
|
||||
BaseURL string `yaml:"base-url"`
|
||||
AssetsHash string `yaml:"-"`
|
||||
StartedAt time.Time `yaml:"-"` // used in custom css file
|
||||
}
|
||||
|
||||
type Branding struct {
|
||||
@@ -61,11 +65,13 @@ type templateData struct {
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
Title string `yaml:"name"`
|
||||
Slug string `yaml:"slug"`
|
||||
ShowMobileHeader bool `yaml:"show-mobile-header"`
|
||||
Columns []Column `yaml:"columns"`
|
||||
mu sync.Mutex
|
||||
Title string `yaml:"name"`
|
||||
Slug string `yaml:"slug"`
|
||||
Width string `yaml:"width"`
|
||||
ShowMobileHeader bool `yaml:"show-mobile-header"`
|
||||
HideDesktopNavigation bool `yaml:"hide-desktop-navigation"`
|
||||
Columns []Column `yaml:"columns"`
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (p *Page) UpdateOutdatedWidgets() {
|
||||
@@ -111,16 +117,34 @@ func NewApplication(config *Config) (*Application, error) {
|
||||
Version: buildVersion,
|
||||
Config: *config,
|
||||
slugToPage: make(map[string]*Page),
|
||||
widgetByID: make(map[uint64]widget.Widget),
|
||||
}
|
||||
|
||||
app.slugToPage[""] = &config.Pages[0]
|
||||
|
||||
for i := range config.Pages {
|
||||
if config.Pages[i].Slug == "" {
|
||||
config.Pages[i].Slug = titleToSlug(config.Pages[i].Title)
|
||||
for p := range config.Pages {
|
||||
if config.Pages[p].Slug == "" {
|
||||
config.Pages[p].Slug = titleToSlug(config.Pages[p].Title)
|
||||
}
|
||||
|
||||
app.slugToPage[config.Pages[i].Slug] = &config.Pages[i]
|
||||
app.slugToPage[config.Pages[p].Slug] = &config.Pages[p]
|
||||
|
||||
for c := range config.Pages[p].Columns {
|
||||
for w := range config.Pages[p].Columns[c].Widgets {
|
||||
widget := config.Pages[p].Columns[c].Widgets[w]
|
||||
app.widgetByID[widget.GetID()] = widget
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config = &app.Config
|
||||
|
||||
config.Server.BaseURL = strings.TrimRight(config.Server.BaseURL, "/")
|
||||
|
||||
if config.Server.BaseURL != "" &&
|
||||
config.Theme.CustomCSSFile != "" &&
|
||||
strings.HasPrefix(config.Theme.CustomCSSFile, "/assets/") {
|
||||
config.Theme.CustomCSSFile = config.Server.BaseURL + config.Theme.CustomCSSFile
|
||||
}
|
||||
|
||||
return app, nil
|
||||
@@ -195,15 +219,47 @@ func FileServerWithCache(fs http.FileSystem, cacheDuration time.Duration) http.H
|
||||
})
|
||||
}
|
||||
|
||||
func (a *Application) HandleWidgetRequest(w http.ResponseWriter, r *http.Request) {
|
||||
widgetValue := r.PathValue("widget")
|
||||
|
||||
widgetID, err := strconv.ParseUint(widgetValue, 10, 64)
|
||||
|
||||
if err != nil {
|
||||
a.HandleNotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
widget, exists := a.widgetByID[widgetID]
|
||||
|
||||
if !exists {
|
||||
a.HandleNotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
widget.HandleRequest(w, r)
|
||||
}
|
||||
|
||||
func (a *Application) AssetPath(asset string) string {
|
||||
return a.Config.Server.BaseURL + "/static/" + a.Config.Server.AssetsHash + "/" + asset
|
||||
}
|
||||
|
||||
func (a *Application) Serve() error {
|
||||
a.Config.Server.AssetsHash = assets.PublicFSHash
|
||||
|
||||
// TODO: add gzip support, static files must have their gzipped contents cached
|
||||
// TODO: add HTTPS support
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.HandleFunc("GET /{$}", a.HandlePageRequest)
|
||||
mux.HandleFunc("GET /{page}", a.HandlePageRequest)
|
||||
|
||||
mux.HandleFunc("GET /api/pages/{page}/content/{$}", a.HandlePageContentRequest)
|
||||
mux.Handle("GET /static/{path...}", http.StripPrefix("/static/", FileServerWithCache(http.FS(assets.PublicFS), 2*time.Hour)))
|
||||
mux.HandleFunc("/api/widgets/{widget}/{path...}", a.HandleWidgetRequest)
|
||||
|
||||
mux.Handle(
|
||||
fmt.Sprintf("GET /static/%s/{path...}", a.Config.Server.AssetsHash),
|
||||
http.StripPrefix("/static/"+a.Config.Server.AssetsHash, FileServerWithCache(http.FS(assets.PublicFS), 8*time.Hour)),
|
||||
)
|
||||
|
||||
if a.Config.Server.AssetsPath != "" {
|
||||
absAssetsPath, err := filepath.Abs(a.Config.Server.AssetsPath)
|
||||
@@ -223,7 +279,7 @@ func (a *Application) Serve() error {
|
||||
}
|
||||
|
||||
a.Config.Server.StartedAt = time.Now()
|
||||
slog.Info("Starting server", "host", a.Config.Server.Host, "port", a.Config.Server.Port, "base-url", a.Config.Server.BaseURL)
|
||||
|
||||
slog.Info("Starting server", "host", a.Config.Server.Host, "port", a.Config.Server.Port)
|
||||
return server.ListenAndServe()
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func Main() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
if app.Serve() != nil {
|
||||
if err := app.Serve(); err != nil {
|
||||
fmt.Printf("http server error: %v\n", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user