feat: Sentinela v0.2.0 — Brazilian Financial Data API in Go

- 20 Go source files, single 16MB binary
- SQLite + FTS5 full-text search (pure Go, no CGO)
- BCB integration: Selic, CDI, IPCA, USD/BRL, EUR/BRL
- CVM integration: 2,524 companies from registry
- Fiber v2 REST API with 42 handlers
- Auto-seeds on first run (~5s for BCB + CVM)
- Token bucket rate limiter, optional API key auth
- Periodic sync scheduler (configurable)
- Graceful shutdown, structured logging (slog)
- All endpoints tested with real data
This commit is contained in:
2026-02-10 11:15:54 -03:00
commit f7c8b446bf
28 changed files with 1763 additions and 0 deletions

52
internal/db/sqlite.go Normal file
View File

@@ -0,0 +1,52 @@
package db
import (
"database/sql"
"fmt"
"log/slog"
"os"
"path/filepath"
_ "modernc.org/sqlite"
)
type DB struct {
Conn *sql.DB
}
func New(dbPath string) (*DB, error) {
dir := filepath.Dir(dbPath)
if err := os.MkdirAll(dir, 0755); err != nil {
return nil, fmt.Errorf("create db dir: %w", err)
}
conn, err := sql.Open("sqlite", dbPath+"?_journal_mode=WAL&_busy_timeout=5000")
if err != nil {
return nil, fmt.Errorf("open db: %w", err)
}
conn.SetMaxOpenConns(1) // SQLite single-writer
if _, err := conn.Exec(schema); err != nil {
return nil, fmt.Errorf("run schema: %w", err)
}
slog.Info("database initialized", "path", dbPath)
return &DB{Conn: conn}, nil
}
func (d *DB) Close() error {
return d.Conn.Close()
}
func (d *DB) IsEmpty() bool {
var count int
d.Conn.QueryRow("SELECT COUNT(*) FROM companies").Scan(&count)
return count == 0
}
func (d *DB) IsMarketEmpty() bool {
var count int
d.Conn.QueryRow("SELECT COUNT(*) FROM selic_history").Scan(&count)
return count == 0
}