From da5133eef8c5b3a1037099afec491dd888a20d84 Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Fri, 10 Oct 2025 22:28:05 +0200 Subject: [PATCH] Hallucinate a whole lot of shit... Too much shit... --- esi/sso.go | 65 ++++++++++++++------------------------ main.go | 9 ++++++ repositories/base.go | 30 ++++++++++++++++++ repositories/cache.go | 44 ++++++++++++++++++++++++++ repositories/character.go | 44 ++++++++++++++++++++++++++ repositories/database.go | 64 +++++++++++++++++++++++++++++++++++++ repositories/interfaces.go | 9 ++++++ routes/routes.go | 2 +- types/character.go | 8 +++++ 9 files changed, 233 insertions(+), 42 deletions(-) create mode 100644 repositories/base.go create mode 100644 repositories/cache.go create mode 100644 repositories/character.go create mode 100644 repositories/database.go create mode 100644 repositories/interfaces.go diff --git a/esi/sso.go b/esi/sso.go index 42cc0f5..b2d9df3 100644 --- a/esi/sso.go +++ b/esi/sso.go @@ -16,12 +16,13 @@ import ( "sync" "time" + "go-eve-pi/repositories" "go-eve-pi/types" - "gorm.io/gorm" logger "git.site.quack-lab.dev/dave/cylogger" "github.com/fasthttp/router" "github.com/valyala/fasthttp" + "gorm.io/gorm" ) const ( @@ -29,22 +30,18 @@ const ( issuerTokenURL = "https://login.eveonline.com/v2/oauth/token" ) -// DB interface for database operations -type DB interface { - GetCharacterByName(characterName string) (*types.Character, error) - SaveCharacter(character *types.Character) error - AutoMigrate(dst ...interface{}) error -} +// CharacterRepositoryInterface defines the interface for character operations +type CharacterRepositoryInterface = repositories.CharacterRepositoryInterface type SSO struct { - clientID string - redirectURI string - scopes []string - db DB - mu sync.Mutex - router *router.Router - state string - callbackChan chan struct { + clientID string + redirectURI string + scopes []string + characterRepo CharacterRepositoryInterface + mu sync.Mutex + router *router.Router + state string + callbackChan chan struct { code string state string err error @@ -52,19 +49,14 @@ type SSO struct { } // NewSSO creates a new SSO instance -func NewSSO(clientID, redirectURI string, scopes []string, db DB) (*SSO, error) { +func NewSSO(clientID, redirectURI string, scopes []string, characterRepo CharacterRepositoryInterface) (*SSO, error) { logger.Info("Creating new SSO instance for clientID %s with redirectURI %s and scopes %v", clientID, redirectURI, scopes) s := &SSO{ - clientID: clientID, - redirectURI: redirectURI, - scopes: scopes, - db: db, - } - - if err := s.initDB(); err != nil { - logger.Error("Failed to initialize SSO database %v", err) - return nil, err + clientID: clientID, + redirectURI: redirectURI, + scopes: scopes, + characterRepo: characterRepo, } logger.Info("SSO instance created successfully") @@ -78,16 +70,7 @@ func (s *SSO) SetRouter(r *router.Router) { s.setupCallbackHandler() } -func (s *SSO) initDB() error { - logger.Debug("Initializing SSO database schema") - err := s.db.AutoMigrate(&types.Character{}) - if err != nil { - logger.Error("Failed to migrate Token table %v", err) - return err - } - logger.Debug("SSO database schema initialized successfully") - return nil -} +// initDB is no longer needed as migrations are handled by the repository // GetCharacter returns a valid character object for the given character name // If no token exists, it will start the OAuth flow @@ -98,7 +81,7 @@ func (s *SSO) GetCharacter(ctx context.Context, characterName string) (types.Cha defer s.mu.Unlock() // Try to get existing token from DB - char, err := s.db.GetCharacterByName(characterName) + char, err := s.characterRepo.GetCharacterByName(characterName) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { logger.Info("No existing token found for character %s, starting authentication flow", characterName) @@ -107,7 +90,7 @@ func (s *SSO) GetCharacter(ctx context.Context, characterName string) (types.Cha return types.Character{}, err } // After authentication, fetch the token from DB - char, err = s.db.GetCharacterByName(characterName) + char, err = s.characterRepo.GetCharacterByName(characterName) if err != nil { return types.Character{}, err } @@ -124,7 +107,7 @@ func (s *SSO) GetCharacter(ctx context.Context, characterName string) (types.Cha if eveCharID > 0 { char.ID = eveCharID logger.Debug("Updating character %s with ID: %d", characterName, eveCharID) - if err := s.db.SaveCharacter(char); err != nil { + if err := s.characterRepo.SaveCharacter(char); err != nil { logger.Warning("Failed to update character %s with ID: %v", characterName, err) } } else { @@ -143,7 +126,7 @@ func (s *SSO) GetCharacter(ctx context.Context, characterName string) (types.Cha return types.Character{}, err } // After re-authentication, fetch the token from DB - char, err = s.db.GetCharacterByName(characterName) + char, err = s.characterRepo.GetCharacterByName(characterName) if err != nil { return types.Character{}, err } @@ -213,7 +196,7 @@ func (s *SSO) startAuthFlow(ctx context.Context, characterName string) error { _, eveCharID := parseTokenCharacter(char.AccessToken) char.ID = eveCharID logger.Debug("Saving token to database for character %s (EVE ID: %d)", characterName, eveCharID) - if err := s.db.SaveCharacter(char); err != nil { + if err := s.characterRepo.SaveCharacter(char); err != nil { return err } @@ -397,7 +380,7 @@ func (s *SSO) refreshToken(ctx context.Context, char *types.Character) error { } logger.Debug("Saving refreshed token to database for character %s", char.CharacterName) - if err := s.db.SaveCharacter(char); err != nil { + if err := s.characterRepo.SaveCharacter(char); err != nil { return err } diff --git a/main.go b/main.go index 902bbd6..1494010 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( "go-eve-pi/esi" "go-eve-pi/options" + "go-eve-pi/repositories" "go-eve-pi/routes" wh "go-eve-pi/webhook" @@ -36,11 +37,19 @@ func main() { logger.Init(logger.ParseLevel(options.GlobalOptions.LogLevel)) logger.Info("Starting Eve PI") + // Initialize database with repositories + database, err := repositories.NewDatabase() + if err != nil { + logger.Error("Failed to initialize database: %v", err) + return + } + // Create SSO instance sso, err := esi.NewSSO( options.GlobalOptions.ClientID, options.GlobalOptions.RedirectURI, options.GlobalOptions.Scopes, + database.Character, ) if err != nil { logger.Error("Failed to create SSO instance %v", err) diff --git a/repositories/base.go b/repositories/base.go new file mode 100644 index 0000000..643e7d2 --- /dev/null +++ b/repositories/base.go @@ -0,0 +1,30 @@ +package repositories + +import ( + "gorm.io/gorm" +) + +// BaseRepository provides common database operations +type BaseRepository struct { + db *gorm.DB +} + +// NewBaseRepository creates a new base repository +func NewBaseRepository(db *gorm.DB) *BaseRepository { + return &BaseRepository{db: db} +} + +// DB returns the underlying gorm.DB instance +func (r *BaseRepository) DB() *gorm.DB { + return r.db +} + +// Raw executes raw SQL +func (r *BaseRepository) Raw(sql string, args ...any) *gorm.DB { + return r.db.Raw(sql, args...) +} + +// AutoMigrate runs auto migration +func (r *BaseRepository) AutoMigrate(dst ...interface{}) error { + return r.db.AutoMigrate(dst...) +} diff --git a/repositories/cache.go b/repositories/cache.go new file mode 100644 index 0000000..bef4068 --- /dev/null +++ b/repositories/cache.go @@ -0,0 +1,44 @@ +package repositories + +import ( + "go-eve-pi/types" + + logger "git.site.quack-lab.dev/dave/cylogger" + "gorm.io/gorm" +) + +// CacheRepository handles cache-related database operations +type CacheRepository struct { + *BaseRepository +} + +// NewCacheRepository creates a new cache repository +func NewCacheRepository(db *gorm.DB) *CacheRepository { + return &CacheRepository{ + BaseRepository: NewBaseRepository(db), + } +} + +// GetCacheEntry retrieves a cache entry by URL hash +func (r *CacheRepository) GetCacheEntry(urlHash string) (*types.CacheEntry, error) { + logger.Debug("Fetching cache entry for hash: %s", urlHash) + var entry types.CacheEntry + err := r.db.Where("url_hash = ?", urlHash).First(&entry).Error + if err != nil { + logger.Debug("No cache entry found for hash %s: %v", urlHash, err) + return nil, err + } + logger.Debug("Cache entry found for hash %s", urlHash) + return &entry, nil +} + +// SaveCacheEntry saves a cache entry to the database +func (r *CacheRepository) SaveCacheEntry(entry *types.CacheEntry) error { + logger.Debug("Saving cache entry for hash: %s", entry.URLHash) + err := r.db.Save(entry).Error + if err != nil { + return err + } + logger.Debug("Cache entry saved successfully for hash %s", entry.URLHash) + return nil +} diff --git a/repositories/character.go b/repositories/character.go new file mode 100644 index 0000000..c351e26 --- /dev/null +++ b/repositories/character.go @@ -0,0 +1,44 @@ +package repositories + +import ( + "go-eve-pi/types" + + logger "git.site.quack-lab.dev/dave/cylogger" + "gorm.io/gorm" +) + +// CharacterRepository handles character-related database operations +type CharacterRepository struct { + *BaseRepository +} + +// NewCharacterRepository creates a new character repository +func NewCharacterRepository(db *gorm.DB) *CharacterRepository { + return &CharacterRepository{ + BaseRepository: NewBaseRepository(db), + } +} + +// GetCharacterByName retrieves a character by name +func (r *CharacterRepository) GetCharacterByName(characterName string) (*types.Character, error) { + logger.Debug("Fetching token for character %s from database", characterName) + var char types.Character + err := r.db.Where("character_name = ?", characterName).First(&char).Error + if err != nil { + logger.Debug("No token found for character %s: %v", characterName, err) + return nil, err + } + logger.Debug("Token found for character %s, expires at %v", characterName, char.ExpiresAt) + return &char, nil +} + +// SaveCharacter saves a character to the database +func (r *CharacterRepository) SaveCharacter(character *types.Character) error { + logger.Debug("Saving token for character %s to database", character.CharacterName) + err := r.db.Save(character).Error + if err != nil { + return err + } + logger.Debug("Token saved successfully for character %s", character.CharacterName) + return nil +} diff --git a/repositories/database.go b/repositories/database.go new file mode 100644 index 0000000..1ad4996 --- /dev/null +++ b/repositories/database.go @@ -0,0 +1,64 @@ +package repositories + +import ( + "os" + "path/filepath" + + "go-eve-pi/options" + "go-eve-pi/types" + + "gorm.io/driver/sqlite" + "gorm.io/gorm" + + logger "git.site.quack-lab.dev/dave/cylogger" +) + +// Database manages all repositories and provides a unified interface +type Database struct { + *gorm.DB + Character *CharacterRepository + Cache *CacheRepository +} + +// NewDatabase creates a new database instance with all repositories +func NewDatabase() (*Database, error) { + // Get database path from options + dbPath := options.GlobalOptions.DBPath + if dbPath == "" { + dbPath = "eve-pi.db" + } + + // Ensure directory exists + dir := filepath.Dir(dbPath) + if err := os.MkdirAll(dir, 0755); err != nil { + return nil, err + } + + // Connect to database + db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{}) + if err != nil { + logger.Error("Failed to connect to database: %v", err) + return nil, err + } + + logger.Info("Connected to database: %s", dbPath) + + // Create repositories + characterRepo := NewCharacterRepository(db) + cacheRepo := NewCacheRepository(db) + + database := &Database{ + DB: db, + Character: characterRepo, + Cache: cacheRepo, + } + + // Run migrations + if err := database.AutoMigrate(&types.Character{}, &types.CacheEntry{}); err != nil { + logger.Error("Failed to run database migrations: %v", err) + return nil, err + } + + logger.Info("Database initialized successfully") + return database, nil +} diff --git a/repositories/interfaces.go b/repositories/interfaces.go new file mode 100644 index 0000000..d46a9bc --- /dev/null +++ b/repositories/interfaces.go @@ -0,0 +1,9 @@ +package repositories + +import "go-eve-pi/types" + +// CharacterRepositoryInterface defines the interface for character operations +type CharacterRepositoryInterface interface { + GetCharacterByName(characterName string) (*types.Character, error) + SaveCharacter(character *types.Character) error +} diff --git a/routes/routes.go b/routes/routes.go index dd061fd..7bf611e 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -65,7 +65,7 @@ func (rh *RouteHandler) handleLogin(ctx *fasthttp.RequestCtx) { return } logger.Info("Login requested for character %s", charNameStr) - + // Trigger the auth flow (will register callback if needed) char, err := rh.SSO.GetCharacter(context.Background(), charNameStr) if err != nil { diff --git a/types/character.go b/types/character.go index acd5d5a..a87d6b9 100644 --- a/types/character.go +++ b/types/character.go @@ -12,3 +12,11 @@ type Character struct { UpdatedAt time.Time CreatedAt time.Time } + +// CacheEntry represents a cached API response +type CacheEntry struct { + ID uint `gorm:"primaryKey"` + URLHash string `gorm:"uniqueIndex"` + Response string `gorm:"type:text"` + CachedAt time.Time `gorm:"index"` +}