79 lines
2.2 KiB
Go
79 lines
2.2 KiB
Go
// Package revalidate pings the frontend's on-demand cache revalidation
|
|
// endpoint so sitemap-relevant changes (new signups, content re-seeds) surface
|
|
// within seconds instead of waiting out the frontend's daily ISR window.
|
|
package revalidate
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"time"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// Client posts to <frontend>/api/revalidate. Safe to use when nil or when the
|
|
// frontend URL / secret are unset — Trigger becomes a no-op (so dev / misconfig
|
|
// never breaks the calling flow).
|
|
type Client struct {
|
|
base string
|
|
secret string
|
|
logger *zap.Logger
|
|
}
|
|
|
|
func New(frontendURL, secret string, logger *zap.Logger) *Client {
|
|
return &Client{
|
|
base: trimRightSlash(frontendURL),
|
|
secret: secret,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// Trigger fires a fire-and-forget POST to revalidate the given cache tag.
|
|
// Never blocks the caller and never errors out of band — use from request
|
|
// handlers (e.g. signup) on a long-lived server.
|
|
func (c *Client) Trigger(tag string) {
|
|
if c == nil || c.base == "" || c.secret == "" {
|
|
return
|
|
}
|
|
go func() {
|
|
if err := c.TriggerSync(tag); err != nil && c.logger != nil {
|
|
c.logger.Warn("revalidate ping failed", zap.String("tag", tag), zap.Error(err))
|
|
}
|
|
}()
|
|
}
|
|
|
|
// TriggerSync POSTs synchronously and returns any error. Use from short-lived
|
|
// CLIs (e.g. the indexer) that would exit before a background goroutine ran.
|
|
// No-op (nil) when the client / URL / secret is unset.
|
|
func (c *Client) TriggerSync(tag string) error {
|
|
if c == nil || c.base == "" || c.secret == "" {
|
|
return nil
|
|
}
|
|
endpoint := fmt.Sprintf("%s/api/revalidate?tag=%s&secret=%s",
|
|
c.base, url.QueryEscape(tag), url.QueryEscape(c.secret))
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
if resp.StatusCode >= 300 {
|
|
return fmt.Errorf("revalidate returned %d", resp.StatusCode)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func trimRightSlash(s string) string {
|
|
for len(s) > 0 && s[len(s)-1] == '/' {
|
|
s = s[:len(s)-1]
|
|
}
|
|
return s
|
|
}
|