Skip to content

Commit

Permalink
refactor: Register map and GitHub status updaters as PocketBase crons
Browse files Browse the repository at this point in the history
  • Loading branch information
gabe565 committed Jan 19, 2025
1 parent 3e87fe6 commit a86fa26
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 89 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module gabe565.com/portfolio
go 1.23.5

require (
gabe565.com/utils v0.0.0-20241213205714-152b8de1d3fe
github.com/meyskens/go-turnstile v0.0.0-20230622160222-89160e594ca1
github.com/pocketbase/pocketbase v0.24.4
github.com/spf13/cobra v1.8.1
Expand Down
9 changes: 6 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyX
cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
gabe565.com/utils v0.0.0-20241213205714-152b8de1d3fe h1:/QXzT2uIjZJBhJ8eADVxXMPZT716LpRVLj4TeZm+Qz0=
gabe565.com/utils v0.0.0-20241213205714-152b8de1d3fe/go.mod h1:6FZP8OeF0k3hTsPClyDGEILcBg3hvGcSp4u4pmjMusY=
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
Expand Down Expand Up @@ -65,8 +67,9 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -192,8 +195,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
Expand Down
16 changes: 12 additions & 4 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package config

import "time"
import (
"net/url"
"time"
)

type Config struct {
PublicDir string
Expand All @@ -21,7 +24,7 @@ type Turnstile struct {

type GitHubStats struct {
Interval time.Duration
SourceURL string
SourceURL URL
UserParams map[string]string
LangsParams map[string]string
}
Expand All @@ -37,8 +40,13 @@ func New() *Config {
Secret: "1x0000000000000000000000000000000AA",
},
GitHubStats: GitHubStats{
Interval: 4 * time.Hour,
SourceURL: "https://github-readme-stats.vercel.app",
Interval: 4 * time.Hour,
SourceURL: URL{
&url.URL{
Scheme: "https",
Host: "github-readme-stats.vercel.app",
},
},
UserParams: map[string]string{
"username": "gabe565",
"show_icons": "true",
Expand Down
2 changes: 1 addition & 1 deletion internal/config/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (c *Config) RegisterFlags(cmd *cobra.Command) {
fs.StringVar(&c.Turnstile.Secret, FlagTurnstileSecret, c.Turnstile.Secret, "Turnstile captcha secret key")

fs.DurationVar(&c.GitHubStats.Interval, FlagStatsInterval, c.GitHubStats.Interval, "GitHub readme stats update interval")
fs.StringVar(&c.GitHubStats.SourceURL, FlagStatsSource, c.GitHubStats.SourceURL, "GitHub readme stats source URL")
fs.Var(&c.GitHubStats.SourceURL, FlagStatsSource, "GitHub readme stats source URL")
fs.StringToStringVar(&c.GitHubStats.UserParams, FlagStatsUserParams, c.GitHubStats.UserParams, "GitHub readme stats params")
fs.StringToStringVar(&c.GitHubStats.LangsParams, FlagStatsLangsParams, c.GitHubStats.LangsParams, "GitHub readme stats top-langs params")
}
20 changes: 20 additions & 0 deletions internal/config/url.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package config

import "net/url"

type URL struct {
*url.URL
}

func (u *URL) Set(s string) error {
newURL, err := url.Parse(s)
if err != nil {
return err
}
u.URL = newURL
return err
}

func (u *URL) Type() string {
return "string"
}
41 changes: 7 additions & 34 deletions internal/handlers/githubstats/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"io"
"log/slog"
"net/http"
"slices"
"strconv"
Expand All @@ -16,20 +15,12 @@ import (
"github.com/pocketbase/pocketbase/core"
)

func NewCache(ctx context.Context, endpoint, sourceURL string, interval time.Duration) *Cache {
c := Cache{
endpoint: endpoint,
sourceURL: sourceURL,
interval: interval,
}
c.beginUpdate(ctx)
return &c
func NewCache(sourceURL string) *Cache {
return &Cache{sourceURL: sourceURL}
}

type Cache struct {
endpoint string
sourceURL string
interval time.Duration
lastModified time.Time
etag string
data []byte
Expand All @@ -45,15 +36,16 @@ func (c *Cache) Handler(e *core.RequestEvent) error {
}

e.Response.Header().Set("Content-Type", "image/svg+xml; charset=utf-8")
e.Response.Header().Set("Cache-Control", "public, max-age="+strconv.Itoa(int(c.interval.Seconds()))+", must-revalidate")
e.Response.Header().Set("Cache-Control", "public, max-age=86400, must-revalidate")
e.Response.Header().Set("ETag", c.etag)
http.ServeContent(e.Response, e.Request, "", c.lastModified, bytes.NewReader(c.data))
return nil
}

func (c *Cache) RegisterRoutes(e *core.ServeEvent) {
e.Router.HEAD(c.endpoint, c.Handler)
e.Router.GET(c.endpoint, c.Handler)
func (c *Cache) RegisterRoutes(e *core.ServeEvent, endpoint string) *Cache {
e.Router.HEAD(endpoint, c.Handler)
e.Router.GET(endpoint, c.Handler)
return c
}

var ErrUpstreamRequest = errors.New("upstream request failed")
Expand Down Expand Up @@ -113,22 +105,3 @@ func (c *Cache) Update(ctx context.Context) error {
c.etag = etag
return nil
}

func (c *Cache) beginUpdate(ctx context.Context) {
go func() {
timer := time.NewTimer(0)
defer timer.Stop()

for {
select {
case <-ctx.Done():
return
case <-timer.C:
timer.Reset(c.interval)
if err := c.Update(ctx); err != nil {
slog.Error("Failed to update GitHub stats", "error", err)
}
}
}
}()
}
44 changes: 25 additions & 19 deletions internal/handlers/githubstats/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,43 @@ package githubstats

import (
"context"
"log/slog"
"net/url"

"gabe565.com/portfolio/internal/config"
"github.com/pocketbase/pocketbase/core"
)

func RegisterRoutes(ctx context.Context, conf *config.Config, e *core.ServeEvent) error {
parsedURL, err := url.Parse(conf.GitHubStats.SourceURL)
if err != nil {
return err
}

userURL, err := formatURL(parsedURL, "api", conf.GitHubStats.UserParams)
if err != nil {
return err
}
NewCache(ctx, "/api/github-stats/stats", userURL, conf.GitHubStats.Interval).RegisterRoutes(e)

langsURL, err := formatURL(parsedURL, "api/top-langs", conf.GitHubStats.LangsParams)
if err != nil {
return err
stats := NewCache(
formatURL(conf.GitHubStats.SourceURL.URL, "api", conf.GitHubStats.UserParams),
).RegisterRoutes(e, "/api/github-stats/stats")

topLangs := NewCache(
formatURL(conf.GitHubStats.SourceURL.URL, "api/top-langs", conf.GitHubStats.LangsParams),
).RegisterRoutes(e, "/api/github-stats/top-langs")

update := func() {
go func() {
if err := stats.Update(ctx); err != nil {
slog.Error("Failed to update GitHub stats", "error", err)
}
}()
go func() {
if err := topLangs.Update(ctx); err != nil {
slog.Error("Failed to update GitHub top langs", "error", err)
}
}()
}
NewCache(ctx, "/api/github-stats/top-langs", langsURL, conf.GitHubStats.Interval).RegisterRoutes(e)

return nil
go update()
return e.App.Cron().Add("updateGitHubStats", "0 */4 * * *", update)
}

func formatURL(src *url.URL, path string, params map[string]string) (string, error) {
func formatURL(src *url.URL, path string, params map[string]string) string {
u, err := src.Parse(path)
if err != nil {
return "", err
panic(err)
}

q := u.Query()
Expand All @@ -41,5 +47,5 @@ func formatURL(src *url.URL, path string, params map[string]string) (string, err
}
u.RawQuery = q.Encode()

return u.String(), nil
return u.String()
}
51 changes: 23 additions & 28 deletions internal/handlers/mapbox/mapbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"time"

"gabe565.com/portfolio/internal/config"
"gabe565.com/utils/must"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/apis"
"github.com/pocketbase/pocketbase/core"
Expand All @@ -28,21 +29,26 @@ func New(ctx context.Context, app *pocketbase.PocketBase, conf *config.Config) (
return nil, err
}

h := &Client{
config: conf,
app: app,
dir: dir,
interval: 24 * time.Hour,
c := &Client{
config: conf,
app: app,
dir: dir,
}
h.beginFetch(ctx)
return h, nil

must.Must(c.RegisterCron(ctx, app, ""))
go func() {
if err := c.FetchAll(ctx); err != nil {
slog.Error("Failed to download map", "error", err)
}
}()

return c, nil
}

type Client struct {
config *config.Config
app *pocketbase.PocketBase
dir string
interval time.Duration
config *config.Config
app *pocketbase.PocketBase
dir string
}

type FetchRequest struct {
Expand All @@ -51,23 +57,12 @@ type FetchRequest struct {
Zoom float64
}

func (c *Client) beginFetch(ctx context.Context) {
go func() {
timer := time.NewTimer(0)
defer timer.Stop()

for {
select {
case <-ctx.Done():
return
case <-timer.C:
timer.Reset(c.interval)
if err := c.FetchAll(ctx); err != nil {
slog.Error("Failed to download map", "error", err)
}
}
func (c *Client) RegisterCron(ctx context.Context, app *pocketbase.PocketBase, id string) error {
return app.Cron().Add("updateMaps"+id, "0 0 * * *", func() {
if err := c.FetchAll(ctx); err != nil {
slog.Error("Failed to download map", "error", err)
}
}()
})
}

func (c *Client) FetchAll(ctx context.Context) error {
Expand Down Expand Up @@ -191,7 +186,7 @@ func (c *Client) fetchMap(ctx context.Context, name string, conf FetchRequest) e
func (c *Client) Handler() func(e *core.RequestEvent) error {
handler := apis.Static(os.DirFS(c.dir), false)
return func(e *core.RequestEvent) error {
e.Response.Header().Set("Cache-Control", "public, max-age="+strconv.Itoa(int(c.interval.Seconds()))+", must-revalidate")
e.Response.Header().Set("Cache-Control", "public, max-age=86400, must-revalidate")
return handler(e)
}
}

0 comments on commit a86fa26

Please sign in to comment.