123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- package dbstore
- import (
- "context"
- "database/sql"
- "fmt"
- "os"
- "git.linuxforward.com/byop/byop-engine/models"
- _ "github.com/mattn/go-sqlite3" // Import SQLite driver for compatibility
- "gorm.io/driver/sqlite"
- "gorm.io/gorm"
- "gorm.io/gorm/logger"
- )
- // Store defines the interface for all database operations.
- // This will include methods for all models (User, Client, App, Component, Deployment, Ticket, etc.)
- type Store interface {
- // User methods
- CreateUser(ctx context.Context, user *models.User) error
- GetUserByEmail(ctx context.Context, email string) (*models.User, error)
- GetUserByID(ctx context.Context, id uint) (*models.User, error)
- GetUsers(ctx context.Context) ([]*models.User, error)
- UpdateUser(ctx context.Context, user *models.User) error
- DeleteUser(ctx context.Context, id uint) error
- CreateDefaultAdmin(ctx context.Context) error
- GetByUsername(ctx context.Context, username string) (*models.User, error)
- // Client methods
- CreateClient(ctx context.Context, client *models.Client) error
- GetAllClients(ctx context.Context) ([]*models.Client, error)
- GetClientByID(ctx context.Context, id uint) (*models.Client, error)
- UpdateClient(ctx context.Context, client *models.Client) error
- DeleteClient(ctx context.Context, id uint) error
- // App methods
- CreateApp(ctx context.Context, app *models.App) error
- GetAppByID(ctx context.Context, id uint) (*models.App, error)
- GetAppsByUserID(ctx context.Context, userID uint) ([]*models.App, error)
- UpdateApp(ctx context.Context, app *models.App) error
- DeleteApp(ctx context.Context, id uint) error
- UpdateAppStatus(ctx context.Context, appID uint, status string, message string) error
- UpdateAppPreview(ctx context.Context, appID uint, previewID uint, previewURL string) error
- GetAllApps(ctx context.Context) ([]*models.App, error)
- UpdateAppCurrentImage(ctx context.Context, appID uint, imageTag string, imageURI string) error
- // Component methods
- CreateComponent(ctx context.Context, component *models.Component) error
- GetComponentByID(ctx context.Context, id uint) (*models.Component, error)
- GetComponentsByUserID(ctx context.Context, userID uint) ([]*models.Component, error)
- GetAllComponents(ctx context.Context) ([]*models.Component, error)
- UpdateComponent(ctx context.Context, component *models.Component) error
- UpdateComponentStatus(ctx context.Context, componentID uint, status string, errorMsg string) error
- DeleteComponent(ctx context.Context, id uint) error
- // Deployment methods
- CreateDeployment(ctx context.Context, deployment *models.Deployment) error
- GetDeploymentByID(ctx context.Context, id uint) (*models.Deployment, error)
- GetDeploymentsByClientID(ctx context.Context, clientID uint) ([]*models.Deployment, error)
- GetAllDeployments(ctx context.Context) ([]*models.Deployment, error)
- UpdateDeployment(ctx context.Context, deployment *models.Deployment) error
- DeleteDeployment(ctx context.Context, id uint) error
- // Preview methods
- CreatePreview(ctx context.Context, preview *models.Preview) error
- GetPreviewByID(ctx context.Context, id uint) (*models.Preview, error)
- GetPreviewByAppID(ctx context.Context, appID uint) (*models.Preview, error)
- UpdatePreview(ctx context.Context, preview *models.Preview) error
- DeletePreview(ctx context.Context, id uint) error
- UpdatePreviewVPS(ctx context.Context, previewID uint, vpsID string, ipAddress string, previewURL string) error
- UpdatePreviewStatus(ctx context.Context, previewID uint, status string, errorMsg string) error
- UpdatePreviewBuildLogs(ctx context.Context, previewID uint, logs string) error
- UpdatePreviewDeployLogs(ctx context.Context, previewID uint, logs string) error
- GetAllPreviews(ctx context.Context) ([]*models.Preview, error)
- GetPreviewsByStatus(ctx context.Context, status string) ([]*models.Preview, error)
- GetPreviewsByAppID(ctx context.Context, appID uint) ([]*models.Preview, error)
- // Ticket methods
- CreateTicket(ctx context.Context, ticket *models.Ticket) error
- GetTicketByID(ctx context.Context, id uint) (*models.Ticket, error)
- GetTickets(ctx context.Context) ([]*models.Ticket, error)
- UpdateTicket(ctx context.Context, ticket *models.Ticket) error
- // TicketComment methods
- CreateTicketComment(ctx context.Context, comment *models.TicketComment) error
- GetTicketComments(ctx context.Context, ticketID uint) ([]*models.TicketComment, error)
- // BuildJob methods
- CreateBuildJob(ctx context.Context, job *models.BuildJob) error
- GetBuildJobByID(ctx context.Context, id uint) (*models.BuildJob, error)
- UpdateBuildJob(ctx context.Context, job *models.BuildJob) error
- UpdateBuildJobStatus(ctx context.Context, id uint, status models.BuildStatus, errorMessage string) error
- AppendBuildJobLog(ctx context.Context, id uint, logMessage string) error
- GetQueuedBuildJobs(ctx context.Context, limit int) ([]*models.BuildJob, error)
- GetBuildJobsByAppID(ctx context.Context, appID uint, page, pageSize int) ([]*models.BuildJob, int64, error)
- // General DB methods
- GetDB() *sql.DB
- GetGormDB() *gorm.DB
- Close() error
- }
- // SQLiteStore implements the Store interface for SQLite using GORM
- type SQLiteStore struct {
- db *gorm.DB
- rawDB *sql.DB // Keep for backward compatibility and specific raw SQL operations
- dsn string
- }
- // NewSQLiteStore initializes a new SQLiteStore with GORM
- func NewSQLiteStore(dataSourceName string) (*SQLiteStore, error) {
- // First check if the database file exists
- isNewDb := !fileExists(dataSourceName)
- if isNewDb {
- // Create the database file if it doesn't exist
- file, err := os.Create(dataSourceName)
- if err != nil {
- return nil, fmt.Errorf("failed to create SQLite database file: %w", err)
- }
- defer file.Close()
- }
- // Open GORM database
- gormDB, err := gorm.Open(sqlite.Open(dataSourceName), &gorm.Config{
- Logger: logger.Default.LogMode(logger.Silent), // Change to logger.Info for debugging
- })
- if err != nil {
- return nil, fmt.Errorf("failed to open database with GORM: %w", err)
- }
- // Get underlying SQL DB for backward compatibility
- rawDB, err := gormDB.DB()
- if err != nil {
- return nil, fmt.Errorf("failed to get underlying SQL DB: %w", err)
- }
- if err := rawDB.Ping(); err != nil {
- return nil, fmt.Errorf("failed to ping database: %w", err)
- }
- // Enable foreign keys in SQLite
- err = gormDB.Exec("PRAGMA foreign_keys = ON").Error
- if err != nil {
- return nil, fmt.Errorf("failed to enable foreign keys: %w", err)
- }
- // Auto-migrate all models
- err = gormDB.AutoMigrate(
- &models.User{},
- &models.Client{},
- &models.App{},
- &models.Component{},
- &models.Deployment{},
- &models.Preview{},
- &models.Ticket{},
- &models.TicketComment{},
- &models.Provider{},
- &models.BuildJob{},
- )
- if err != nil {
- return nil, fmt.Errorf("failed to auto-migrate models: %w", err)
- }
- store := &SQLiteStore{
- db: gormDB,
- rawDB: rawDB,
- dsn: dataSourceName,
- }
- return store, nil
- }
- // fileExists checks if a file exists
- func fileExists(filename string) bool {
- _, err := os.Stat(filename)
- return !os.IsNotExist(err)
- }
- // GetDB returns the raw SQL database instance for backward compatibility
- func (m *SQLiteStore) GetDB() *sql.DB {
- return m.rawDB
- }
- // GetGormDB returns the GORM database instance
- func (m *SQLiteStore) GetGormDB() *gorm.DB {
- return m.db
- }
- // Connect establishes a connection to the SQLite database
- func (m *SQLiteStore) Connect() error {
- // Connection is already established in NewSQLiteStore
- return nil
- }
- // Disconnect closes the connection to the SQLite database
- func (m *SQLiteStore) Disconnect() error {
- sqlDB, err := m.db.DB()
- if err != nil {
- return fmt.Errorf("failed to get underlying SQL DB: %w", err)
- }
- return sqlDB.Close()
- }
- // Close provides a more standard name for closing the database connection.
- // It simply calls Disconnect.
- func (m *SQLiteStore) Close() error {
- return m.Disconnect()
- }
|