main.go
Rama main · réplica de lectura del repositorio original.
package main
import (
"context"
"fmt"
"log"
"os"
"time"
cache "github.com/Code-Hex/go-generics-cache"
"github.com/gofiber/contrib/otelfiber"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/compress"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/limiter"
"github.com/gofiber/fiber/v2/middleware/pprof"
"github.com/gofiber/fiber/v2/middleware/recover"
"github.com/emanuelef/gh-repo-stats-server/news"
"github.com/emanuelef/gh-repo-stats-server/otel_instrumentation"
"github.com/emanuelef/gh-repo-stats-server/routes"
"github.com/emanuelef/gh-repo-stats-server/session"
"github.com/emanuelef/gh-repo-stats-server/starhistory"
"github.com/emanuelef/gh-repo-stats-server/types"
"github.com/emanuelef/gh-repo-stats-server/utils"
"github.com/emanuelef/github-repo-activity-stats/repostats"
"github.com/emanuelef/github-repo-activity-stats/stats"
_ "github.com/joho/godotenv/autoload"
)
var (
currentSessions session.SessionsLock
allStarsRequestStats types.RequestStats
)
// getRecentStarsByHourHandler handles API requests for hourly stars
func main() {
ctx := context.Background()
tp, exp, err := otel_instrumentation.InitializeGlobalTracerProvider(ctx)
// Handle shutdown to ensure all sub processes are closed correctly and telemetry is exported
defer func() {
_ = exp.Shutdown(ctx)
_ = tp.Shutdown(ctx)
}()
if err != nil {
log.Fatalf("failed to initialize OpenTelemetry: %e", err)
}
cacheOverall := cache.New[string, *stats.RepoStats]()
cacheStars := cache.New[string, types.StarsWithStatsResponse]()
cacheIssues := cache.New[string, types.IssuesWithStatsResponse]()
cacheForks := cache.New[string, types.ForksWithStatsResponse]()
cachePRs := cache.New[string, types.PRsWithStatsResponse]()
cacheCommits := cache.New[string, types.CommitsWithStatsResponse]()
cacheContributors := cache.New[string, types.ContributorsWithStatsResponse]()
cacheNewRepos := cache.New[string, types.NewReposWithStatsResponse]()
cacheNewPRs := cache.New[string, types.NewPRsWithStatsResponse]()
cacheHackerNews := cache.New[string, []news.Article]()
cacheReddit := cache.New[string, []news.ArticleData]()
cacheYouTube := cache.New[string, []news.YTVideoMetadata]()
cacheReleases := cache.New[string, []stats.ReleaseInfo]()
cacheShowHN := cache.New[string, []news.ShowHNPost]()
cacheRedditGitHub := cache.New[string, []news.RedditGitHubPost]()
cacheRecentStarsByHour := cache.New[string, []types.HourlyStars]()
cacheGitHubMentions := cache.New[string, types.GitHubMentionsResponse]()
onGoingStars := make(map[string]bool)
onGoingIssues := make(map[string]bool)
onGoingForks := make(map[string]bool)
onGoingPRs := make(map[string]bool)
onGoingCommits := make(map[string]bool)
onGoingContributors := make(map[string]bool)
onGoingNewRepos := make(map[string]bool)
onGoingNewPRs := make(map[string]bool)
ghStatClients := make(map[string]*repostats.ClientGQL)
ghStatClients["PAT"] = utils.NewClientWithPAT(os.Getenv("PAT"))
if pat2 := os.Getenv("PAT2"); pat2 != "" {
ghStatClients["PAT2"] = utils.NewClientWithPAT(pat2)
}
// Star history comes from the REST API rather than GraphQL: GitHub
// restricted the stargazers connection the library walks, and the
// restriction is silent (HTTP 200 with an empty edge list), so the old path
// charted a flat zero line instead of failing. REST also spends a separate
// rate-limit bucket from the GraphQL queries the other features use.
starClients := make(map[string]*starhistory.Client)
starClients["PAT"] = starhistory.New(os.Getenv("PAT"))
if pat2 := os.Getenv("PAT2"); pat2 != "" {
starClients["PAT2"] = starhistory.New(pat2)
}
app := fiber.New()
app.Use(pprof.New())
app.Use(otelfiber.Middleware(otelfiber.WithNext(func(c *fiber.Ctx) bool {
return c.Path() == "/health" || c.Path() == "/sse"
})))
rateLimiter := limiter.New(limiter.Config{
Max: 120, // Maximum number of requests allowed per hour
Expiration: 1 * time.Hour, // Duration for the rate limit window
KeyGenerator: func(c *fiber.Ctx) string {
ip := c.Get("X-Forwarded-For")
// If X-Forwarded-For is empty, fallback to RemoteIP
if ip == "" {
ip = "unknown"
}
return ip
},
})
rateLimiterFeed := limiter.New(limiter.Config{
Max: 200, // Maximum number of requests allowed per hour
Expiration: 1 * time.Hour, // Duration for the rate limit window
KeyGenerator: func(c *fiber.Ctx) string {
ip := c.Get("X-Forwarded-For")
// If X-Forwarded-For is empty, fallback to RemoteIP
if ip == "" {
ip = "unknown"
}
return ip
},
})
app.Use(recover.New())
app.Use(cors.New())
app.Use(compress.New())
app.Use("/allStars", rateLimiter)
app.Use("/youtube", rateLimiterFeed)
app.Use("/showhn", rateLimiterFeed)
app.Use("/redditrepos", rateLimiterFeed)
app.Use("/reddit", rateLimiterFeed)
app.Use("/hackernews", rateLimiterFeed)
app.Use("/ghmentions", rateLimiterFeed)
app.Use("/allReleases", rateLimiter)
// Initialize caches struct
caches := &routes.Caches{
Overall: cacheOverall,
Stars: cacheStars,
Issues: cacheIssues,
Forks: cacheForks,
PRs: cachePRs,
Commits: cacheCommits,
Contributors: cacheContributors,
NewRepos: cacheNewRepos,
NewPRs: cacheNewPRs,
HackerNews: cacheHackerNews,
Reddit: cacheReddit,
YouTube: cacheYouTube,
Releases: cacheReleases,
ShowHN: cacheShowHN,
RedditGitHub: cacheRedditGitHub,
RecentStarsByHour: cacheRecentStarsByHour,
GitHubMentions: cacheGitHubMentions,
}
// Initialize ongoing operations map
onGoingMaps := &routes.OnGoingMaps{
Stars: onGoingStars,
Issues: onGoingIssues,
Forks: onGoingForks,
PRs: onGoingPRs,
Commits: onGoingCommits,
Contributors: onGoingContributors,
NewRepos: onGoingNewRepos,
NewPRs: onGoingNewPRs,
}
// Register system routes
routes.RegisterSystemRoutes(app)
// serve the static files from the website/dist folder
app.Static("/", "./website/dist")
// serve the assets files from the website/dist/assets folder
app.Static("/daily-stars-explorer/assets", "./website/dist/assets")
// Register news routes
routes.RegisterNewsRoutes(app, caches)
// Register GitHub stats routes
routes.RegisterGitHubStatsRoutes(app, ctx, ghStatClients, caches)
// Register cache routes
routes.RegisterCacheRoutes(app, caches, onGoingStars)
// Register request stats routes
routes.RegisterRequestStatsRoutes(app, &allStarsRequestStats)
// Register stars routes
routes.RegisterStarsRoutes(app, ctx, ghStatClients, starClients, caches, onGoingStars, ¤tSessions, &allStarsRequestStats)
// Register repository activity routes
routes.RegisterRepoActivityRoutes(app, ctx, ghStatClients, caches, onGoingMaps, ¤tSessions)
// Register SSE routes
routes.RegisterSSERoutes(app, ¤tSessions)
// Register limits routes
routes.RegisterLimitsRoutes(app, ctx, ghStatClients)
host := utils.GetEnv("HOST", "0.0.0.0")
port := utils.GetEnv("PORT", "8080")
hostAddress := fmt.Sprintf("%s:%s", host, port)
err = app.Listen(hostAddress)
if err != nil {
log.Panic(err)
}
}