feat(esi): integrate GORM for local ESI token storage and system ID resolution
This commit is contained in:
86
esi_sso.go
86
esi_sso.go
@@ -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"`
|
||||
|
Reference in New Issue
Block a user