simplify icon caching and homepage item override

This commit is contained in:
yusing
2025-01-21 06:16:00 +08:00
parent d429374924
commit 8b1a3a31ff
21 changed files with 395 additions and 331 deletions

View File

@@ -47,6 +47,10 @@ func NewHandler(cfg config.ConfigInstance) http.Handler {
})
mux.HandleFunc("GET,POST", "/v1/auth/callback", defaultAuth.LoginCallbackHandler)
mux.HandleFunc("GET,POST", "/v1/auth/logout", auth.LogoutCallbackHandler(defaultAuth))
} else {
mux.HandleFunc("GET", "/v1/auth/check", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
}
return mux
}

View File

@@ -5,7 +5,6 @@ import (
"bytes"
"context"
"errors"
"fmt"
"io"
"net"
"net/http"
@@ -17,13 +16,15 @@ import (
"github.com/PuerkitoBio/goquery"
"github.com/vincent-petithory/dataurl"
"github.com/yusing/go-proxy/internal"
U "github.com/yusing/go-proxy/internal/api/v1/utils"
"github.com/yusing/go-proxy/internal/common"
"github.com/yusing/go-proxy/internal/homepage"
"github.com/yusing/go-proxy/internal/logging"
gphttp "github.com/yusing/go-proxy/internal/net/http"
"github.com/yusing/go-proxy/internal/route/routes"
route "github.com/yusing/go-proxy/internal/route/types"
"github.com/yusing/go-proxy/internal/task"
"github.com/yusing/go-proxy/internal/utils"
)
type content struct {
@@ -80,17 +81,15 @@ func GetFavIcon(w http.ResponseWriter, req *http.Request) {
var status int
var errMsg string
hp := r.RawEntry().Homepage
if hp != nil && hp.Icon != nil {
hp := r.RawEntry().Homepage.GetOverride()
if !hp.IsEmpty() && hp.Icon != nil {
switch hp.Icon.IconSource {
case homepage.IconSourceAbsolute:
icon, status, errMsg = fetchIconAbsolute(hp.Icon.Value)
case homepage.IconSourceRelative:
icon, status, errMsg = findIcon(r, req, hp.Icon.Value)
case homepage.IconSourceWalkXCode:
icon, status, errMsg = fetchWalkxcodeIcon(hp.Icon.Extra.FileType, hp.Icon.Extra.Name)
case homepage.IconSourceSelfhSt:
icon, status, errMsg = fetchSelfhStIcon(hp.Icon.Extra.FileType, hp.Icon.Extra.Name)
case homepage.IconSourceWalkXCode, homepage.IconSourceSelfhSt:
icon, status, errMsg = fetchKnownIcon(hp.Icon)
}
} else {
// try extract from "link[rel=icon]"
@@ -112,6 +111,24 @@ var (
iconCacheMu sync.RWMutex
)
func InitIconCache() {
err := utils.LoadJSONIfExist(common.IconCachePath, &iconCache)
if err != nil {
logging.Error().Err(err).Msg("failed to load icon cache")
} else {
logging.Info().Msgf("icon cache loaded (%d icons)", len(iconCache))
}
task.OnProgramExit("save_favicon_cache", func() {
iconCacheMu.Lock()
defer iconCacheMu.Unlock()
if err := utils.SaveJSON(common.IconCachePath, &iconCache, 0o644); err != nil {
logging.Error().Err(err).Msg("failed to save icon cache")
}
})
}
func ResetIconCache(route route.HTTPRoute) {
iconCacheMu.Lock()
defer iconCacheMu.Unlock()
@@ -122,6 +139,11 @@ func loadIconCache(key string) (icon []byte, ok bool) {
iconCacheMu.RLock()
defer iconCacheMu.RUnlock()
icon, ok = iconCache[key]
if ok {
logging.Debug().
Str("key", key).
Msg("icon found in cache")
}
return
}
@@ -172,56 +194,30 @@ func sanitizeName(name string) string {
return strings.ToLower(nameSanitizer.Replace(name))
}
func fetchWalkxcodeIcon(filetype, name string) ([]byte, int, string) {
func fetchKnownIcon(url *homepage.IconURL) ([]byte, int, string) {
// if icon isn't in the list, no need to fetch
if !internal.HasWalkxCodeIcon(name, filetype) {
if !url.HasIcon() {
logging.Debug().
Str("filetype", filetype).
Str("name", name).
Msg("icon not found")
return nil, http.StatusNotFound, "icon not found"
Str("value", url.String()).
Str("url", url.URL()).
Msg("no such icon")
return nil, http.StatusNotFound, "no such icon"
}
icon, ok := loadIconCache("walkxcode/" + filetype + "/" + name)
if ok {
return icon, http.StatusOK, ""
}
// url := homepage.DashboardIconBaseURL + filetype + "/" + name + "." + filetype
url := fmt.Sprintf("https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/%s/%s.%s", filetype, name, filetype)
return fetchIconAbsolute(url)
}
func fetchSelfhStIcon(filetype, reference string) ([]byte, int, string) {
// if icon isn't in the list, no need to fetch
if !internal.HasSelfhstIcon(reference, filetype) {
logging.Debug().
Str("filetype", filetype).
Str("reference", reference).
Msg("icon not found")
return nil, http.StatusNotFound, "icon not found"
}
icon, ok := loadIconCache("selfh.st/" + filetype + "/" + reference)
if ok {
return icon, http.StatusOK, ""
}
url := fmt.Sprintf("https://cdn.jsdelivr.net/gh/selfhst/icons/%s/%s.%s", filetype, reference, filetype)
return fetchIconAbsolute(url)
return fetchIconAbsolute(url.URL())
}
func fetchIcon(filetype, filename string) (icon []byte, status int, errMsg string) {
icon, status, errMsg = fetchSelfhStIcon(filetype, filename)
icon, status, errMsg = fetchKnownIcon(homepage.NewSelfhStIconURL(filename, filetype))
if icon != nil {
return
}
icon, status, errMsg = fetchWalkxcodeIcon(filetype, filename)
icon, status, errMsg = fetchKnownIcon(homepage.NewWalkXCodeIconURL(filename, filetype))
return
}
func findIcon(r route.HTTPRoute, req *http.Request, uri string) (icon []byte, status int, errMsg string) {
key := r.TargetName()
key := r.RawEntry().Provider + ":" + r.TargetName()
icon, ok := loadIconCache(key)
if ok {
if icon == nil {
@@ -239,8 +235,9 @@ func findIcon(r route.HTTPRoute, req *http.Request, uri string) (icon []byte, st
// fallback to parse html
icon, status, errMsg = findIconSlow(r, req, uri)
}
// set even if error (nil)
storeIconCache(key, icon)
if icon != nil {
storeIconCache(key, icon)
}
return
}

View File

@@ -4,19 +4,14 @@ import (
"net/http"
"strconv"
"github.com/yusing/go-proxy/internal/api/v1/utils"
"github.com/yusing/go-proxy/internal/homepage"
"github.com/yusing/go-proxy/internal/utils/strutils"
"github.com/yusing/go-proxy/internal/utils"
)
const (
HomepageOverrideDisplayname = "display_name"
HomepageOverrideDisplayOrder = "display_order"
HomepageOverrideDisplayCategory = "display_category"
HomepageOverrideCategoryOrder = "category_order"
HomepageOverrideCategoryName = "category_name"
HomepageOverrideIcon = "icon"
HomepageOverrideShow = "show"
HomepageOverrideItem = "item"
HomepageOverrideCategoryOrder = "category_order"
HomepageOverrideCategoryName = "category_name"
)
func SetHomePageOverrides(w http.ResponseWriter, r *http.Request) {
@@ -29,30 +24,27 @@ func SetHomePageOverrides(w http.ResponseWriter, r *http.Request) {
http.Error(w, "missing value", http.StatusBadRequest)
return
}
overrides := homepage.GetJSONConfig()
overrides := homepage.GetOverrideConfig()
switch what {
case HomepageOverrideDisplayname:
utils.RespondError(w, overrides.SetDisplayNameOverride(which, value))
case HomepageOverrideDisplayCategory:
utils.RespondError(w, overrides.SetDisplayCategoryOverride(which, value))
case HomepageOverrideItem:
var override homepage.ItemConfig
if err := utils.DeserializeJSON([]byte(value), &override); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
overrides.OverrideItem(which, &override)
case HomepageOverrideCategoryName:
utils.RespondError(w, overrides.SetCategoryNameOverride(which, value))
case HomepageOverrideIcon:
utils.RespondError(w, overrides.SetIconOverride(which, value))
case HomepageOverrideShow:
utils.RespondError(w, overrides.SetShowItemOverride(which, strutils.ParseBool(value)))
case HomepageOverrideDisplayOrder, HomepageOverrideCategoryOrder:
overrides.SetCategoryNameOverride(which, value)
case HomepageOverrideCategoryOrder:
v, err := strconv.Atoi(value)
if err != nil {
http.Error(w, "invalid integer", http.StatusBadRequest)
return
}
if what == HomepageOverrideDisplayOrder {
utils.RespondError(w, overrides.SetDisplayOrder(which, v))
} else {
utils.RespondError(w, overrides.SetCategoryOrder(which, v))
}
overrides.SetCategoryOrder(which, v)
default:
http.Error(w, "invalid what", http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusOK)
}

View File

@@ -2,6 +2,7 @@ package v1
import (
"net/http"
"strconv"
"strings"
"github.com/yusing/go-proxy/internal"
@@ -61,7 +62,11 @@ func List(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
case ListHomepageCategories:
U.RespondJSON(w, r, routequery.HomepageCategories())
case ListIcons:
icons, err := internal.ListAvailableIcons()
limit, err := strconv.Atoi(r.FormValue("limit"))
if err != nil {
limit = 0
}
icons, err := internal.SearchIcons(r.FormValue("keyword"), limit)
if err != nil {
U.RespondError(w, err)
return

View File

@@ -60,7 +60,7 @@ func PeriodicWS(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Reques
return
case <-ticker.C:
if err := do(conn); err != nil {
HandleErr(w, r, err)
LogError(r).Msg(err.Error())
return
}
}