mirror of
https://github.com/nprasad2077/NBA_Go.git
synced 2026-09-22 14:05:13 +00:00
CDN
This commit is contained in:
+2
-1
@@ -47,4 +47,5 @@ docs/digital_ocean/digitaal ocean llms.txt
|
|||||||
|
|
||||||
.docs
|
.docs
|
||||||
|
|
||||||
NBA_Go
|
NBA_Go
|
||||||
|
.docs
|
||||||
@@ -32,6 +32,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/gofiber/fiber/v2/middleware/adaptor"
|
"github.com/gofiber/fiber/v2/middleware/adaptor"
|
||||||
|
"github.com/gofiber/fiber/v2/middleware/compress"
|
||||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
fiberswagger "github.com/swaggo/fiber-swagger"
|
fiberswagger "github.com/swaggo/fiber-swagger"
|
||||||
@@ -100,6 +101,11 @@ func main() {
|
|||||||
// — CORS Allow ALL origins (development) —
|
// — CORS Allow ALL origins (development) —
|
||||||
app.Use(cors.New())
|
app.Use(cors.New())
|
||||||
|
|
||||||
|
// — Global Brotli/Gzip Compression (shrinks transatlantic payload bytes by 70-85%) —
|
||||||
|
app.Use(compress.New(compress.Config{
|
||||||
|
Level: compress.LevelBestSpeed,
|
||||||
|
}))
|
||||||
|
|
||||||
// Middlewares
|
// Middlewares
|
||||||
app.Use(middleware.StructuredLogger())
|
app.Use(middleware.StructuredLogger())
|
||||||
app.Use(middleware.RateLimiter())
|
app.Use(middleware.RateLimiter())
|
||||||
|
|||||||
@@ -2,14 +2,15 @@ package routes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"gorm.io/gorm"
|
|
||||||
"github.com/nprasad2077/NBA_Go/controllers"
|
"github.com/nprasad2077/NBA_Go/controllers"
|
||||||
|
"github.com/nprasad2077/NBA_Go/utils/middleware"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterGameRoutes(app *fiber.App, db *gorm.DB) {
|
func RegisterGameRoutes(app *fiber.App, db *gorm.DB) {
|
||||||
// Create a new group for game-related endpoints
|
// Create a new group for game-related endpoints
|
||||||
api := app.Group("/api/games")
|
api := app.Group("/api/games")
|
||||||
|
|
||||||
// Register the GET endpoint to fetch games
|
// Register the GET endpoint to fetch games with immutable edge caching
|
||||||
api.Get("/", controllers.GetGames(db))
|
api.Get("/", middleware.EdgeCache(middleware.Immutable, "nba-games"), controllers.GetGames(db))
|
||||||
}
|
}
|
||||||
@@ -2,14 +2,16 @@ package routes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"gorm.io/gorm"
|
|
||||||
"github.com/nprasad2077/NBA_Go/controllers"
|
"github.com/nprasad2077/NBA_Go/controllers"
|
||||||
|
"github.com/nprasad2077/NBA_Go/utils/middleware"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterPlayerAdvancedRoutes(app *fiber.App, db *gorm.DB) {
|
func RegisterPlayerAdvancedRoutes(app *fiber.App, db *gorm.DB) {
|
||||||
api := app.Group("/api/playeradvancedstats")
|
api := app.Group("/api/playeradvancedstats")
|
||||||
|
|
||||||
// api.Get("/fetch", controllers.FetchPlayerAdvancedStats(db))
|
// Scraper trigger - never cached
|
||||||
api.Get("/scrape", controllers.ScrapePlayerAdvancedStats(db))
|
api.Get("/scrape", middleware.EdgeCache(middleware.NoCache), controllers.ScrapePlayerAdvancedStats(db))
|
||||||
api.Get("/", controllers.GetAllAdvancedPlayerStats(db))
|
// Public query endpoint - edge cached with stale-while-revalidate
|
||||||
|
api.Get("/", middleware.EdgeCache(middleware.SemiDynamic, "nba-player-advanced"), controllers.GetAllAdvancedPlayerStats(db))
|
||||||
}
|
}
|
||||||
@@ -3,13 +3,15 @@ package routes
|
|||||||
import (
|
import (
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/nprasad2077/NBA_Go/controllers"
|
"github.com/nprasad2077/NBA_Go/controllers"
|
||||||
|
"github.com/nprasad2077/NBA_Go/utils/middleware"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RegisterPlayerShotChartRoutes sets up the shot-chart endpoints
|
// RegisterPlayerShotChartRoutes sets up the shot-chart endpoints
|
||||||
func RegisterPlayerShotChartRoutes(app *fiber.App, db *gorm.DB) {
|
func RegisterPlayerShotChartRoutes(app *fiber.App, db *gorm.DB) {
|
||||||
api := app.Group("/api/playershotchart")
|
api := app.Group("/api/playershotchart")
|
||||||
// api.Get("/fetch", controllers.FetchPlayerShotChartAPI(db))
|
// Scraper trigger - never cached
|
||||||
api.Get("/scrape", controllers.ScrapePlayerShotChart(db))
|
api.Get("/scrape", middleware.EdgeCache(middleware.NoCache), controllers.ScrapePlayerShotChart(db))
|
||||||
api.Get("/", controllers.GetPlayerShotChart(db))
|
// Public shot chart query endpoint - immutable 30-day edge cache
|
||||||
|
api.Get("/", middleware.EdgeCache(middleware.Immutable, "nba-shot-charts"), controllers.GetPlayerShotChart(db))
|
||||||
}
|
}
|
||||||
@@ -3,13 +3,15 @@ package routes
|
|||||||
import (
|
import (
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/nprasad2077/NBA_Go/controllers"
|
"github.com/nprasad2077/NBA_Go/controllers"
|
||||||
|
"github.com/nprasad2077/NBA_Go/utils/middleware"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RegisterPlayerTotalRoutes(app *fiber.App, db *gorm.DB) {
|
func RegisterPlayerTotalRoutes(app *fiber.App, db *gorm.DB) {
|
||||||
api := app.Group("/api/playertotals")
|
api := app.Group("/api/playertotals")
|
||||||
|
|
||||||
// api.Get("/fetch", controllers.FetchPlayerTotalStats(db))
|
// Scraper trigger - never cached
|
||||||
api.Get("/scrape", controllers.ScrapePlayerTotalStats(db))
|
api.Get("/scrape", middleware.EdgeCache(middleware.NoCache), controllers.ScrapePlayerTotalStats(db))
|
||||||
api.Get("/", controllers.GetPlayerTotalStats(db))
|
// Public query endpoint - edge cached with stale-while-revalidate
|
||||||
|
api.Get("/", middleware.EdgeCache(middleware.SemiDynamic, "nba-player-totals"), controllers.GetPlayerTotalStats(db))
|
||||||
}
|
}
|
||||||
+13700
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,77 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CacheStrategy defines caching profiles for different API data mutability tiers
|
||||||
|
type CacheStrategy string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Immutable: For historical completed games, past seasons, and shot charts.
|
||||||
|
// Edge TTL: 30 Days | Browser TTL: 1 Day
|
||||||
|
Immutable CacheStrategy = "immutable"
|
||||||
|
|
||||||
|
// SemiDynamic: For daily leaderboards, player season averages, and standings.
|
||||||
|
// Edge TTL: 1 Day | Browser TTL: 1 Minute | Stale-While-Revalidate: 10 Minutes
|
||||||
|
SemiDynamic CacheStrategy = "semi_dynamic"
|
||||||
|
|
||||||
|
// Realtime: For active game telemetry, live quarter stats, and fast-updating data.
|
||||||
|
// Edge TTL: 10 Seconds | Browser TTL: 5 Seconds | Stale-While-Revalidate: 30 Seconds
|
||||||
|
Realtime CacheStrategy = "realtime"
|
||||||
|
|
||||||
|
// NoCache: For transactional mutations, scrapers, and admin endpoints.
|
||||||
|
NoCache CacheStrategy = "no_cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EdgeCache injects standardized Cloudflare & HTTP caching headers
|
||||||
|
func EdgeCache(strategy CacheStrategy, tags ...string) fiber.Handler {
|
||||||
|
return func(c *fiber.Ctx) error {
|
||||||
|
// Only cache idempotent read requests (GET / HEAD)
|
||||||
|
if c.Method() != fiber.MethodGet && c.Method() != fiber.MethodHead {
|
||||||
|
c.Set("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate")
|
||||||
|
c.Set("Pragma", "no-cache")
|
||||||
|
c.Set("Expires", "0")
|
||||||
|
return c.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch strategy {
|
||||||
|
case Immutable:
|
||||||
|
// Served directly by Cloudflare US Edge for 30 days without hitting Europe origin
|
||||||
|
c.Set("Cache-Control", "public, max-age=86400, s-maxage=2592000, immutable")
|
||||||
|
case SemiDynamic:
|
||||||
|
// Instant edge delivery; background revalidation over transatlantic backbone
|
||||||
|
c.Set("Cache-Control", "public, max-age=60, s-maxage=86400, stale-while-revalidate=600")
|
||||||
|
case Realtime:
|
||||||
|
c.Set("Cache-Control", "public, max-age=5, s-maxage=10, stale-while-revalidate=30")
|
||||||
|
case NoCache:
|
||||||
|
c.Set("Cache-Control", "no-store, no-cache, must-revalidate")
|
||||||
|
return c.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cloudflare Cache-Tag header for instantaneous programmatic purge by entity
|
||||||
|
if len(tags) > 0 {
|
||||||
|
var tagHeader string
|
||||||
|
for i, tag := range tags {
|
||||||
|
if i > 0 {
|
||||||
|
tagHeader += ","
|
||||||
|
}
|
||||||
|
tagHeader += tag
|
||||||
|
}
|
||||||
|
c.Set("Cache-Tag", tagHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set("Vary", "Accept-Encoding, Origin")
|
||||||
|
return c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ComputeETag generates a weak ETag based on response body bytes for 304 Not Modified optimization
|
||||||
|
func ComputeETag(data []byte) string {
|
||||||
|
hash := sha256.Sum256(data)
|
||||||
|
return fmt.Sprintf(`W/"%s"`, hex.EncodeToString(hash[:8]))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user