package content import ( "context" "database/sql" "fmt" "time" "github.com/insertr/insertr/internal/db" "github.com/insertr/insertr/internal/db/postgresql" "github.com/insertr/insertr/internal/db/sqlite" "github.com/insertr/insertr/internal/engine" ) // Helper function to convert sql.NullString to string func getStringFromNullString(ns sql.NullString) string { if ns.Valid { return ns.String } return "" } // DatabaseClient implements ContentClient for direct database access type DatabaseClient struct { db *db.Database } // NewDatabaseClient creates a new database content client func NewDatabaseClient(database *db.Database) *DatabaseClient { return &DatabaseClient{ db: database, } } // GetContent fetches a single content item by ID func (d *DatabaseClient) GetContent(siteID, contentID string) (*engine.ContentItem, error) { ctx := context.Background() var content interface{} var err error switch d.db.GetDBType() { case "sqlite3": content, err = d.db.GetSQLiteQueries().GetContent(ctx, sqlite.GetContentParams{ ID: contentID, SiteID: siteID, }) case "postgresql": content, err = d.db.GetPostgreSQLQueries().GetContent(ctx, postgresql.GetContentParams{ ID: contentID, SiteID: siteID, }) default: return nil, fmt.Errorf("unsupported database type: %s", d.db.GetDBType()) } if err != nil { if err == sql.ErrNoRows { return nil, nil // Content not found, return nil without error } return nil, fmt.Errorf("database error: %w", err) } item := d.convertToContentItem(content) return &item, nil } // GetBulkContent fetches multiple content items by IDs func (d *DatabaseClient) GetBulkContent(siteID string, contentIDs []string) (map[string]engine.ContentItem, error) { if len(contentIDs) == 0 { return make(map[string]engine.ContentItem), nil } ctx := context.Background() var dbContent interface{} var err error switch d.db.GetDBType() { case "sqlite3": dbContent, err = d.db.GetSQLiteQueries().GetBulkContent(ctx, sqlite.GetBulkContentParams{ SiteID: siteID, Ids: contentIDs, }) case "postgresql": dbContent, err = d.db.GetPostgreSQLQueries().GetBulkContent(ctx, postgresql.GetBulkContentParams{ SiteID: siteID, Ids: contentIDs, }) default: return nil, fmt.Errorf("unsupported database type: %s", d.db.GetDBType()) } if err != nil { return nil, fmt.Errorf("database error: %w", err) } items := d.convertToContentItemList(dbContent) // Convert slice to map for easy lookup result := make(map[string]engine.ContentItem) for _, item := range items { result[item.ID] = item } return result, nil } // GetAllContent fetches all content for a site func (d *DatabaseClient) GetAllContent(siteID string) (map[string]engine.ContentItem, error) { ctx := context.Background() var dbContent interface{} var err error switch d.db.GetDBType() { case "sqlite3": dbContent, err = d.db.GetSQLiteQueries().GetAllContent(ctx, siteID) case "postgresql": dbContent, err = d.db.GetPostgreSQLQueries().GetAllContent(ctx, siteID) default: return nil, fmt.Errorf("unsupported database type: %s", d.db.GetDBType()) } if err != nil { return nil, fmt.Errorf("database error: %w", err) } items := d.convertToContentItemList(dbContent) // Convert slice to map for easy lookup result := make(map[string]engine.ContentItem) for _, item := range items { result[item.ID] = item } return result, nil } // convertToContentItem converts database models to engine.ContentItem func (d *DatabaseClient) convertToContentItem(content interface{}) engine.ContentItem { switch d.db.GetDBType() { case "sqlite3": c := content.(sqlite.Content) return engine.ContentItem{ ID: c.ID, SiteID: c.SiteID, HTMLContent: c.HtmlContent, OriginalTemplate: getStringFromNullString(c.OriginalTemplate), Type: c.Type, UpdatedAt: time.Unix(c.UpdatedAt, 0).Format(time.RFC3339), } case "postgresql": c := content.(postgresql.Content) return engine.ContentItem{ ID: c.ID, SiteID: c.SiteID, HTMLContent: c.HtmlContent, OriginalTemplate: getStringFromNullString(c.OriginalTemplate), Type: c.Type, UpdatedAt: time.Unix(c.UpdatedAt, 0).Format(time.RFC3339), } } return engine.ContentItem{} // Should never happen } // convertToContentItemList converts database model lists to engine.ContentItem slice func (d *DatabaseClient) convertToContentItemList(contentList interface{}) []engine.ContentItem { switch d.db.GetDBType() { case "sqlite3": list := contentList.([]sqlite.Content) items := make([]engine.ContentItem, len(list)) for i, content := range list { items[i] = d.convertToContentItem(content) } return items case "postgresql": list := contentList.([]postgresql.Content) items := make([]engine.ContentItem, len(list)) for i, content := range list { items[i] = d.convertToContentItem(content) } return items } return []engine.ContentItem{} // Should never happen } // CreateContent creates a new content item func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, contentType, lastEditedBy string) (*engine.ContentItem, error) { switch c.db.GetDBType() { case "sqlite3": content, err := c.db.GetSQLiteQueries().CreateContent(context.Background(), sqlite.CreateContentParams{ ID: contentID, SiteID: siteID, HtmlContent: htmlContent, OriginalTemplate: toNullString(originalTemplate), Type: contentType, LastEditedBy: lastEditedBy, }) if err != nil { return nil, err } return &engine.ContentItem{ ID: content.ID, SiteID: content.SiteID, HTMLContent: content.HtmlContent, OriginalTemplate: getStringFromNullString(content.OriginalTemplate), Type: content.Type, LastEditedBy: content.LastEditedBy, }, nil case "postgresql": content, err := c.db.GetPostgreSQLQueries().CreateContent(context.Background(), postgresql.CreateContentParams{ ID: contentID, SiteID: siteID, HtmlContent: htmlContent, OriginalTemplate: toNullString(originalTemplate), Type: contentType, LastEditedBy: lastEditedBy, }) if err != nil { return nil, err } return &engine.ContentItem{ ID: content.ID, SiteID: content.SiteID, HTMLContent: content.HtmlContent, OriginalTemplate: getStringFromNullString(content.OriginalTemplate), Type: content.Type, LastEditedBy: content.LastEditedBy, }, nil default: return nil, fmt.Errorf("unsupported database type: %s", c.db.GetDBType()) } } // Helper function to convert string to sql.NullString func toNullString(s string) sql.NullString { if s == "" { return sql.NullString{Valid: false} } return sql.NullString{String: s, Valid: true} }