Allow widgets to handle HTTP requests

This commit is contained in:
Svilen Markov 2024-08-01 18:26:38 +01:00
parent c041197f3f
commit 795caa5d9d
2 changed files with 84 additions and 25 deletions

View File

@ -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 {
@ -106,16 +108,24 @@ 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
}
}
}
return app, nil
@ -190,6 +200,26 @@ 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 "/static/" + a.Config.Server.AssetsHash + "/" + asset
}
@ -203,7 +233,10 @@ func (a *Application) Serve() error {
mux.HandleFunc("GET /{$}", a.HandlePageRequest)
mux.HandleFunc("GET /{page}", a.HandlePageRequest)
mux.HandleFunc("GET /api/pages/{page}/content/{$}", a.HandlePageContentRequest)
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)),

View File

@ -8,6 +8,8 @@ import (
"html/template"
"log/slog"
"math"
"net/http"
"sync/atomic"
"time"
"github.com/glanceapp/glance/internal/feed"
@ -15,51 +17,59 @@ import (
"gopkg.in/yaml.v3"
)
var uniqueID atomic.Uint64
func New(widgetType string) (Widget, error) {
var widget Widget
switch widgetType {
case "calendar":
return &Calendar{}, nil
widget = &Calendar{}
case "clock":
return &Clock{}, nil
widget = &Clock{}
case "weather":
return &Weather{}, nil
widget = &Weather{}
case "bookmarks":
return &Bookmarks{}, nil
widget = &Bookmarks{}
case "iframe":
return &IFrame{}, nil
widget = &IFrame{}
case "html":
return &HTML{}, nil
widget = &HTML{}
case "hacker-news":
return &HackerNews{}, nil
widget = &HackerNews{}
case "releases":
return &Releases{}, nil
widget = &Releases{}
case "videos":
return &Videos{}, nil
widget = &Videos{}
case "markets", "stocks":
return &Markets{}, nil
widget = &Markets{}
case "reddit":
return &Reddit{}, nil
widget = &Reddit{}
case "rss":
return &RSS{}, nil
widget = &RSS{}
case "monitor":
return &Monitor{}, nil
widget = &Monitor{}
case "twitch-top-games":
return &TwitchGames{}, nil
widget = &TwitchGames{}
case "twitch-channels":
return &TwitchChannels{}, nil
widget = &TwitchChannels{}
case "lobsters":
return &Lobsters{}, nil
widget = &Lobsters{}
case "change-detection":
return &ChangeDetection{}, nil
widget = &ChangeDetection{}
case "repository":
return &Repository{}, nil
widget = &Repository{}
case "search":
return &Search{}, nil
widget = &Search{}
case "extension":
return &Extension{}, nil
widget = &Extension{}
default:
return nil, fmt.Errorf("unknown widget type: %s", widgetType)
}
widget.SetID(uniqueID.Add(1))
return widget, nil
}
type Widgets []Widget
@ -90,7 +100,7 @@ func (w *Widgets) UnmarshalYAML(node *yaml.Node) error {
return err
}
if err = widget.Initialize(); err != nil {
if err := widget.Initialize(); err != nil {
return err
}
@ -106,6 +116,9 @@ type Widget interface {
Update(context.Context)
Render() template.HTML
GetType() string
GetID() uint64
SetID(uint64)
HandleRequest(w http.ResponseWriter, r *http.Request)
}
type cacheType int
@ -117,6 +130,7 @@ const (
)
type widgetBase struct {
ID uint64 `yaml:"-"`
Type string `yaml:"type"`
Title string `yaml:"title"`
TitleURL string `yaml:"title-url"`
@ -148,6 +162,18 @@ func (w *widgetBase) Update(ctx context.Context) {
}
func (w *widgetBase) GetID() uint64 {
return w.ID
}
func (w *widgetBase) SetID(id uint64) {
w.ID = id
}
func (widget *widgetBase) HandleRequest(w http.ResponseWriter, r *http.Request) {
http.Error(w, "not implemented", http.StatusNotImplemented)
}
func (w *widgetBase) GetType() string {
return w.Type
}