• Add full multi-table schema for collections with normalized design (collections, collection_templates, collection_items, collection_item_versions) • Implement collection detection and processing in enhancement pipeline for .insertr-add elements • Add template extraction and storage from existing HTML children with multi-variant support • Enable collection reconstruction from database on server restart with proper DOM rebuilding • Extend ContentClient interface with collection operations and full database integration • Update enhance command to use engine.DatabaseClient for collection persistence support
413 lines
12 KiB
Go
413 lines
12 KiB
Go
package engine
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
|
|
"github.com/insertr/insertr/internal/db"
|
|
"github.com/insertr/insertr/internal/db/postgresql"
|
|
"github.com/insertr/insertr/internal/db/sqlite"
|
|
)
|
|
|
|
// Helper function to convert sql.NullString to string
|
|
func getStringFromNullString(ns sql.NullString) string {
|
|
if ns.Valid {
|
|
return ns.String
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// DatabaseClient implements ContentClient interface using the database
|
|
type DatabaseClient struct {
|
|
database *db.Database
|
|
}
|
|
|
|
// NewDatabaseClient creates a new database client
|
|
func NewDatabaseClient(database *db.Database) *DatabaseClient {
|
|
return &DatabaseClient{
|
|
database: database,
|
|
}
|
|
}
|
|
|
|
// GetContent retrieves a single content item
|
|
func (c *DatabaseClient) GetContent(siteID, contentID string) (*ContentItem, error) {
|
|
switch c.database.GetDBType() {
|
|
case "sqlite3":
|
|
content, err := c.database.GetSQLiteQueries().GetContent(context.Background(), sqlite.GetContentParams{
|
|
ID: contentID,
|
|
SiteID: siteID,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &ContentItem{
|
|
ID: content.ID,
|
|
SiteID: content.SiteID,
|
|
HTMLContent: content.HtmlContent,
|
|
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
|
LastEditedBy: content.LastEditedBy,
|
|
}, nil
|
|
|
|
case "postgresql":
|
|
content, err := c.database.GetPostgreSQLQueries().GetContent(context.Background(), postgresql.GetContentParams{
|
|
ID: contentID,
|
|
SiteID: siteID,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &ContentItem{
|
|
ID: content.ID,
|
|
SiteID: content.SiteID,
|
|
HTMLContent: content.HtmlContent,
|
|
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
|
LastEditedBy: content.LastEditedBy,
|
|
}, nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unsupported database type: %s", c.database.GetDBType())
|
|
}
|
|
}
|
|
|
|
// GetBulkContent retrieves multiple content items
|
|
func (c *DatabaseClient) GetBulkContent(siteID string, contentIDs []string) (map[string]ContentItem, error) {
|
|
switch c.database.GetDBType() {
|
|
case "sqlite3":
|
|
contents, err := c.database.GetSQLiteQueries().GetBulkContent(context.Background(), sqlite.GetBulkContentParams{
|
|
SiteID: siteID,
|
|
Ids: contentIDs,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
items := make(map[string]ContentItem)
|
|
for _, content := range contents {
|
|
items[content.ID] = ContentItem{
|
|
ID: content.ID,
|
|
SiteID: content.SiteID,
|
|
HTMLContent: content.HtmlContent,
|
|
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
|
LastEditedBy: content.LastEditedBy,
|
|
}
|
|
}
|
|
return items, nil
|
|
|
|
case "postgresql":
|
|
contents, err := c.database.GetPostgreSQLQueries().GetBulkContent(context.Background(), postgresql.GetBulkContentParams{
|
|
SiteID: siteID,
|
|
Ids: contentIDs,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
items := make(map[string]ContentItem)
|
|
for _, content := range contents {
|
|
items[content.ID] = ContentItem{
|
|
ID: content.ID,
|
|
SiteID: content.SiteID,
|
|
HTMLContent: content.HtmlContent,
|
|
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
|
LastEditedBy: content.LastEditedBy,
|
|
}
|
|
}
|
|
return items, nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unsupported database type: %s", c.database.GetDBType())
|
|
}
|
|
}
|
|
|
|
// GetAllContent retrieves all content items for a site
|
|
func (c *DatabaseClient) GetAllContent(siteID string) (map[string]ContentItem, error) {
|
|
switch c.database.GetDBType() {
|
|
case "sqlite3":
|
|
contents, err := c.database.GetSQLiteQueries().GetAllContent(context.Background(), siteID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
items := make(map[string]ContentItem)
|
|
for _, content := range contents {
|
|
items[content.ID] = ContentItem{
|
|
ID: content.ID,
|
|
SiteID: content.SiteID,
|
|
HTMLContent: content.HtmlContent,
|
|
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
|
LastEditedBy: content.LastEditedBy,
|
|
}
|
|
}
|
|
return items, nil
|
|
|
|
case "postgresql":
|
|
contents, err := c.database.GetPostgreSQLQueries().GetAllContent(context.Background(), siteID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
items := make(map[string]ContentItem)
|
|
for _, content := range contents {
|
|
items[content.ID] = ContentItem{
|
|
ID: content.ID,
|
|
SiteID: content.SiteID,
|
|
HTMLContent: content.HtmlContent,
|
|
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
|
LastEditedBy: content.LastEditedBy,
|
|
}
|
|
}
|
|
return items, nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unsupported database type: %s", c.database.GetDBType())
|
|
}
|
|
}
|
|
|
|
// CreateContent creates a new content item
|
|
func (c *DatabaseClient) CreateContent(siteID, contentID, htmlContent, originalTemplate, lastEditedBy string) (*ContentItem, error) {
|
|
switch c.database.GetDBType() {
|
|
case "sqlite3":
|
|
content, err := c.database.GetSQLiteQueries().CreateContent(context.Background(), sqlite.CreateContentParams{
|
|
ID: contentID,
|
|
SiteID: siteID,
|
|
HtmlContent: htmlContent,
|
|
OriginalTemplate: toNullString(originalTemplate),
|
|
LastEditedBy: lastEditedBy,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &ContentItem{
|
|
ID: content.ID,
|
|
SiteID: content.SiteID,
|
|
HTMLContent: content.HtmlContent,
|
|
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
|
LastEditedBy: content.LastEditedBy,
|
|
}, nil
|
|
|
|
case "postgresql":
|
|
content, err := c.database.GetPostgreSQLQueries().CreateContent(context.Background(), postgresql.CreateContentParams{
|
|
ID: contentID,
|
|
SiteID: siteID,
|
|
HtmlContent: htmlContent,
|
|
OriginalTemplate: toNullString(originalTemplate),
|
|
LastEditedBy: lastEditedBy,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &ContentItem{
|
|
ID: content.ID,
|
|
SiteID: content.SiteID,
|
|
HTMLContent: content.HtmlContent,
|
|
OriginalTemplate: getStringFromNullString(content.OriginalTemplate),
|
|
LastEditedBy: content.LastEditedBy,
|
|
}, nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unsupported database type: %s", c.database.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}
|
|
}
|
|
|
|
// GetCollection retrieves a collection container
|
|
func (c *DatabaseClient) GetCollection(siteID, collectionID string) (*CollectionItem, error) {
|
|
switch c.database.GetDBType() {
|
|
case "sqlite3":
|
|
collection, err := c.database.GetSQLiteQueries().GetCollection(context.Background(), sqlite.GetCollectionParams{
|
|
ID: collectionID,
|
|
SiteID: siteID,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &CollectionItem{
|
|
ID: collection.ID,
|
|
SiteID: collection.SiteID,
|
|
ContainerHTML: collection.ContainerHtml,
|
|
LastEditedBy: collection.LastEditedBy,
|
|
}, nil
|
|
|
|
case "postgresql":
|
|
collection, err := c.database.GetPostgreSQLQueries().GetCollection(context.Background(), postgresql.GetCollectionParams{
|
|
ID: collectionID,
|
|
SiteID: siteID,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &CollectionItem{
|
|
ID: collection.ID,
|
|
SiteID: collection.SiteID,
|
|
ContainerHTML: collection.ContainerHtml,
|
|
LastEditedBy: collection.LastEditedBy,
|
|
}, nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unsupported database type: %s", c.database.GetDBType())
|
|
}
|
|
}
|
|
|
|
// CreateCollection creates a new collection container
|
|
func (c *DatabaseClient) CreateCollection(siteID, collectionID, containerHTML, lastEditedBy string) (*CollectionItem, error) {
|
|
switch c.database.GetDBType() {
|
|
case "sqlite3":
|
|
collection, err := c.database.GetSQLiteQueries().CreateCollection(context.Background(), sqlite.CreateCollectionParams{
|
|
ID: collectionID,
|
|
SiteID: siteID,
|
|
ContainerHtml: containerHTML,
|
|
LastEditedBy: lastEditedBy,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &CollectionItem{
|
|
ID: collection.ID,
|
|
SiteID: collection.SiteID,
|
|
ContainerHTML: collection.ContainerHtml,
|
|
LastEditedBy: collection.LastEditedBy,
|
|
}, nil
|
|
|
|
case "postgresql":
|
|
collection, err := c.database.GetPostgreSQLQueries().CreateCollection(context.Background(), postgresql.CreateCollectionParams{
|
|
ID: collectionID,
|
|
SiteID: siteID,
|
|
ContainerHtml: containerHTML,
|
|
LastEditedBy: lastEditedBy,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &CollectionItem{
|
|
ID: collection.ID,
|
|
SiteID: collection.SiteID,
|
|
ContainerHTML: collection.ContainerHtml,
|
|
LastEditedBy: collection.LastEditedBy,
|
|
}, nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unsupported database type: %s", c.database.GetDBType())
|
|
}
|
|
}
|
|
|
|
// GetCollectionItems retrieves all items in a collection with template information
|
|
func (c *DatabaseClient) GetCollectionItems(siteID, collectionID string) ([]CollectionItemWithTemplate, error) {
|
|
switch c.database.GetDBType() {
|
|
case "sqlite3":
|
|
items, err := c.database.GetSQLiteQueries().GetCollectionItemsWithTemplate(context.Background(), sqlite.GetCollectionItemsWithTemplateParams{
|
|
CollectionID: collectionID,
|
|
SiteID: siteID,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]CollectionItemWithTemplate, len(items))
|
|
for i, item := range items {
|
|
result[i] = CollectionItemWithTemplate{
|
|
ItemID: item.ItemID,
|
|
CollectionID: item.CollectionID,
|
|
SiteID: item.SiteID,
|
|
TemplateID: int(item.TemplateID),
|
|
HTMLContent: item.HtmlContent,
|
|
Position: int(item.Position),
|
|
LastEditedBy: item.LastEditedBy,
|
|
TemplateName: item.TemplateName,
|
|
HTMLTemplate: item.HtmlTemplate,
|
|
IsDefault: item.IsDefault != 0, // SQLite uses INTEGER for boolean
|
|
}
|
|
}
|
|
return result, nil
|
|
|
|
case "postgresql":
|
|
items, err := c.database.GetPostgreSQLQueries().GetCollectionItemsWithTemplate(context.Background(), postgresql.GetCollectionItemsWithTemplateParams{
|
|
CollectionID: collectionID,
|
|
SiteID: siteID,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]CollectionItemWithTemplate, len(items))
|
|
for i, item := range items {
|
|
result[i] = CollectionItemWithTemplate{
|
|
ItemID: item.ItemID,
|
|
CollectionID: item.CollectionID,
|
|
SiteID: item.SiteID,
|
|
TemplateID: int(item.TemplateID),
|
|
HTMLContent: item.HtmlContent,
|
|
Position: int(item.Position),
|
|
LastEditedBy: item.LastEditedBy,
|
|
TemplateName: item.TemplateName,
|
|
HTMLTemplate: item.HtmlTemplate,
|
|
IsDefault: item.IsDefault, // PostgreSQL uses BOOLEAN
|
|
}
|
|
}
|
|
return result, nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unsupported database type: %s", c.database.GetDBType())
|
|
}
|
|
}
|
|
|
|
// CreateCollectionTemplate creates a new template for a collection
|
|
func (c *DatabaseClient) CreateCollectionTemplate(siteID, collectionID, name, htmlTemplate string, isDefault bool) (*CollectionTemplateItem, error) {
|
|
switch c.database.GetDBType() {
|
|
case "sqlite3":
|
|
var isDefaultInt int64
|
|
if isDefault {
|
|
isDefaultInt = 1
|
|
}
|
|
|
|
template, err := c.database.GetSQLiteQueries().CreateCollectionTemplate(context.Background(), sqlite.CreateCollectionTemplateParams{
|
|
CollectionID: collectionID,
|
|
SiteID: siteID,
|
|
Name: name,
|
|
HtmlTemplate: htmlTemplate,
|
|
IsDefault: isDefaultInt,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &CollectionTemplateItem{
|
|
TemplateID: int(template.TemplateID),
|
|
CollectionID: template.CollectionID,
|
|
SiteID: template.SiteID,
|
|
Name: template.Name,
|
|
HTMLTemplate: template.HtmlTemplate,
|
|
IsDefault: template.IsDefault != 0,
|
|
}, nil
|
|
|
|
case "postgresql":
|
|
template, err := c.database.GetPostgreSQLQueries().CreateCollectionTemplate(context.Background(), postgresql.CreateCollectionTemplateParams{
|
|
CollectionID: collectionID,
|
|
SiteID: siteID,
|
|
Name: name,
|
|
HtmlTemplate: htmlTemplate,
|
|
IsDefault: isDefault,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &CollectionTemplateItem{
|
|
TemplateID: int(template.TemplateID),
|
|
CollectionID: template.CollectionID,
|
|
SiteID: template.SiteID,
|
|
Name: template.Name,
|
|
HTMLTemplate: template.HtmlTemplate,
|
|
IsDefault: template.IsDefault,
|
|
}, nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unsupported database type: %s", c.database.GetDBType())
|
|
}
|
|
}
|