feat(esi): integrate GORM for local ESI token storage and system ID resolution

This commit is contained in:
2025-08-09 19:18:09 +02:00
parent ca610000db
commit 3f9d315978
3 changed files with 177 additions and 65 deletions

View File

@@ -12,10 +12,15 @@ import (
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
const (
@@ -47,14 +52,80 @@ type ESISSO struct {
callbackOnce sync.Once
server *http.Server
db *gorm.DB
}
type SolarSystem struct {
SolarSystemID int64 `gorm:"column:solarSystemID;primaryKey"`
SolarSystemName string `gorm:"column:solarSystemName"`
}
func (SolarSystem) TableName() string { return "mapSolarSystems" }
type ESIToken struct {
ID uint `gorm:"primaryKey"`
CharacterID int64 `gorm:"index"`
CharacterName string
AccessToken string
RefreshToken string
ExpiresAt time.Time
UpdatedAt time.Time
CreatedAt time.Time
}
func NewESISSO(clientID string, redirectURI string, scopes []string) *ESISSO {
return &ESISSO{
s := &ESISSO{
clientID: clientID,
redirectURI: redirectURI,
scopes: scopes,
}
_ = s.initDB()
return s
}
func (s *ESISSO) initDB() error {
home, err := os.UserHomeDir()
if err != nil {
return err
}
dbPath := filepath.Join(home, ".industrializer", "sqlite-latest.sqlite")
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
if err != nil {
return err
}
// Ensure tokens table exists in same DB; safe to AutoMigrate
if err := db.AutoMigrate(&ESIToken{}); err != nil {
return err
}
s.db = db
return nil
}
func (s *ESISSO) saveToken() {
if s.db == nil || s.characterID == 0 {
return
}
t := ESIToken{}
s.db.Where("character_id = ?", s.characterID).First(&t)
t.CharacterID = s.characterID
t.CharacterName = s.characterName
t.AccessToken = s.accessToken
t.RefreshToken = s.refreshToken
t.ExpiresAt = s.expiresAt
s.db.Save(&t)
}
func (s *ESISSO) loadToken() {
if s.db == nil || s.characterID == 0 {
return
}
t := ESIToken{}
if err := s.db.Where("character_id = ?", s.characterID).First(&t).Error; err == nil {
s.accessToken = t.AccessToken
s.refreshToken = t.RefreshToken
s.expiresAt = t.ExpiresAt
}
}
// BuildAuthorizeURL prepares state and PKCE challenge and returns the browser URL
@@ -233,6 +304,7 @@ func (s *ESISSO) exchangeToken(ctx context.Context, code string) error {
name, cid := parseTokenCharacter(tr.AccessToken)
s.characterName = name
s.characterID = cid
s.saveToken()
return nil
}
@@ -279,6 +351,7 @@ func (s *ESISSO) refresh(ctx context.Context) error {
name, cid := parseTokenCharacter(tr.AccessToken)
s.characterName = name
s.characterID = cid
s.saveToken()
return nil
}
@@ -356,12 +429,21 @@ func (s *ESISSO) Status() SSOStatus {
}
}
// ResolveSystemIDByName searches ESI for a solar system by name and returns its ID
// ResolveSystemIDByName first checks local DB via GORM, then falls back to ESI
func (s *ESISSO) ResolveSystemIDByName(ctx context.Context, name string) (int64, error) {
name = strings.TrimSpace(name)
if name == "" {
return 0, errors.New("empty system name")
}
// 0) Try DB first
if s.db != nil {
var sys SolarSystem
if err := s.db.Where("solarSystemName = ?", name).First(&sys).Error; err == nil && sys.SolarSystemID != 0 {
fmt.Printf("DB: resolved %q -> %d\n", name, sys.SolarSystemID)
return sys.SolarSystemID, nil
}
}
// Fallback to ESI logic
// 1) Prefer universe/ids (name->id) for accuracy
type idsReq struct {
Names []string `json:"names"`