server/internal/handlers/github_releases.go
Rama main · réplica de lectura del repositorio original.
package handlers
import (
"encoding/json"
"io"
"log"
"net/http"
"time"
"blackbox/server/internal/models"
"gorm.io/gorm"
)
const githubReleasesURL = "https://api.github.com/repos/maxjb-xyz/blackbox/releases?per_page=100"
const githubCacheTTL = time.Hour
var githubHTTPClient = &http.Client{Timeout: 10 * time.Second}
func GetGitHubReleases(database *gorm.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Check cache
var cachedAt models.AppSetting
var cachedBody models.AppSetting
atErr := database.Where("key = ?", "github_releases_cached_at").First(&cachedAt).Error
bodyErr := database.Where("key = ?", "github_releases_body").First(&cachedBody).Error
if atErr == nil && bodyErr == nil {
if t, err := time.Parse(time.RFC3339, cachedAt.Value); err == nil {
if time.Since(t) < githubCacheTTL {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Cache-Control", "no-store")
if _, err := w.Write([]byte(cachedBody.Value)); err != nil {
log.Printf("GetGitHubReleases write cached: %v", err)
}
return
}
}
}
// Fetch from GitHub
req, err := http.NewRequestWithContext(r.Context(), http.MethodGet, githubReleasesURL, nil)
if err != nil {
writeError(w, http.StatusInternalServerError, "failed to build request")
return
}
resp, err := githubHTTPClient.Do(req)
if err != nil {
writeError(w, http.StatusBadGateway, "failed to reach GitHub")
return
}
defer func() {
if closeErr := resp.Body.Close(); closeErr != nil {
log.Printf("GetGitHubReleases close body: %v", closeErr)
}
}()
body, err := io.ReadAll(io.LimitReader(resp.Body, 512*1024))
if err != nil || resp.StatusCode != http.StatusOK {
writeError(w, http.StatusBadGateway, "GitHub API error")
return
}
// Validate JSON
var releases []json.RawMessage
if err := json.Unmarshal(body, &releases); err != nil {
writeError(w, http.StatusBadGateway, "invalid GitHub response")
return
}
// Cache
now := time.Now().UTC().Format(time.RFC3339)
if err := database.Save(&models.AppSetting{Key: "github_releases_cached_at", Value: now, UpdatedAt: time.Now()}).Error; err != nil {
log.Printf("GetGitHubReleases cache timestamp save: %v", err)
}
if err := database.Save(&models.AppSetting{Key: "github_releases_body", Value: string(body), UpdatedAt: time.Now()}).Error; err != nil {
log.Printf("GetGitHubReleases cache body save: %v", err)
}
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Cache-Control", "no-store")
if _, err := w.Write(body); err != nil {
log.Printf("GetGitHubReleases write: %v", err)
}
}
}