Files
sentinela-go/cmd/sentinela/main.go
Rainbow a2b0db8f3f feat: tiered API plans (Free/Bronze/Gold/Platinum)
- Free: $0, 30 req/min, market data only
- Bronze: $29/mo, 100 req/min, + companies & search
- Gold: $99/mo, 500 req/min, + filings & historical
- Platinum: $299/mo, 2000 req/min, all features + priority
- Plan-aware rate limiting per API key (or per IP for free)
- Usage tracking with daily aggregation
- GET /api/v1/plans — plan listing
- POST /api/v1/plans/register — instant free API key
- GET /api/v1/plans/usage — usage stats
- /pricing — dark-themed HTML pricing page
- X-RateLimit-* and X-Plan headers on every response
- Restricted endpoints return upgrade prompt
- Updated OpenAPI spec with security scheme
- 53 handlers, compiles clean
2026-02-10 12:55:45 -03:00

83 lines
2.0 KiB
Go

package main
import (
"fmt"
"log/slog"
"os"
"os/signal"
"syscall"
"time"
"github.com/sentinela-go/internal/api"
"github.com/sentinela-go/internal/config"
"github.com/sentinela-go/internal/db"
"github.com/sentinela-go/internal/fetcher"
)
func main() {
cfg := config.Load()
// Setup structured logging
level := slog.LevelInfo
if cfg.LogLevel == "debug" {
level = slog.LevelDebug
}
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: level})))
slog.Info("starting Sentinela", "port", cfg.Port)
// Initialize database
database, err := db.New(cfg.DatabasePath)
if err != nil {
slog.Error("failed to initialize database", "error", err)
os.Exit(1)
}
defer database.Close()
// Seed data if empty
if database.IsMarketEmpty() {
slog.Info("database is empty, seeding BCB data...")
if err := fetcher.FetchAllBCB(database); err != nil {
slog.Error("failed to seed BCB data", "error", err)
}
}
if database.IsEmpty() {
slog.Info("no companies found, seeding CVM data...")
if err := fetcher.FetchAllCVM(database); err != nil {
slog.Error("failed to seed CVM data", "error", err)
}
}
// Seed test API key
if _, err := database.CreateAPIKeyWithValue("sentinela-test-key-2026", "Test User", "test@sentinela.dev", "platinum"); err != nil {
slog.Warn("failed to seed test API key", "error", err)
}
// Start scheduler
syncInterval, err := time.ParseDuration(cfg.SyncInterval)
if err != nil {
syncInterval = 30 * time.Minute
}
stopChan := make(chan struct{})
go fetcher.StartScheduler(database, syncInterval, stopChan)
// Create and start server
app := api.NewServer(cfg, database)
// Graceful shutdown
go func() {
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
slog.Info("shutting down...")
close(stopChan)
app.Shutdown()
}()
addr := fmt.Sprintf(":%d", cfg.Port)
if err := app.Listen(addr); err != nil {
slog.Error("server error", "error", err)
os.Exit(1)
}
}