Implement complete collection persistence with database-backed survival across server restarts
• 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
This commit is contained in:
@@ -827,4 +827,130 @@ body:not(.insertr-edit-mode) .insertr-editing-hover::after {
|
||||
margin-right: 0;
|
||||
order: -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
COLLECTION MANAGEMENT STYLES (.insertr-add)
|
||||
================================================================= */
|
||||
|
||||
/* Collection container when active */
|
||||
.insertr-collection-active {
|
||||
outline: 2px dashed var(--insertr-primary);
|
||||
outline-offset: 4px;
|
||||
border-radius: var(--insertr-border-radius);
|
||||
}
|
||||
|
||||
/* Add button positioned in top right of container */
|
||||
.insertr-add-btn {
|
||||
position: absolute;
|
||||
top: -12px;
|
||||
right: -12px;
|
||||
background: var(--insertr-primary);
|
||||
color: var(--insertr-text-inverse);
|
||||
border: none;
|
||||
padding: var(--insertr-spacing-xs) var(--insertr-spacing-sm);
|
||||
border-radius: var(--insertr-border-radius);
|
||||
font-size: var(--insertr-font-size-sm);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
z-index: var(--insertr-z-floating);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.insertr-add-btn:hover {
|
||||
background: var(--insertr-primary-hover);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.insertr-add-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Item controls positioned in top right corner of each item */
|
||||
.insertr-item-controls {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
z-index: var(--insertr-z-floating);
|
||||
}
|
||||
|
||||
/* Individual control buttons */
|
||||
.insertr-control-btn {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: var(--insertr-bg-primary);
|
||||
border: 1px solid var(--insertr-border-color);
|
||||
border-radius: 3px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--insertr-text-primary);
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.insertr-control-btn:hover {
|
||||
background: var(--insertr-bg-secondary);
|
||||
border-color: var(--insertr-primary);
|
||||
color: var(--insertr-primary);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* Remove button specific styling */
|
||||
.insertr-control-btn:last-child {
|
||||
color: var(--insertr-danger);
|
||||
}
|
||||
|
||||
.insertr-control-btn:last-child:hover {
|
||||
background: var(--insertr-danger);
|
||||
color: var(--insertr-text-inverse);
|
||||
border-color: var(--insertr-danger);
|
||||
}
|
||||
|
||||
/* Collection items hover state */
|
||||
.insertr-collection-active > *:hover {
|
||||
background: rgba(0, 123, 255, 0.03);
|
||||
outline: 1px solid rgba(var(--insertr-primary), 0.2);
|
||||
outline-offset: 2px;
|
||||
border-radius: var(--insertr-border-radius);
|
||||
}
|
||||
|
||||
/* Show item controls on hover */
|
||||
.insertr-collection-active > *:hover .insertr-item-controls {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Responsive adjustments for collection management */
|
||||
@media (max-width: 768px) {
|
||||
.insertr-add-btn {
|
||||
position: static;
|
||||
display: block;
|
||||
margin: var(--insertr-spacing-sm) auto 0;
|
||||
width: 100%;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.insertr-item-controls {
|
||||
position: relative;
|
||||
opacity: 1;
|
||||
top: auto;
|
||||
right: auto;
|
||||
justify-content: center;
|
||||
margin-top: var(--insertr-spacing-xs);
|
||||
}
|
||||
|
||||
.insertr-control-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
@@ -171,3 +171,20 @@ func (c *HTTPClient) CreateContent(siteID, contentID, htmlContent, originalTempl
|
||||
// This would typically be used in API-driven enhancement scenarios
|
||||
return nil, fmt.Errorf("CreateContent not implemented for HTTPClient - use DatabaseClient for enhancement")
|
||||
}
|
||||
|
||||
// Collection method stubs - TODO: Implement these for HTTP API
|
||||
func (c *HTTPClient) GetCollection(siteID, collectionID string) (*engine.CollectionItem, error) {
|
||||
return nil, fmt.Errorf("collection operations not implemented in HTTPClient")
|
||||
}
|
||||
|
||||
func (c *HTTPClient) CreateCollection(siteID, collectionID, containerHTML, lastEditedBy string) (*engine.CollectionItem, error) {
|
||||
return nil, fmt.Errorf("collection operations not implemented in HTTPClient")
|
||||
}
|
||||
|
||||
func (c *HTTPClient) GetCollectionItems(siteID, collectionID string) ([]engine.CollectionItemWithTemplate, error) {
|
||||
return nil, fmt.Errorf("collection operations not implemented in HTTPClient")
|
||||
}
|
||||
|
||||
func (c *HTTPClient) CreateCollectionTemplate(siteID, collectionID, name, htmlTemplate string, isDefault bool) (*engine.CollectionTemplateItem, error) {
|
||||
return nil, fmt.Errorf("collection operations not implemented in HTTPClient")
|
||||
}
|
||||
|
||||
@@ -233,3 +233,20 @@ func toNullString(s string) sql.NullString {
|
||||
}
|
||||
return sql.NullString{String: s, Valid: true}
|
||||
}
|
||||
|
||||
// Collection method stubs - TODO: Implement these
|
||||
func (d *DatabaseClient) GetCollection(siteID, collectionID string) (*engine.CollectionItem, error) {
|
||||
return nil, fmt.Errorf("collection operations not implemented in content.DatabaseClient")
|
||||
}
|
||||
|
||||
func (d *DatabaseClient) CreateCollection(siteID, collectionID, containerHTML, lastEditedBy string) (*engine.CollectionItem, error) {
|
||||
return nil, fmt.Errorf("collection operations not implemented in content.DatabaseClient")
|
||||
}
|
||||
|
||||
func (d *DatabaseClient) GetCollectionItems(siteID, collectionID string) ([]engine.CollectionItemWithTemplate, error) {
|
||||
return nil, fmt.Errorf("collection operations not implemented in content.DatabaseClient")
|
||||
}
|
||||
|
||||
func (d *DatabaseClient) CreateCollectionTemplate(siteID, collectionID, name, htmlTemplate string, isDefault bool) (*engine.CollectionTemplateItem, error) {
|
||||
return nil, fmt.Errorf("collection operations not implemented in content.DatabaseClient")
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package content
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/insertr/insertr/internal/engine"
|
||||
@@ -156,3 +157,20 @@ func (m *MockClient) CreateContent(siteID, contentID, htmlContent, originalTempl
|
||||
|
||||
return &item, nil
|
||||
}
|
||||
|
||||
// Collection method stubs - TODO: Implement these for mock testing
|
||||
func (m *MockClient) GetCollection(siteID, collectionID string) (*engine.CollectionItem, error) {
|
||||
return nil, fmt.Errorf("collection operations not implemented in MockClient")
|
||||
}
|
||||
|
||||
func (m *MockClient) CreateCollection(siteID, collectionID, containerHTML, lastEditedBy string) (*engine.CollectionItem, error) {
|
||||
return nil, fmt.Errorf("collection operations not implemented in MockClient")
|
||||
}
|
||||
|
||||
func (m *MockClient) GetCollectionItems(siteID, collectionID string) ([]engine.CollectionItemWithTemplate, error) {
|
||||
return nil, fmt.Errorf("collection operations not implemented in MockClient")
|
||||
}
|
||||
|
||||
func (m *MockClient) CreateCollectionTemplate(siteID, collectionID, name, htmlTemplate string, isDefault bool) (*engine.CollectionTemplateItem, error) {
|
||||
return nil, fmt.Errorf("collection operations not implemented in MockClient")
|
||||
}
|
||||
|
||||
@@ -108,11 +108,36 @@ func (db *Database) initializeSQLiteSchema() error {
|
||||
return fmt.Errorf("failed to create content_versions table: %w", err)
|
||||
}
|
||||
|
||||
// Create collection tables
|
||||
if err := db.sqliteQueries.InitializeCollectionsTable(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create collections table: %w", err)
|
||||
}
|
||||
|
||||
if err := db.sqliteQueries.InitializeCollectionTemplatesTable(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create collection_templates table: %w", err)
|
||||
}
|
||||
|
||||
if err := db.sqliteQueries.InitializeCollectionItemsTable(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create collection_items table: %w", err)
|
||||
}
|
||||
|
||||
if err := db.sqliteQueries.InitializeCollectionItemVersionsTable(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create collection_item_versions table: %w", err)
|
||||
}
|
||||
|
||||
// Create indexes manually (sqlc doesn't generate CREATE INDEX functions for SQLite)
|
||||
indexQueries := []string{
|
||||
"CREATE INDEX IF NOT EXISTS idx_content_site_id ON content(site_id);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_content_updated_at ON content(updated_at);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_content_versions_lookup ON content_versions(content_id, site_id, created_at DESC);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_collections_site_id ON collections(site_id);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_collections_updated_at ON collections(updated_at);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_collection_templates_lookup ON collection_templates(collection_id, site_id);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_collection_templates_default ON collection_templates(collection_id, site_id, is_default DESC);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_collection_items_lookup ON collection_items(collection_id, site_id, position);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_collection_items_template ON collection_items(template_id);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_collection_item_versions_lookup ON collection_item_versions(item_id, collection_id, site_id, created_at DESC);",
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_collection_templates_one_default ON collection_templates(collection_id, site_id) WHERE is_default = 1;",
|
||||
}
|
||||
|
||||
for _, query := range indexQueries {
|
||||
@@ -121,17 +146,32 @@ func (db *Database) initializeSQLiteSchema() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Create update trigger manually (sqlc doesn't generate trigger creation functions)
|
||||
triggerQuery := `
|
||||
CREATE TRIGGER IF NOT EXISTS update_content_updated_at
|
||||
AFTER UPDATE ON content
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE content SET updated_at = strftime('%s', 'now') WHERE id = NEW.id AND site_id = NEW.site_id;
|
||||
END;`
|
||||
// Create update triggers manually (sqlc doesn't generate trigger creation functions)
|
||||
triggerQueries := []string{
|
||||
`CREATE TRIGGER IF NOT EXISTS update_content_updated_at
|
||||
AFTER UPDATE ON content
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE content SET updated_at = strftime('%s', 'now') WHERE id = NEW.id AND site_id = NEW.site_id;
|
||||
END;`,
|
||||
`CREATE TRIGGER IF NOT EXISTS update_collections_updated_at
|
||||
AFTER UPDATE ON collections
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE collections SET updated_at = strftime('%s', 'now') WHERE id = NEW.id AND site_id = NEW.site_id;
|
||||
END;`,
|
||||
`CREATE TRIGGER IF NOT EXISTS update_collection_items_updated_at
|
||||
AFTER UPDATE ON collection_items
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE collection_items SET updated_at = strftime('%s', 'now') WHERE item_id = NEW.item_id AND collection_id = NEW.collection_id AND site_id = NEW.site_id;
|
||||
END;`,
|
||||
}
|
||||
|
||||
if _, err := db.conn.Exec(triggerQuery); err != nil {
|
||||
return fmt.Errorf("failed to create update trigger: %w", err)
|
||||
for _, query := range triggerQueries {
|
||||
if _, err := db.conn.Exec(query); err != nil {
|
||||
return fmt.Errorf("failed to create trigger: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -150,6 +190,23 @@ func (db *Database) initializePostgreSQLSchema() error {
|
||||
return fmt.Errorf("failed to create content_versions table: %w", err)
|
||||
}
|
||||
|
||||
// Create collection tables
|
||||
if err := db.postgresqlQueries.InitializeCollectionsTable(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create collections table: %w", err)
|
||||
}
|
||||
|
||||
if err := db.postgresqlQueries.InitializeCollectionTemplatesTable(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create collection_templates table: %w", err)
|
||||
}
|
||||
|
||||
if err := db.postgresqlQueries.InitializeCollectionItemsTable(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create collection_items table: %w", err)
|
||||
}
|
||||
|
||||
if err := db.postgresqlQueries.InitializeCollectionItemVersionsTable(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create collection_item_versions table: %w", err)
|
||||
}
|
||||
|
||||
// Create indexes using sqlc-generated functions (PostgreSQL supports this)
|
||||
if err := db.postgresqlQueries.CreateContentSiteIndex(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create content site index: %w", err)
|
||||
@@ -163,21 +220,67 @@ func (db *Database) initializePostgreSQLSchema() error {
|
||||
return fmt.Errorf("failed to create versions lookup index: %w", err)
|
||||
}
|
||||
|
||||
// Create collection indexes using sqlc-generated functions
|
||||
if err := db.postgresqlQueries.CreateCollectionsSiteIndex(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create collections site index: %w", err)
|
||||
}
|
||||
|
||||
if err := db.postgresqlQueries.CreateCollectionsUpdatedAtIndex(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create collections updated_at index: %w", err)
|
||||
}
|
||||
|
||||
if err := db.postgresqlQueries.CreateCollectionTemplatesLookupIndex(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create collection templates lookup index: %w", err)
|
||||
}
|
||||
|
||||
if err := db.postgresqlQueries.CreateCollectionTemplatesDefaultIndex(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create collection templates default index: %w", err)
|
||||
}
|
||||
|
||||
if err := db.postgresqlQueries.CreateCollectionItemsLookupIndex(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create collection items lookup index: %w", err)
|
||||
}
|
||||
|
||||
if err := db.postgresqlQueries.CreateCollectionItemsTemplateIndex(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create collection items template index: %w", err)
|
||||
}
|
||||
|
||||
if err := db.postgresqlQueries.CreateCollectionItemVersionsLookupIndex(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create collection item versions lookup index: %w", err)
|
||||
}
|
||||
|
||||
if err := db.postgresqlQueries.CreateCollectionTemplatesOneDefaultIndex(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create collection templates one default constraint: %w", err)
|
||||
}
|
||||
|
||||
// Create update function using sqlc-generated function
|
||||
if err := db.postgresqlQueries.CreateUpdateFunction(ctx); err != nil {
|
||||
return fmt.Errorf("failed to create update function: %w", err)
|
||||
}
|
||||
|
||||
// Create trigger manually (sqlc doesn't generate trigger creation functions)
|
||||
triggerQuery := `
|
||||
DROP TRIGGER IF EXISTS update_content_updated_at ON content;
|
||||
// Create triggers manually (sqlc doesn't generate trigger creation functions)
|
||||
triggerQueries := []string{
|
||||
`DROP TRIGGER IF EXISTS update_content_updated_at ON content;
|
||||
CREATE TRIGGER update_content_updated_at
|
||||
BEFORE UPDATE ON content
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_content_timestamp();`
|
||||
EXECUTE FUNCTION update_content_timestamp();`,
|
||||
`DROP TRIGGER IF EXISTS update_collections_updated_at ON collections;
|
||||
CREATE TRIGGER update_collections_updated_at
|
||||
BEFORE UPDATE ON collections
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_content_timestamp();`,
|
||||
`DROP TRIGGER IF EXISTS update_collection_items_updated_at ON collection_items;
|
||||
CREATE TRIGGER update_collection_items_updated_at
|
||||
BEFORE UPDATE ON collection_items
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_content_timestamp();`,
|
||||
}
|
||||
|
||||
if _, err := db.conn.Exec(triggerQuery); err != nil {
|
||||
return fmt.Errorf("failed to create update trigger: %w", err)
|
||||
for _, query := range triggerQueries {
|
||||
if _, err := db.conn.Exec(query); err != nil {
|
||||
return fmt.Errorf("failed to create trigger: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
197
internal/db/postgresql/collection_item_versions.sql.go
Normal file
197
internal/db/postgresql/collection_item_versions.sql.go
Normal file
@@ -0,0 +1,197 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
// source: collection_item_versions.sql
|
||||
|
||||
package postgresql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
const createCollectionItemVersion = `-- name: CreateCollectionItemVersion :exec
|
||||
|
||||
INSERT INTO collection_item_versions (item_id, collection_id, site_id, html_content, template_id, position, created_by)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
`
|
||||
|
||||
type CreateCollectionItemVersionParams struct {
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
TemplateID int32 `json:"template_id"`
|
||||
Position int32 `json:"position"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
}
|
||||
|
||||
// Collection item versions table queries
|
||||
func (q *Queries) CreateCollectionItemVersion(ctx context.Context, arg CreateCollectionItemVersionParams) error {
|
||||
_, err := q.db.ExecContext(ctx, createCollectionItemVersion,
|
||||
arg.ItemID,
|
||||
arg.CollectionID,
|
||||
arg.SiteID,
|
||||
arg.HtmlContent,
|
||||
arg.TemplateID,
|
||||
arg.Position,
|
||||
arg.CreatedBy,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteOldCollectionItemVersions = `-- name: DeleteOldCollectionItemVersions :exec
|
||||
DELETE FROM collection_item_versions
|
||||
WHERE created_at < $1 AND site_id = $2
|
||||
`
|
||||
|
||||
type DeleteOldCollectionItemVersionsParams struct {
|
||||
CreatedBefore int64 `json:"created_before"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteOldCollectionItemVersions(ctx context.Context, arg DeleteOldCollectionItemVersionsParams) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteOldCollectionItemVersions, arg.CreatedBefore, arg.SiteID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getAllCollectionItemVersionsForSite = `-- name: GetAllCollectionItemVersionsForSite :many
|
||||
SELECT
|
||||
civ.version_id, civ.item_id, civ.collection_id, civ.site_id, civ.html_content, civ.template_id, civ.position, civ.created_at, civ.created_by,
|
||||
ci.html_content as current_html_content, ci.position as current_position
|
||||
FROM collection_item_versions civ
|
||||
LEFT JOIN collection_items ci ON civ.item_id = ci.item_id AND civ.collection_id = ci.collection_id AND civ.site_id = ci.site_id
|
||||
WHERE civ.site_id = $1
|
||||
ORDER BY civ.created_at DESC
|
||||
LIMIT $2
|
||||
`
|
||||
|
||||
type GetAllCollectionItemVersionsForSiteParams struct {
|
||||
SiteID string `json:"site_id"`
|
||||
LimitCount int32 `json:"limit_count"`
|
||||
}
|
||||
|
||||
type GetAllCollectionItemVersionsForSiteRow struct {
|
||||
VersionID int32 `json:"version_id"`
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
TemplateID int32 `json:"template_id"`
|
||||
Position int32 `json:"position"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
CurrentHtmlContent sql.NullString `json:"current_html_content"`
|
||||
CurrentPosition sql.NullInt32 `json:"current_position"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetAllCollectionItemVersionsForSite(ctx context.Context, arg GetAllCollectionItemVersionsForSiteParams) ([]GetAllCollectionItemVersionsForSiteRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getAllCollectionItemVersionsForSite, arg.SiteID, arg.LimitCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetAllCollectionItemVersionsForSiteRow
|
||||
for rows.Next() {
|
||||
var i GetAllCollectionItemVersionsForSiteRow
|
||||
if err := rows.Scan(
|
||||
&i.VersionID,
|
||||
&i.ItemID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.TemplateID,
|
||||
&i.Position,
|
||||
&i.CreatedAt,
|
||||
&i.CreatedBy,
|
||||
&i.CurrentHtmlContent,
|
||||
&i.CurrentPosition,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getCollectionItemVersion = `-- name: GetCollectionItemVersion :one
|
||||
SELECT version_id, item_id, collection_id, site_id, html_content, template_id, position, created_at, created_by
|
||||
FROM collection_item_versions
|
||||
WHERE version_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetCollectionItemVersion(ctx context.Context, versionID int32) (CollectionItemVersion, error) {
|
||||
row := q.db.QueryRowContext(ctx, getCollectionItemVersion, versionID)
|
||||
var i CollectionItemVersion
|
||||
err := row.Scan(
|
||||
&i.VersionID,
|
||||
&i.ItemID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.TemplateID,
|
||||
&i.Position,
|
||||
&i.CreatedAt,
|
||||
&i.CreatedBy,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getCollectionItemVersionHistory = `-- name: GetCollectionItemVersionHistory :many
|
||||
SELECT version_id, item_id, collection_id, site_id, html_content, template_id, position, created_at, created_by
|
||||
FROM collection_item_versions
|
||||
WHERE item_id = $1 AND collection_id = $2 AND site_id = $3
|
||||
ORDER BY created_at DESC
|
||||
LIMIT $4
|
||||
`
|
||||
|
||||
type GetCollectionItemVersionHistoryParams struct {
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
LimitCount int32 `json:"limit_count"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetCollectionItemVersionHistory(ctx context.Context, arg GetCollectionItemVersionHistoryParams) ([]CollectionItemVersion, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getCollectionItemVersionHistory,
|
||||
arg.ItemID,
|
||||
arg.CollectionID,
|
||||
arg.SiteID,
|
||||
arg.LimitCount,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []CollectionItemVersion
|
||||
for rows.Next() {
|
||||
var i CollectionItemVersion
|
||||
if err := rows.Scan(
|
||||
&i.VersionID,
|
||||
&i.ItemID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.TemplateID,
|
||||
&i.Position,
|
||||
&i.CreatedAt,
|
||||
&i.CreatedBy,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
327
internal/db/postgresql/collection_items.sql.go
Normal file
327
internal/db/postgresql/collection_items.sql.go
Normal file
@@ -0,0 +1,327 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
// source: collection_items.sql
|
||||
|
||||
package postgresql
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const createCollectionItem = `-- name: CreateCollectionItem :one
|
||||
INSERT INTO collection_items (item_id, collection_id, site_id, template_id, html_content, position, last_edited_by)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING item_id, collection_id, site_id, template_id, html_content, position, created_at, updated_at, last_edited_by
|
||||
`
|
||||
|
||||
type CreateCollectionItemParams struct {
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
TemplateID int32 `json:"template_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
Position int32 `json:"position"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateCollectionItem(ctx context.Context, arg CreateCollectionItemParams) (CollectionItem, error) {
|
||||
row := q.db.QueryRowContext(ctx, createCollectionItem,
|
||||
arg.ItemID,
|
||||
arg.CollectionID,
|
||||
arg.SiteID,
|
||||
arg.TemplateID,
|
||||
arg.HtmlContent,
|
||||
arg.Position,
|
||||
arg.LastEditedBy,
|
||||
)
|
||||
var i CollectionItem
|
||||
err := row.Scan(
|
||||
&i.ItemID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.TemplateID,
|
||||
&i.HtmlContent,
|
||||
&i.Position,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteCollectionItem = `-- name: DeleteCollectionItem :exec
|
||||
DELETE FROM collection_items
|
||||
WHERE item_id = $1 AND collection_id = $2 AND site_id = $3
|
||||
`
|
||||
|
||||
type DeleteCollectionItemParams struct {
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteCollectionItem(ctx context.Context, arg DeleteCollectionItemParams) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteCollectionItem, arg.ItemID, arg.CollectionID, arg.SiteID)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteCollectionItems = `-- name: DeleteCollectionItems :exec
|
||||
DELETE FROM collection_items
|
||||
WHERE collection_id = $1 AND site_id = $2
|
||||
`
|
||||
|
||||
type DeleteCollectionItemsParams struct {
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteCollectionItems(ctx context.Context, arg DeleteCollectionItemsParams) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteCollectionItems, arg.CollectionID, arg.SiteID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getCollectionItem = `-- name: GetCollectionItem :one
|
||||
|
||||
SELECT item_id, collection_id, site_id, template_id, html_content, position, created_at, updated_at, last_edited_by
|
||||
FROM collection_items
|
||||
WHERE item_id = $1 AND collection_id = $2 AND site_id = $3
|
||||
`
|
||||
|
||||
type GetCollectionItemParams struct {
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
// Collection items table queries
|
||||
func (q *Queries) GetCollectionItem(ctx context.Context, arg GetCollectionItemParams) (CollectionItem, error) {
|
||||
row := q.db.QueryRowContext(ctx, getCollectionItem, arg.ItemID, arg.CollectionID, arg.SiteID)
|
||||
var i CollectionItem
|
||||
err := row.Scan(
|
||||
&i.ItemID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.TemplateID,
|
||||
&i.HtmlContent,
|
||||
&i.Position,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getCollectionItems = `-- name: GetCollectionItems :many
|
||||
SELECT item_id, collection_id, site_id, template_id, html_content, position, created_at, updated_at, last_edited_by
|
||||
FROM collection_items
|
||||
WHERE collection_id = $1 AND site_id = $2
|
||||
ORDER BY position ASC
|
||||
`
|
||||
|
||||
type GetCollectionItemsParams struct {
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetCollectionItems(ctx context.Context, arg GetCollectionItemsParams) ([]CollectionItem, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getCollectionItems, arg.CollectionID, arg.SiteID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []CollectionItem
|
||||
for rows.Next() {
|
||||
var i CollectionItem
|
||||
if err := rows.Scan(
|
||||
&i.ItemID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.TemplateID,
|
||||
&i.HtmlContent,
|
||||
&i.Position,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getCollectionItemsWithTemplate = `-- name: GetCollectionItemsWithTemplate :many
|
||||
SELECT
|
||||
ci.item_id, ci.collection_id, ci.site_id, ci.template_id, ci.html_content, ci.position, ci.created_at, ci.updated_at, ci.last_edited_by,
|
||||
ct.name as template_name, ct.html_template, ct.is_default
|
||||
FROM collection_items ci
|
||||
JOIN collection_templates ct ON ci.template_id = ct.template_id
|
||||
WHERE ci.collection_id = $1 AND ci.site_id = $2
|
||||
ORDER BY ci.position ASC
|
||||
`
|
||||
|
||||
type GetCollectionItemsWithTemplateParams struct {
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
type GetCollectionItemsWithTemplateRow struct {
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
TemplateID int32 `json:"template_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
Position int32 `json:"position"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
TemplateName string `json:"template_name"`
|
||||
HtmlTemplate string `json:"html_template"`
|
||||
IsDefault bool `json:"is_default"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetCollectionItemsWithTemplate(ctx context.Context, arg GetCollectionItemsWithTemplateParams) ([]GetCollectionItemsWithTemplateRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getCollectionItemsWithTemplate, arg.CollectionID, arg.SiteID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetCollectionItemsWithTemplateRow
|
||||
for rows.Next() {
|
||||
var i GetCollectionItemsWithTemplateRow
|
||||
if err := rows.Scan(
|
||||
&i.ItemID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.TemplateID,
|
||||
&i.HtmlContent,
|
||||
&i.Position,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
&i.TemplateName,
|
||||
&i.HtmlTemplate,
|
||||
&i.IsDefault,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getMaxPosition = `-- name: GetMaxPosition :one
|
||||
SELECT COALESCE(MAX(position), 0) as max_position
|
||||
FROM collection_items
|
||||
WHERE collection_id = $1 AND site_id = $2
|
||||
`
|
||||
|
||||
type GetMaxPositionParams struct {
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetMaxPosition(ctx context.Context, arg GetMaxPositionParams) (interface{}, error) {
|
||||
row := q.db.QueryRowContext(ctx, getMaxPosition, arg.CollectionID, arg.SiteID)
|
||||
var max_position interface{}
|
||||
err := row.Scan(&max_position)
|
||||
return max_position, err
|
||||
}
|
||||
|
||||
const reorderCollectionItems = `-- name: ReorderCollectionItems :exec
|
||||
UPDATE collection_items
|
||||
SET position = position + $1
|
||||
WHERE collection_id = $2 AND site_id = $3
|
||||
AND position >= $4
|
||||
`
|
||||
|
||||
type ReorderCollectionItemsParams struct {
|
||||
PositionDelta int32 `json:"position_delta"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
StartPosition int32 `json:"start_position"`
|
||||
}
|
||||
|
||||
func (q *Queries) ReorderCollectionItems(ctx context.Context, arg ReorderCollectionItemsParams) error {
|
||||
_, err := q.db.ExecContext(ctx, reorderCollectionItems,
|
||||
arg.PositionDelta,
|
||||
arg.CollectionID,
|
||||
arg.SiteID,
|
||||
arg.StartPosition,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateCollectionItem = `-- name: UpdateCollectionItem :one
|
||||
UPDATE collection_items
|
||||
SET html_content = $1, last_edited_by = $2
|
||||
WHERE item_id = $3 AND collection_id = $4 AND site_id = $5
|
||||
RETURNING item_id, collection_id, site_id, template_id, html_content, position, created_at, updated_at, last_edited_by
|
||||
`
|
||||
|
||||
type UpdateCollectionItemParams struct {
|
||||
HtmlContent string `json:"html_content"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateCollectionItem(ctx context.Context, arg UpdateCollectionItemParams) (CollectionItem, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateCollectionItem,
|
||||
arg.HtmlContent,
|
||||
arg.LastEditedBy,
|
||||
arg.ItemID,
|
||||
arg.CollectionID,
|
||||
arg.SiteID,
|
||||
)
|
||||
var i CollectionItem
|
||||
err := row.Scan(
|
||||
&i.ItemID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.TemplateID,
|
||||
&i.HtmlContent,
|
||||
&i.Position,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateCollectionItemPosition = `-- name: UpdateCollectionItemPosition :exec
|
||||
UPDATE collection_items
|
||||
SET position = $1
|
||||
WHERE item_id = $2 AND collection_id = $3 AND site_id = $4
|
||||
`
|
||||
|
||||
type UpdateCollectionItemPositionParams struct {
|
||||
Position int32 `json:"position"`
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateCollectionItemPosition(ctx context.Context, arg UpdateCollectionItemPositionParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateCollectionItemPosition,
|
||||
arg.Position,
|
||||
arg.ItemID,
|
||||
arg.CollectionID,
|
||||
arg.SiteID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
217
internal/db/postgresql/collection_templates.sql.go
Normal file
217
internal/db/postgresql/collection_templates.sql.go
Normal file
@@ -0,0 +1,217 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
// source: collection_templates.sql
|
||||
|
||||
package postgresql
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const createCollectionTemplate = `-- name: CreateCollectionTemplate :one
|
||||
INSERT INTO collection_templates (collection_id, site_id, name, html_template, is_default)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING template_id, collection_id, site_id, name, html_template, is_default, created_at
|
||||
`
|
||||
|
||||
type CreateCollectionTemplateParams struct {
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
Name string `json:"name"`
|
||||
HtmlTemplate string `json:"html_template"`
|
||||
IsDefault bool `json:"is_default"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateCollectionTemplate(ctx context.Context, arg CreateCollectionTemplateParams) (CollectionTemplate, error) {
|
||||
row := q.db.QueryRowContext(ctx, createCollectionTemplate,
|
||||
arg.CollectionID,
|
||||
arg.SiteID,
|
||||
arg.Name,
|
||||
arg.HtmlTemplate,
|
||||
arg.IsDefault,
|
||||
)
|
||||
var i CollectionTemplate
|
||||
err := row.Scan(
|
||||
&i.TemplateID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.Name,
|
||||
&i.HtmlTemplate,
|
||||
&i.IsDefault,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteCollectionTemplate = `-- name: DeleteCollectionTemplate :exec
|
||||
DELETE FROM collection_templates
|
||||
WHERE template_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteCollectionTemplate(ctx context.Context, templateID int32) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteCollectionTemplate, templateID)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteCollectionTemplates = `-- name: DeleteCollectionTemplates :exec
|
||||
DELETE FROM collection_templates
|
||||
WHERE collection_id = $1 AND site_id = $2
|
||||
`
|
||||
|
||||
type DeleteCollectionTemplatesParams struct {
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteCollectionTemplates(ctx context.Context, arg DeleteCollectionTemplatesParams) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteCollectionTemplates, arg.CollectionID, arg.SiteID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getCollectionTemplate = `-- name: GetCollectionTemplate :one
|
||||
|
||||
SELECT template_id, collection_id, site_id, name, html_template, is_default, created_at
|
||||
FROM collection_templates
|
||||
WHERE template_id = $1
|
||||
`
|
||||
|
||||
// Collection templates table queries
|
||||
func (q *Queries) GetCollectionTemplate(ctx context.Context, templateID int32) (CollectionTemplate, error) {
|
||||
row := q.db.QueryRowContext(ctx, getCollectionTemplate, templateID)
|
||||
var i CollectionTemplate
|
||||
err := row.Scan(
|
||||
&i.TemplateID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.Name,
|
||||
&i.HtmlTemplate,
|
||||
&i.IsDefault,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getCollectionTemplates = `-- name: GetCollectionTemplates :many
|
||||
SELECT template_id, collection_id, site_id, name, html_template, is_default, created_at
|
||||
FROM collection_templates
|
||||
WHERE collection_id = $1 AND site_id = $2
|
||||
ORDER BY is_default DESC, created_at ASC
|
||||
`
|
||||
|
||||
type GetCollectionTemplatesParams struct {
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetCollectionTemplates(ctx context.Context, arg GetCollectionTemplatesParams) ([]CollectionTemplate, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getCollectionTemplates, arg.CollectionID, arg.SiteID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []CollectionTemplate
|
||||
for rows.Next() {
|
||||
var i CollectionTemplate
|
||||
if err := rows.Scan(
|
||||
&i.TemplateID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.Name,
|
||||
&i.HtmlTemplate,
|
||||
&i.IsDefault,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getDefaultTemplate = `-- name: GetDefaultTemplate :one
|
||||
SELECT template_id, collection_id, site_id, name, html_template, is_default, created_at
|
||||
FROM collection_templates
|
||||
WHERE collection_id = $1 AND site_id = $2 AND is_default = TRUE
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
type GetDefaultTemplateParams struct {
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetDefaultTemplate(ctx context.Context, arg GetDefaultTemplateParams) (CollectionTemplate, error) {
|
||||
row := q.db.QueryRowContext(ctx, getDefaultTemplate, arg.CollectionID, arg.SiteID)
|
||||
var i CollectionTemplate
|
||||
err := row.Scan(
|
||||
&i.TemplateID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.Name,
|
||||
&i.HtmlTemplate,
|
||||
&i.IsDefault,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const setTemplateAsDefault = `-- name: SetTemplateAsDefault :exec
|
||||
UPDATE collection_templates
|
||||
SET is_default = CASE
|
||||
WHEN template_id = $1 THEN TRUE
|
||||
ELSE FALSE
|
||||
END
|
||||
WHERE collection_id = $2 AND site_id = $3
|
||||
`
|
||||
|
||||
type SetTemplateAsDefaultParams struct {
|
||||
TemplateID int32 `json:"template_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) SetTemplateAsDefault(ctx context.Context, arg SetTemplateAsDefaultParams) error {
|
||||
_, err := q.db.ExecContext(ctx, setTemplateAsDefault, arg.TemplateID, arg.CollectionID, arg.SiteID)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateCollectionTemplate = `-- name: UpdateCollectionTemplate :one
|
||||
UPDATE collection_templates
|
||||
SET name = $1, html_template = $2, is_default = $3
|
||||
WHERE template_id = $4
|
||||
RETURNING template_id, collection_id, site_id, name, html_template, is_default, created_at
|
||||
`
|
||||
|
||||
type UpdateCollectionTemplateParams struct {
|
||||
Name string `json:"name"`
|
||||
HtmlTemplate string `json:"html_template"`
|
||||
IsDefault bool `json:"is_default"`
|
||||
TemplateID int32 `json:"template_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateCollectionTemplate(ctx context.Context, arg UpdateCollectionTemplateParams) (CollectionTemplate, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateCollectionTemplate,
|
||||
arg.Name,
|
||||
arg.HtmlTemplate,
|
||||
arg.IsDefault,
|
||||
arg.TemplateID,
|
||||
)
|
||||
var i CollectionTemplate
|
||||
err := row.Scan(
|
||||
&i.TemplateID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.Name,
|
||||
&i.HtmlTemplate,
|
||||
&i.IsDefault,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
199
internal/db/postgresql/collections.sql.go
Normal file
199
internal/db/postgresql/collections.sql.go
Normal file
@@ -0,0 +1,199 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
// source: collections.sql
|
||||
|
||||
package postgresql
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const createCollection = `-- name: CreateCollection :one
|
||||
INSERT INTO collections (id, site_id, container_html, last_edited_by)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id, site_id, container_html, created_at, updated_at, last_edited_by
|
||||
`
|
||||
|
||||
type CreateCollectionParams struct {
|
||||
ID string `json:"id"`
|
||||
SiteID string `json:"site_id"`
|
||||
ContainerHtml string `json:"container_html"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateCollection(ctx context.Context, arg CreateCollectionParams) (Collection, error) {
|
||||
row := q.db.QueryRowContext(ctx, createCollection,
|
||||
arg.ID,
|
||||
arg.SiteID,
|
||||
arg.ContainerHtml,
|
||||
arg.LastEditedBy,
|
||||
)
|
||||
var i Collection
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.SiteID,
|
||||
&i.ContainerHtml,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteAllSiteCollections = `-- name: DeleteAllSiteCollections :exec
|
||||
DELETE FROM collections
|
||||
WHERE site_id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteAllSiteCollections(ctx context.Context, siteID string) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteAllSiteCollections, siteID)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteCollection = `-- name: DeleteCollection :exec
|
||||
DELETE FROM collections
|
||||
WHERE id = $1 AND site_id = $2
|
||||
`
|
||||
|
||||
type DeleteCollectionParams struct {
|
||||
ID string `json:"id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteCollection(ctx context.Context, arg DeleteCollectionParams) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteCollection, arg.ID, arg.SiteID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getAllCollections = `-- name: GetAllCollections :many
|
||||
SELECT id, site_id, container_html, created_at, updated_at, last_edited_by
|
||||
FROM collections
|
||||
WHERE site_id = $1
|
||||
ORDER BY updated_at DESC
|
||||
`
|
||||
|
||||
func (q *Queries) GetAllCollections(ctx context.Context, siteID string) ([]Collection, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getAllCollections, siteID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Collection
|
||||
for rows.Next() {
|
||||
var i Collection
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.SiteID,
|
||||
&i.ContainerHtml,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getCollection = `-- name: GetCollection :one
|
||||
|
||||
SELECT id, site_id, container_html, created_at, updated_at, last_edited_by
|
||||
FROM collections
|
||||
WHERE id = $1 AND site_id = $2
|
||||
`
|
||||
|
||||
type GetCollectionParams struct {
|
||||
ID string `json:"id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
// Collections table queries
|
||||
func (q *Queries) GetCollection(ctx context.Context, arg GetCollectionParams) (Collection, error) {
|
||||
row := q.db.QueryRowContext(ctx, getCollection, arg.ID, arg.SiteID)
|
||||
var i Collection
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.SiteID,
|
||||
&i.ContainerHtml,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateCollection = `-- name: UpdateCollection :one
|
||||
UPDATE collections
|
||||
SET container_html = $1, last_edited_by = $2
|
||||
WHERE id = $3 AND site_id = $4
|
||||
RETURNING id, site_id, container_html, created_at, updated_at, last_edited_by
|
||||
`
|
||||
|
||||
type UpdateCollectionParams struct {
|
||||
ContainerHtml string `json:"container_html"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
ID string `json:"id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateCollection(ctx context.Context, arg UpdateCollectionParams) (Collection, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateCollection,
|
||||
arg.ContainerHtml,
|
||||
arg.LastEditedBy,
|
||||
arg.ID,
|
||||
arg.SiteID,
|
||||
)
|
||||
var i Collection
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.SiteID,
|
||||
&i.ContainerHtml,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const upsertCollection = `-- name: UpsertCollection :one
|
||||
INSERT INTO collections (id, site_id, container_html, last_edited_by)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT(id, site_id) DO UPDATE SET
|
||||
container_html = EXCLUDED.container_html,
|
||||
last_edited_by = EXCLUDED.last_edited_by
|
||||
RETURNING id, site_id, container_html, created_at, updated_at, last_edited_by
|
||||
`
|
||||
|
||||
type UpsertCollectionParams struct {
|
||||
ID string `json:"id"`
|
||||
SiteID string `json:"site_id"`
|
||||
ContainerHtml string `json:"container_html"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpsertCollection(ctx context.Context, arg UpsertCollectionParams) (Collection, error) {
|
||||
row := q.db.QueryRowContext(ctx, upsertCollection,
|
||||
arg.ID,
|
||||
arg.SiteID,
|
||||
arg.ContainerHtml,
|
||||
arg.LastEditedBy,
|
||||
)
|
||||
var i Collection
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.SiteID,
|
||||
&i.ContainerHtml,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -8,6 +8,49 @@ import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type Collection struct {
|
||||
ID string `json:"id"`
|
||||
SiteID string `json:"site_id"`
|
||||
ContainerHtml string `json:"container_html"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
}
|
||||
|
||||
type CollectionItem struct {
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
TemplateID int32 `json:"template_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
Position int32 `json:"position"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
}
|
||||
|
||||
type CollectionItemVersion struct {
|
||||
VersionID int32 `json:"version_id"`
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
TemplateID int32 `json:"template_id"`
|
||||
Position int32 `json:"position"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
}
|
||||
|
||||
type CollectionTemplate struct {
|
||||
TemplateID int32 `json:"template_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
Name string `json:"name"`
|
||||
HtmlTemplate string `json:"html_template"`
|
||||
IsDefault bool `json:"is_default"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
}
|
||||
|
||||
type Content struct {
|
||||
ID string `json:"id"`
|
||||
SiteID string `json:"site_id"`
|
||||
|
||||
@@ -9,24 +9,70 @@ import (
|
||||
)
|
||||
|
||||
type Querier interface {
|
||||
CreateCollection(ctx context.Context, arg CreateCollectionParams) (Collection, error)
|
||||
CreateCollectionItem(ctx context.Context, arg CreateCollectionItemParams) (CollectionItem, error)
|
||||
// Collection item versions table queries
|
||||
CreateCollectionItemVersion(ctx context.Context, arg CreateCollectionItemVersionParams) error
|
||||
CreateCollectionItemVersionsLookupIndex(ctx context.Context) error
|
||||
CreateCollectionItemsLookupIndex(ctx context.Context) error
|
||||
CreateCollectionItemsTemplateIndex(ctx context.Context) error
|
||||
CreateCollectionTemplate(ctx context.Context, arg CreateCollectionTemplateParams) (CollectionTemplate, error)
|
||||
CreateCollectionTemplatesDefaultIndex(ctx context.Context) error
|
||||
CreateCollectionTemplatesLookupIndex(ctx context.Context) error
|
||||
CreateCollectionTemplatesOneDefaultIndex(ctx context.Context) error
|
||||
CreateCollectionsSiteIndex(ctx context.Context) error
|
||||
CreateCollectionsUpdatedAtIndex(ctx context.Context) error
|
||||
CreateContent(ctx context.Context, arg CreateContentParams) (Content, error)
|
||||
CreateContentSiteIndex(ctx context.Context) error
|
||||
CreateContentUpdatedAtIndex(ctx context.Context) error
|
||||
CreateContentVersion(ctx context.Context, arg CreateContentVersionParams) error
|
||||
CreateUpdateFunction(ctx context.Context) error
|
||||
CreateVersionsLookupIndex(ctx context.Context) error
|
||||
DeleteAllSiteCollections(ctx context.Context, siteID string) error
|
||||
DeleteAllSiteContent(ctx context.Context, siteID string) error
|
||||
DeleteCollection(ctx context.Context, arg DeleteCollectionParams) error
|
||||
DeleteCollectionItem(ctx context.Context, arg DeleteCollectionItemParams) error
|
||||
DeleteCollectionItems(ctx context.Context, arg DeleteCollectionItemsParams) error
|
||||
DeleteCollectionTemplate(ctx context.Context, templateID int32) error
|
||||
DeleteCollectionTemplates(ctx context.Context, arg DeleteCollectionTemplatesParams) error
|
||||
DeleteContent(ctx context.Context, arg DeleteContentParams) error
|
||||
DeleteOldCollectionItemVersions(ctx context.Context, arg DeleteOldCollectionItemVersionsParams) error
|
||||
DeleteOldVersions(ctx context.Context, arg DeleteOldVersionsParams) error
|
||||
GetAllCollectionItemVersionsForSite(ctx context.Context, arg GetAllCollectionItemVersionsForSiteParams) ([]GetAllCollectionItemVersionsForSiteRow, error)
|
||||
GetAllCollections(ctx context.Context, siteID string) ([]Collection, error)
|
||||
GetAllContent(ctx context.Context, siteID string) ([]Content, error)
|
||||
GetAllVersionsForSite(ctx context.Context, arg GetAllVersionsForSiteParams) ([]GetAllVersionsForSiteRow, error)
|
||||
GetBulkContent(ctx context.Context, arg GetBulkContentParams) ([]Content, error)
|
||||
// Collections table queries
|
||||
GetCollection(ctx context.Context, arg GetCollectionParams) (Collection, error)
|
||||
// Collection items table queries
|
||||
GetCollectionItem(ctx context.Context, arg GetCollectionItemParams) (CollectionItem, error)
|
||||
GetCollectionItemVersion(ctx context.Context, versionID int32) (CollectionItemVersion, error)
|
||||
GetCollectionItemVersionHistory(ctx context.Context, arg GetCollectionItemVersionHistoryParams) ([]CollectionItemVersion, error)
|
||||
GetCollectionItems(ctx context.Context, arg GetCollectionItemsParams) ([]CollectionItem, error)
|
||||
GetCollectionItemsWithTemplate(ctx context.Context, arg GetCollectionItemsWithTemplateParams) ([]GetCollectionItemsWithTemplateRow, error)
|
||||
// Collection templates table queries
|
||||
GetCollectionTemplate(ctx context.Context, templateID int32) (CollectionTemplate, error)
|
||||
GetCollectionTemplates(ctx context.Context, arg GetCollectionTemplatesParams) ([]CollectionTemplate, error)
|
||||
GetContent(ctx context.Context, arg GetContentParams) (Content, error)
|
||||
GetContentVersion(ctx context.Context, versionID int32) (ContentVersion, error)
|
||||
GetContentVersionHistory(ctx context.Context, arg GetContentVersionHistoryParams) ([]ContentVersion, error)
|
||||
GetDefaultTemplate(ctx context.Context, arg GetDefaultTemplateParams) (CollectionTemplate, error)
|
||||
GetMaxPosition(ctx context.Context, arg GetMaxPositionParams) (interface{}, error)
|
||||
InitializeCollectionItemVersionsTable(ctx context.Context) error
|
||||
InitializeCollectionItemsTable(ctx context.Context) error
|
||||
InitializeCollectionTemplatesTable(ctx context.Context) error
|
||||
InitializeCollectionsTable(ctx context.Context) error
|
||||
InitializeSchema(ctx context.Context) error
|
||||
InitializeVersionsTable(ctx context.Context) error
|
||||
ReorderCollectionItems(ctx context.Context, arg ReorderCollectionItemsParams) error
|
||||
SetTemplateAsDefault(ctx context.Context, arg SetTemplateAsDefaultParams) error
|
||||
UpdateCollection(ctx context.Context, arg UpdateCollectionParams) (Collection, error)
|
||||
UpdateCollectionItem(ctx context.Context, arg UpdateCollectionItemParams) (CollectionItem, error)
|
||||
UpdateCollectionItemPosition(ctx context.Context, arg UpdateCollectionItemPositionParams) error
|
||||
UpdateCollectionTemplate(ctx context.Context, arg UpdateCollectionTemplateParams) (CollectionTemplate, error)
|
||||
UpdateContent(ctx context.Context, arg UpdateContentParams) (Content, error)
|
||||
UpsertCollection(ctx context.Context, arg UpsertCollectionParams) (Collection, error)
|
||||
UpsertContent(ctx context.Context, arg UpsertContentParams) (Content, error)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,11 +22,78 @@ CREATE TABLE content_versions (
|
||||
created_by TEXT DEFAULT 'system' NOT NULL
|
||||
);
|
||||
|
||||
-- Collections table - manages .insertr-add containers
|
||||
CREATE TABLE collections (
|
||||
id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
container_html TEXT NOT NULL,
|
||||
created_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()) NOT NULL,
|
||||
updated_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()) NOT NULL,
|
||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||
PRIMARY KEY (id, site_id)
|
||||
);
|
||||
|
||||
-- Collection templates - multiple template variants per collection
|
||||
CREATE TABLE collection_templates (
|
||||
template_id SERIAL PRIMARY KEY,
|
||||
collection_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
html_template TEXT NOT NULL,
|
||||
is_default BOOLEAN DEFAULT FALSE NOT NULL,
|
||||
created_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()) NOT NULL,
|
||||
FOREIGN KEY (collection_id, site_id) REFERENCES collections(id, site_id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Collection items - individual items within collections
|
||||
CREATE TABLE collection_items (
|
||||
item_id TEXT NOT NULL,
|
||||
collection_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
template_id INTEGER NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
created_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()) NOT NULL,
|
||||
updated_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()) NOT NULL,
|
||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||
PRIMARY KEY (item_id, collection_id, site_id),
|
||||
FOREIGN KEY (collection_id, site_id) REFERENCES collections(id, site_id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (template_id) REFERENCES collection_templates(template_id) ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
-- Collection item version history
|
||||
CREATE TABLE collection_item_versions (
|
||||
version_id SERIAL PRIMARY KEY,
|
||||
item_id TEXT NOT NULL,
|
||||
collection_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
template_id INTEGER NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
created_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()) NOT NULL,
|
||||
created_by TEXT DEFAULT 'system' NOT NULL,
|
||||
FOREIGN KEY (item_id, collection_id, site_id) REFERENCES collection_items(item_id, collection_id, site_id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Indexes for performance
|
||||
CREATE INDEX IF NOT EXISTS idx_content_site_id ON content(site_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_content_updated_at ON content(updated_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_content_versions_lookup ON content_versions(content_id, site_id, created_at DESC);
|
||||
|
||||
-- Collection indexes for performance
|
||||
CREATE INDEX IF NOT EXISTS idx_collections_site_id ON collections(site_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_collections_updated_at ON collections(updated_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_templates_lookup ON collection_templates(collection_id, site_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_templates_default ON collection_templates(collection_id, site_id, is_default DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_items_lookup ON collection_items(collection_id, site_id, position);
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_items_template ON collection_items(template_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_item_versions_lookup ON collection_item_versions(item_id, collection_id, site_id, created_at DESC);
|
||||
|
||||
-- Constraint to ensure only one default template per collection
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_collection_templates_one_default
|
||||
ON collection_templates(collection_id, site_id)
|
||||
WHERE is_default = TRUE;
|
||||
|
||||
-- Function and trigger to automatically update updated_at timestamp
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
@@ -39,4 +106,15 @@ $$ language 'plpgsql';
|
||||
CREATE TRIGGER update_content_updated_at
|
||||
BEFORE UPDATE ON content
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Triggers for collection timestamps
|
||||
CREATE TRIGGER update_collections_updated_at
|
||||
BEFORE UPDATE ON collections
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_collection_items_updated_at
|
||||
BEFORE UPDATE ON collection_items
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
@@ -21,6 +21,59 @@ CREATE TABLE IF NOT EXISTS content_versions (
|
||||
created_by TEXT DEFAULT 'system' NOT NULL
|
||||
);
|
||||
|
||||
-- name: InitializeCollectionsTable :exec
|
||||
CREATE TABLE IF NOT EXISTS collections (
|
||||
id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
container_html TEXT NOT NULL,
|
||||
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
updated_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||
PRIMARY KEY (id, site_id)
|
||||
);
|
||||
|
||||
-- name: InitializeCollectionTemplatesTable :exec
|
||||
CREATE TABLE IF NOT EXISTS collection_templates (
|
||||
template_id SERIAL PRIMARY KEY,
|
||||
collection_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
html_template TEXT NOT NULL,
|
||||
is_default BOOLEAN DEFAULT FALSE NOT NULL,
|
||||
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
FOREIGN KEY (collection_id, site_id) REFERENCES collections(id, site_id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- name: InitializeCollectionItemsTable :exec
|
||||
CREATE TABLE IF NOT EXISTS collection_items (
|
||||
item_id TEXT NOT NULL,
|
||||
collection_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
template_id INTEGER NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
updated_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||
PRIMARY KEY (item_id, collection_id, site_id),
|
||||
FOREIGN KEY (collection_id, site_id) REFERENCES collections(id, site_id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (template_id) REFERENCES collection_templates(template_id) ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
-- name: InitializeCollectionItemVersionsTable :exec
|
||||
CREATE TABLE IF NOT EXISTS collection_item_versions (
|
||||
version_id SERIAL PRIMARY KEY,
|
||||
item_id TEXT NOT NULL,
|
||||
collection_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
template_id INTEGER NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
created_by TEXT DEFAULT 'system' NOT NULL,
|
||||
FOREIGN KEY (item_id, collection_id, site_id) REFERENCES collection_items(item_id, collection_id, site_id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- name: CreateContentSiteIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_content_site_id ON content(site_id);
|
||||
|
||||
@@ -30,6 +83,32 @@ CREATE INDEX IF NOT EXISTS idx_content_updated_at ON content(updated_at);
|
||||
-- name: CreateVersionsLookupIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_content_versions_lookup ON content_versions(content_id, site_id, created_at DESC);
|
||||
|
||||
-- name: CreateCollectionsSiteIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collections_site_id ON collections(site_id);
|
||||
|
||||
-- name: CreateCollectionsUpdatedAtIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collections_updated_at ON collections(updated_at);
|
||||
|
||||
-- name: CreateCollectionTemplatesLookupIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_templates_lookup ON collection_templates(collection_id, site_id);
|
||||
|
||||
-- name: CreateCollectionTemplatesDefaultIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_templates_default ON collection_templates(collection_id, site_id, is_default DESC);
|
||||
|
||||
-- name: CreateCollectionItemsLookupIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_items_lookup ON collection_items(collection_id, site_id, position);
|
||||
|
||||
-- name: CreateCollectionItemsTemplateIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_items_template ON collection_items(template_id);
|
||||
|
||||
-- name: CreateCollectionItemVersionsLookupIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_item_versions_lookup ON collection_item_versions(item_id, collection_id, site_id, created_at DESC);
|
||||
|
||||
-- name: CreateCollectionTemplatesOneDefaultIndex :exec
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_collection_templates_one_default
|
||||
ON collection_templates(collection_id, site_id)
|
||||
WHERE is_default = TRUE;
|
||||
|
||||
-- name: CreateUpdateFunction :exec
|
||||
CREATE OR REPLACE FUNCTION update_content_timestamp()
|
||||
RETURNS TRIGGER AS $$
|
||||
@@ -44,4 +123,18 @@ DROP TRIGGER IF EXISTS update_content_updated_at ON content;
|
||||
CREATE TRIGGER update_content_updated_at
|
||||
BEFORE UPDATE ON content
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_content_timestamp();
|
||||
|
||||
-- name: CreateCollectionsUpdateTrigger :exec
|
||||
DROP TRIGGER IF EXISTS update_collections_updated_at ON collections;
|
||||
CREATE TRIGGER update_collections_updated_at
|
||||
BEFORE UPDATE ON collections
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_content_timestamp();
|
||||
|
||||
-- name: CreateCollectionItemsUpdateTrigger :exec
|
||||
DROP TRIGGER IF EXISTS update_collection_items_updated_at ON collection_items;
|
||||
CREATE TRIGGER update_collection_items_updated_at
|
||||
BEFORE UPDATE ON collection_items
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_content_timestamp();
|
||||
@@ -9,6 +9,80 @@ import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const createCollectionItemVersionsLookupIndex = `-- name: CreateCollectionItemVersionsLookupIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_item_versions_lookup ON collection_item_versions(item_id, collection_id, site_id, created_at DESC)
|
||||
`
|
||||
|
||||
func (q *Queries) CreateCollectionItemVersionsLookupIndex(ctx context.Context) error {
|
||||
_, err := q.db.ExecContext(ctx, createCollectionItemVersionsLookupIndex)
|
||||
return err
|
||||
}
|
||||
|
||||
const createCollectionItemsLookupIndex = `-- name: CreateCollectionItemsLookupIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_items_lookup ON collection_items(collection_id, site_id, position)
|
||||
`
|
||||
|
||||
func (q *Queries) CreateCollectionItemsLookupIndex(ctx context.Context) error {
|
||||
_, err := q.db.ExecContext(ctx, createCollectionItemsLookupIndex)
|
||||
return err
|
||||
}
|
||||
|
||||
const createCollectionItemsTemplateIndex = `-- name: CreateCollectionItemsTemplateIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_items_template ON collection_items(template_id)
|
||||
`
|
||||
|
||||
func (q *Queries) CreateCollectionItemsTemplateIndex(ctx context.Context) error {
|
||||
_, err := q.db.ExecContext(ctx, createCollectionItemsTemplateIndex)
|
||||
return err
|
||||
}
|
||||
|
||||
const createCollectionTemplatesDefaultIndex = `-- name: CreateCollectionTemplatesDefaultIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_templates_default ON collection_templates(collection_id, site_id, is_default DESC)
|
||||
`
|
||||
|
||||
func (q *Queries) CreateCollectionTemplatesDefaultIndex(ctx context.Context) error {
|
||||
_, err := q.db.ExecContext(ctx, createCollectionTemplatesDefaultIndex)
|
||||
return err
|
||||
}
|
||||
|
||||
const createCollectionTemplatesLookupIndex = `-- name: CreateCollectionTemplatesLookupIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_templates_lookup ON collection_templates(collection_id, site_id)
|
||||
`
|
||||
|
||||
func (q *Queries) CreateCollectionTemplatesLookupIndex(ctx context.Context) error {
|
||||
_, err := q.db.ExecContext(ctx, createCollectionTemplatesLookupIndex)
|
||||
return err
|
||||
}
|
||||
|
||||
const createCollectionTemplatesOneDefaultIndex = `-- name: CreateCollectionTemplatesOneDefaultIndex :exec
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_collection_templates_one_default
|
||||
ON collection_templates(collection_id, site_id)
|
||||
WHERE is_default = TRUE
|
||||
`
|
||||
|
||||
func (q *Queries) CreateCollectionTemplatesOneDefaultIndex(ctx context.Context) error {
|
||||
_, err := q.db.ExecContext(ctx, createCollectionTemplatesOneDefaultIndex)
|
||||
return err
|
||||
}
|
||||
|
||||
const createCollectionsSiteIndex = `-- name: CreateCollectionsSiteIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collections_site_id ON collections(site_id)
|
||||
`
|
||||
|
||||
func (q *Queries) CreateCollectionsSiteIndex(ctx context.Context) error {
|
||||
_, err := q.db.ExecContext(ctx, createCollectionsSiteIndex)
|
||||
return err
|
||||
}
|
||||
|
||||
const createCollectionsUpdatedAtIndex = `-- name: CreateCollectionsUpdatedAtIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collections_updated_at ON collections(updated_at)
|
||||
`
|
||||
|
||||
func (q *Queries) CreateCollectionsUpdatedAtIndex(ctx context.Context) error {
|
||||
_, err := q.db.ExecContext(ctx, createCollectionsUpdatedAtIndex)
|
||||
return err
|
||||
}
|
||||
|
||||
const createContentSiteIndex = `-- name: CreateContentSiteIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_content_site_id ON content(site_id)
|
||||
`
|
||||
@@ -51,6 +125,83 @@ func (q *Queries) CreateVersionsLookupIndex(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
const initializeCollectionItemVersionsTable = `-- name: InitializeCollectionItemVersionsTable :exec
|
||||
CREATE TABLE IF NOT EXISTS collection_item_versions (
|
||||
version_id SERIAL PRIMARY KEY,
|
||||
item_id TEXT NOT NULL,
|
||||
collection_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
template_id INTEGER NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
created_by TEXT DEFAULT 'system' NOT NULL,
|
||||
FOREIGN KEY (item_id, collection_id, site_id) REFERENCES collection_items(item_id, collection_id, site_id) ON DELETE CASCADE
|
||||
)
|
||||
`
|
||||
|
||||
func (q *Queries) InitializeCollectionItemVersionsTable(ctx context.Context) error {
|
||||
_, err := q.db.ExecContext(ctx, initializeCollectionItemVersionsTable)
|
||||
return err
|
||||
}
|
||||
|
||||
const initializeCollectionItemsTable = `-- name: InitializeCollectionItemsTable :exec
|
||||
CREATE TABLE IF NOT EXISTS collection_items (
|
||||
item_id TEXT NOT NULL,
|
||||
collection_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
template_id INTEGER NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
updated_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||
PRIMARY KEY (item_id, collection_id, site_id),
|
||||
FOREIGN KEY (collection_id, site_id) REFERENCES collections(id, site_id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (template_id) REFERENCES collection_templates(template_id) ON DELETE RESTRICT
|
||||
)
|
||||
`
|
||||
|
||||
func (q *Queries) InitializeCollectionItemsTable(ctx context.Context) error {
|
||||
_, err := q.db.ExecContext(ctx, initializeCollectionItemsTable)
|
||||
return err
|
||||
}
|
||||
|
||||
const initializeCollectionTemplatesTable = `-- name: InitializeCollectionTemplatesTable :exec
|
||||
CREATE TABLE IF NOT EXISTS collection_templates (
|
||||
template_id SERIAL PRIMARY KEY,
|
||||
collection_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
html_template TEXT NOT NULL,
|
||||
is_default BOOLEAN DEFAULT FALSE NOT NULL,
|
||||
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
FOREIGN KEY (collection_id, site_id) REFERENCES collections(id, site_id) ON DELETE CASCADE
|
||||
)
|
||||
`
|
||||
|
||||
func (q *Queries) InitializeCollectionTemplatesTable(ctx context.Context) error {
|
||||
_, err := q.db.ExecContext(ctx, initializeCollectionTemplatesTable)
|
||||
return err
|
||||
}
|
||||
|
||||
const initializeCollectionsTable = `-- name: InitializeCollectionsTable :exec
|
||||
CREATE TABLE IF NOT EXISTS collections (
|
||||
id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
container_html TEXT NOT NULL,
|
||||
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
updated_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||
PRIMARY KEY (id, site_id)
|
||||
)
|
||||
`
|
||||
|
||||
func (q *Queries) InitializeCollectionsTable(ctx context.Context) error {
|
||||
_, err := q.db.ExecContext(ctx, initializeCollectionsTable)
|
||||
return err
|
||||
}
|
||||
|
||||
const initializeSchema = `-- name: InitializeSchema :exec
|
||||
CREATE TABLE IF NOT EXISTS content (
|
||||
id TEXT NOT NULL,
|
||||
|
||||
31
internal/db/queries/collection_item_versions.sql
Normal file
31
internal/db/queries/collection_item_versions.sql
Normal file
@@ -0,0 +1,31 @@
|
||||
-- Collection item versions table queries
|
||||
|
||||
-- name: CreateCollectionItemVersion :exec
|
||||
INSERT INTO collection_item_versions (item_id, collection_id, site_id, html_content, template_id, position, created_by)
|
||||
VALUES (sqlc.arg(item_id), sqlc.arg(collection_id), sqlc.arg(site_id), sqlc.arg(html_content), sqlc.arg(template_id), sqlc.arg(position), sqlc.arg(created_by));
|
||||
|
||||
-- name: GetCollectionItemVersionHistory :many
|
||||
SELECT version_id, item_id, collection_id, site_id, html_content, template_id, position, created_at, created_by
|
||||
FROM collection_item_versions
|
||||
WHERE item_id = sqlc.arg(item_id) AND collection_id = sqlc.arg(collection_id) AND site_id = sqlc.arg(site_id)
|
||||
ORDER BY created_at DESC
|
||||
LIMIT sqlc.arg(limit_count);
|
||||
|
||||
-- name: GetCollectionItemVersion :one
|
||||
SELECT version_id, item_id, collection_id, site_id, html_content, template_id, position, created_at, created_by
|
||||
FROM collection_item_versions
|
||||
WHERE version_id = sqlc.arg(version_id);
|
||||
|
||||
-- name: GetAllCollectionItemVersionsForSite :many
|
||||
SELECT
|
||||
civ.version_id, civ.item_id, civ.collection_id, civ.site_id, civ.html_content, civ.template_id, civ.position, civ.created_at, civ.created_by,
|
||||
ci.html_content as current_html_content, ci.position as current_position
|
||||
FROM collection_item_versions civ
|
||||
LEFT JOIN collection_items ci ON civ.item_id = ci.item_id AND civ.collection_id = ci.collection_id AND civ.site_id = ci.site_id
|
||||
WHERE civ.site_id = sqlc.arg(site_id)
|
||||
ORDER BY civ.created_at DESC
|
||||
LIMIT sqlc.arg(limit_count);
|
||||
|
||||
-- name: DeleteOldCollectionItemVersions :exec
|
||||
DELETE FROM collection_item_versions
|
||||
WHERE created_at < sqlc.arg(created_before) AND site_id = sqlc.arg(site_id);
|
||||
57
internal/db/queries/collection_items.sql
Normal file
57
internal/db/queries/collection_items.sql
Normal file
@@ -0,0 +1,57 @@
|
||||
-- Collection items table queries
|
||||
|
||||
-- name: GetCollectionItem :one
|
||||
SELECT item_id, collection_id, site_id, template_id, html_content, position, created_at, updated_at, last_edited_by
|
||||
FROM collection_items
|
||||
WHERE item_id = sqlc.arg(item_id) AND collection_id = sqlc.arg(collection_id) AND site_id = sqlc.arg(site_id);
|
||||
|
||||
-- name: GetCollectionItems :many
|
||||
SELECT item_id, collection_id, site_id, template_id, html_content, position, created_at, updated_at, last_edited_by
|
||||
FROM collection_items
|
||||
WHERE collection_id = sqlc.arg(collection_id) AND site_id = sqlc.arg(site_id)
|
||||
ORDER BY position ASC;
|
||||
|
||||
-- name: GetCollectionItemsWithTemplate :many
|
||||
SELECT
|
||||
ci.item_id, ci.collection_id, ci.site_id, ci.template_id, ci.html_content, ci.position, ci.created_at, ci.updated_at, ci.last_edited_by,
|
||||
ct.name as template_name, ct.html_template, ct.is_default
|
||||
FROM collection_items ci
|
||||
JOIN collection_templates ct ON ci.template_id = ct.template_id
|
||||
WHERE ci.collection_id = sqlc.arg(collection_id) AND ci.site_id = sqlc.arg(site_id)
|
||||
ORDER BY ci.position ASC;
|
||||
|
||||
-- name: GetMaxPosition :one
|
||||
SELECT COALESCE(MAX(position), 0) as max_position
|
||||
FROM collection_items
|
||||
WHERE collection_id = sqlc.arg(collection_id) AND site_id = sqlc.arg(site_id);
|
||||
|
||||
-- name: CreateCollectionItem :one
|
||||
INSERT INTO collection_items (item_id, collection_id, site_id, template_id, html_content, position, last_edited_by)
|
||||
VALUES (sqlc.arg(item_id), sqlc.arg(collection_id), sqlc.arg(site_id), sqlc.arg(template_id), sqlc.arg(html_content), sqlc.arg(position), sqlc.arg(last_edited_by))
|
||||
RETURNING item_id, collection_id, site_id, template_id, html_content, position, created_at, updated_at, last_edited_by;
|
||||
|
||||
-- name: UpdateCollectionItem :one
|
||||
UPDATE collection_items
|
||||
SET html_content = sqlc.arg(html_content), last_edited_by = sqlc.arg(last_edited_by)
|
||||
WHERE item_id = sqlc.arg(item_id) AND collection_id = sqlc.arg(collection_id) AND site_id = sqlc.arg(site_id)
|
||||
RETURNING item_id, collection_id, site_id, template_id, html_content, position, created_at, updated_at, last_edited_by;
|
||||
|
||||
-- name: UpdateCollectionItemPosition :exec
|
||||
UPDATE collection_items
|
||||
SET position = sqlc.arg(position)
|
||||
WHERE item_id = sqlc.arg(item_id) AND collection_id = sqlc.arg(collection_id) AND site_id = sqlc.arg(site_id);
|
||||
|
||||
-- name: ReorderCollectionItems :exec
|
||||
UPDATE collection_items
|
||||
SET position = position + sqlc.arg(position_delta)
|
||||
WHERE collection_id = sqlc.arg(collection_id) AND site_id = sqlc.arg(site_id)
|
||||
AND position >= sqlc.arg(start_position);
|
||||
|
||||
-- name: DeleteCollectionItem :exec
|
||||
DELETE FROM collection_items
|
||||
WHERE item_id = sqlc.arg(item_id) AND collection_id = sqlc.arg(collection_id) AND site_id = sqlc.arg(site_id);
|
||||
|
||||
-- name: DeleteCollectionItems :exec
|
||||
DELETE FROM collection_items
|
||||
WHERE collection_id = sqlc.arg(collection_id) AND site_id = sqlc.arg(site_id);
|
||||
|
||||
45
internal/db/queries/collection_templates.sql
Normal file
45
internal/db/queries/collection_templates.sql
Normal file
@@ -0,0 +1,45 @@
|
||||
-- Collection templates table queries
|
||||
|
||||
-- name: GetCollectionTemplate :one
|
||||
SELECT template_id, collection_id, site_id, name, html_template, is_default, created_at
|
||||
FROM collection_templates
|
||||
WHERE template_id = sqlc.arg(template_id);
|
||||
|
||||
-- name: GetCollectionTemplates :many
|
||||
SELECT template_id, collection_id, site_id, name, html_template, is_default, created_at
|
||||
FROM collection_templates
|
||||
WHERE collection_id = sqlc.arg(collection_id) AND site_id = sqlc.arg(site_id)
|
||||
ORDER BY is_default DESC, created_at ASC;
|
||||
|
||||
-- name: GetDefaultTemplate :one
|
||||
SELECT template_id, collection_id, site_id, name, html_template, is_default, created_at
|
||||
FROM collection_templates
|
||||
WHERE collection_id = sqlc.arg(collection_id) AND site_id = sqlc.arg(site_id) AND is_default = TRUE
|
||||
LIMIT 1;
|
||||
|
||||
-- name: CreateCollectionTemplate :one
|
||||
INSERT INTO collection_templates (collection_id, site_id, name, html_template, is_default)
|
||||
VALUES (sqlc.arg(collection_id), sqlc.arg(site_id), sqlc.arg(name), sqlc.arg(html_template), sqlc.arg(is_default))
|
||||
RETURNING template_id, collection_id, site_id, name, html_template, is_default, created_at;
|
||||
|
||||
-- name: UpdateCollectionTemplate :one
|
||||
UPDATE collection_templates
|
||||
SET name = sqlc.arg(name), html_template = sqlc.arg(html_template), is_default = sqlc.arg(is_default)
|
||||
WHERE template_id = sqlc.arg(template_id)
|
||||
RETURNING template_id, collection_id, site_id, name, html_template, is_default, created_at;
|
||||
|
||||
-- name: SetTemplateAsDefault :exec
|
||||
UPDATE collection_templates
|
||||
SET is_default = CASE
|
||||
WHEN template_id = sqlc.arg(template_id) THEN TRUE
|
||||
ELSE FALSE
|
||||
END
|
||||
WHERE collection_id = sqlc.arg(collection_id) AND site_id = sqlc.arg(site_id);
|
||||
|
||||
-- name: DeleteCollectionTemplate :exec
|
||||
DELETE FROM collection_templates
|
||||
WHERE template_id = sqlc.arg(template_id);
|
||||
|
||||
-- name: DeleteCollectionTemplates :exec
|
||||
DELETE FROM collection_templates
|
||||
WHERE collection_id = sqlc.arg(collection_id) AND site_id = sqlc.arg(site_id);
|
||||
39
internal/db/queries/collections.sql
Normal file
39
internal/db/queries/collections.sql
Normal file
@@ -0,0 +1,39 @@
|
||||
-- Collections table queries
|
||||
|
||||
-- name: GetCollection :one
|
||||
SELECT id, site_id, container_html, created_at, updated_at, last_edited_by
|
||||
FROM collections
|
||||
WHERE id = sqlc.arg(id) AND site_id = sqlc.arg(site_id);
|
||||
|
||||
-- name: GetAllCollections :many
|
||||
SELECT id, site_id, container_html, created_at, updated_at, last_edited_by
|
||||
FROM collections
|
||||
WHERE site_id = sqlc.arg(site_id)
|
||||
ORDER BY updated_at DESC;
|
||||
|
||||
-- name: CreateCollection :one
|
||||
INSERT INTO collections (id, site_id, container_html, last_edited_by)
|
||||
VALUES (sqlc.arg(id), sqlc.arg(site_id), sqlc.arg(container_html), sqlc.arg(last_edited_by))
|
||||
RETURNING id, site_id, container_html, created_at, updated_at, last_edited_by;
|
||||
|
||||
-- name: UpdateCollection :one
|
||||
UPDATE collections
|
||||
SET container_html = sqlc.arg(container_html), last_edited_by = sqlc.arg(last_edited_by)
|
||||
WHERE id = sqlc.arg(id) AND site_id = sqlc.arg(site_id)
|
||||
RETURNING id, site_id, container_html, created_at, updated_at, last_edited_by;
|
||||
|
||||
-- name: UpsertCollection :one
|
||||
INSERT INTO collections (id, site_id, container_html, last_edited_by)
|
||||
VALUES (sqlc.arg(id), sqlc.arg(site_id), sqlc.arg(container_html), sqlc.arg(last_edited_by))
|
||||
ON CONFLICT(id, site_id) DO UPDATE SET
|
||||
container_html = EXCLUDED.container_html,
|
||||
last_edited_by = EXCLUDED.last_edited_by
|
||||
RETURNING id, site_id, container_html, created_at, updated_at, last_edited_by;
|
||||
|
||||
-- name: DeleteCollection :exec
|
||||
DELETE FROM collections
|
||||
WHERE id = sqlc.arg(id) AND site_id = sqlc.arg(site_id);
|
||||
|
||||
-- name: DeleteAllSiteCollections :exec
|
||||
DELETE FROM collections
|
||||
WHERE site_id = sqlc.arg(site_id);
|
||||
197
internal/db/sqlite/collection_item_versions.sql.go
Normal file
197
internal/db/sqlite/collection_item_versions.sql.go
Normal file
@@ -0,0 +1,197 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
// source: collection_item_versions.sql
|
||||
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
const createCollectionItemVersion = `-- name: CreateCollectionItemVersion :exec
|
||||
|
||||
INSERT INTO collection_item_versions (item_id, collection_id, site_id, html_content, template_id, position, created_by)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)
|
||||
`
|
||||
|
||||
type CreateCollectionItemVersionParams struct {
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
TemplateID int64 `json:"template_id"`
|
||||
Position int64 `json:"position"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
}
|
||||
|
||||
// Collection item versions table queries
|
||||
func (q *Queries) CreateCollectionItemVersion(ctx context.Context, arg CreateCollectionItemVersionParams) error {
|
||||
_, err := q.db.ExecContext(ctx, createCollectionItemVersion,
|
||||
arg.ItemID,
|
||||
arg.CollectionID,
|
||||
arg.SiteID,
|
||||
arg.HtmlContent,
|
||||
arg.TemplateID,
|
||||
arg.Position,
|
||||
arg.CreatedBy,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteOldCollectionItemVersions = `-- name: DeleteOldCollectionItemVersions :exec
|
||||
DELETE FROM collection_item_versions
|
||||
WHERE created_at < ?1 AND site_id = ?2
|
||||
`
|
||||
|
||||
type DeleteOldCollectionItemVersionsParams struct {
|
||||
CreatedBefore int64 `json:"created_before"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteOldCollectionItemVersions(ctx context.Context, arg DeleteOldCollectionItemVersionsParams) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteOldCollectionItemVersions, arg.CreatedBefore, arg.SiteID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getAllCollectionItemVersionsForSite = `-- name: GetAllCollectionItemVersionsForSite :many
|
||||
SELECT
|
||||
civ.version_id, civ.item_id, civ.collection_id, civ.site_id, civ.html_content, civ.template_id, civ.position, civ.created_at, civ.created_by,
|
||||
ci.html_content as current_html_content, ci.position as current_position
|
||||
FROM collection_item_versions civ
|
||||
LEFT JOIN collection_items ci ON civ.item_id = ci.item_id AND civ.collection_id = ci.collection_id AND civ.site_id = ci.site_id
|
||||
WHERE civ.site_id = ?1
|
||||
ORDER BY civ.created_at DESC
|
||||
LIMIT ?2
|
||||
`
|
||||
|
||||
type GetAllCollectionItemVersionsForSiteParams struct {
|
||||
SiteID string `json:"site_id"`
|
||||
LimitCount int64 `json:"limit_count"`
|
||||
}
|
||||
|
||||
type GetAllCollectionItemVersionsForSiteRow struct {
|
||||
VersionID int64 `json:"version_id"`
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
TemplateID int64 `json:"template_id"`
|
||||
Position int64 `json:"position"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
CurrentHtmlContent sql.NullString `json:"current_html_content"`
|
||||
CurrentPosition sql.NullInt64 `json:"current_position"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetAllCollectionItemVersionsForSite(ctx context.Context, arg GetAllCollectionItemVersionsForSiteParams) ([]GetAllCollectionItemVersionsForSiteRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getAllCollectionItemVersionsForSite, arg.SiteID, arg.LimitCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetAllCollectionItemVersionsForSiteRow
|
||||
for rows.Next() {
|
||||
var i GetAllCollectionItemVersionsForSiteRow
|
||||
if err := rows.Scan(
|
||||
&i.VersionID,
|
||||
&i.ItemID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.TemplateID,
|
||||
&i.Position,
|
||||
&i.CreatedAt,
|
||||
&i.CreatedBy,
|
||||
&i.CurrentHtmlContent,
|
||||
&i.CurrentPosition,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getCollectionItemVersion = `-- name: GetCollectionItemVersion :one
|
||||
SELECT version_id, item_id, collection_id, site_id, html_content, template_id, position, created_at, created_by
|
||||
FROM collection_item_versions
|
||||
WHERE version_id = ?1
|
||||
`
|
||||
|
||||
func (q *Queries) GetCollectionItemVersion(ctx context.Context, versionID int64) (CollectionItemVersion, error) {
|
||||
row := q.db.QueryRowContext(ctx, getCollectionItemVersion, versionID)
|
||||
var i CollectionItemVersion
|
||||
err := row.Scan(
|
||||
&i.VersionID,
|
||||
&i.ItemID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.TemplateID,
|
||||
&i.Position,
|
||||
&i.CreatedAt,
|
||||
&i.CreatedBy,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getCollectionItemVersionHistory = `-- name: GetCollectionItemVersionHistory :many
|
||||
SELECT version_id, item_id, collection_id, site_id, html_content, template_id, position, created_at, created_by
|
||||
FROM collection_item_versions
|
||||
WHERE item_id = ?1 AND collection_id = ?2 AND site_id = ?3
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ?4
|
||||
`
|
||||
|
||||
type GetCollectionItemVersionHistoryParams struct {
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
LimitCount int64 `json:"limit_count"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetCollectionItemVersionHistory(ctx context.Context, arg GetCollectionItemVersionHistoryParams) ([]CollectionItemVersion, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getCollectionItemVersionHistory,
|
||||
arg.ItemID,
|
||||
arg.CollectionID,
|
||||
arg.SiteID,
|
||||
arg.LimitCount,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []CollectionItemVersion
|
||||
for rows.Next() {
|
||||
var i CollectionItemVersion
|
||||
if err := rows.Scan(
|
||||
&i.VersionID,
|
||||
&i.ItemID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.HtmlContent,
|
||||
&i.TemplateID,
|
||||
&i.Position,
|
||||
&i.CreatedAt,
|
||||
&i.CreatedBy,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
327
internal/db/sqlite/collection_items.sql.go
Normal file
327
internal/db/sqlite/collection_items.sql.go
Normal file
@@ -0,0 +1,327 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
// source: collection_items.sql
|
||||
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const createCollectionItem = `-- name: CreateCollectionItem :one
|
||||
INSERT INTO collection_items (item_id, collection_id, site_id, template_id, html_content, position, last_edited_by)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)
|
||||
RETURNING item_id, collection_id, site_id, template_id, html_content, position, created_at, updated_at, last_edited_by
|
||||
`
|
||||
|
||||
type CreateCollectionItemParams struct {
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
TemplateID int64 `json:"template_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
Position int64 `json:"position"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateCollectionItem(ctx context.Context, arg CreateCollectionItemParams) (CollectionItem, error) {
|
||||
row := q.db.QueryRowContext(ctx, createCollectionItem,
|
||||
arg.ItemID,
|
||||
arg.CollectionID,
|
||||
arg.SiteID,
|
||||
arg.TemplateID,
|
||||
arg.HtmlContent,
|
||||
arg.Position,
|
||||
arg.LastEditedBy,
|
||||
)
|
||||
var i CollectionItem
|
||||
err := row.Scan(
|
||||
&i.ItemID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.TemplateID,
|
||||
&i.HtmlContent,
|
||||
&i.Position,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteCollectionItem = `-- name: DeleteCollectionItem :exec
|
||||
DELETE FROM collection_items
|
||||
WHERE item_id = ?1 AND collection_id = ?2 AND site_id = ?3
|
||||
`
|
||||
|
||||
type DeleteCollectionItemParams struct {
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteCollectionItem(ctx context.Context, arg DeleteCollectionItemParams) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteCollectionItem, arg.ItemID, arg.CollectionID, arg.SiteID)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteCollectionItems = `-- name: DeleteCollectionItems :exec
|
||||
DELETE FROM collection_items
|
||||
WHERE collection_id = ?1 AND site_id = ?2
|
||||
`
|
||||
|
||||
type DeleteCollectionItemsParams struct {
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteCollectionItems(ctx context.Context, arg DeleteCollectionItemsParams) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteCollectionItems, arg.CollectionID, arg.SiteID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getCollectionItem = `-- name: GetCollectionItem :one
|
||||
|
||||
SELECT item_id, collection_id, site_id, template_id, html_content, position, created_at, updated_at, last_edited_by
|
||||
FROM collection_items
|
||||
WHERE item_id = ?1 AND collection_id = ?2 AND site_id = ?3
|
||||
`
|
||||
|
||||
type GetCollectionItemParams struct {
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
// Collection items table queries
|
||||
func (q *Queries) GetCollectionItem(ctx context.Context, arg GetCollectionItemParams) (CollectionItem, error) {
|
||||
row := q.db.QueryRowContext(ctx, getCollectionItem, arg.ItemID, arg.CollectionID, arg.SiteID)
|
||||
var i CollectionItem
|
||||
err := row.Scan(
|
||||
&i.ItemID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.TemplateID,
|
||||
&i.HtmlContent,
|
||||
&i.Position,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getCollectionItems = `-- name: GetCollectionItems :many
|
||||
SELECT item_id, collection_id, site_id, template_id, html_content, position, created_at, updated_at, last_edited_by
|
||||
FROM collection_items
|
||||
WHERE collection_id = ?1 AND site_id = ?2
|
||||
ORDER BY position ASC
|
||||
`
|
||||
|
||||
type GetCollectionItemsParams struct {
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetCollectionItems(ctx context.Context, arg GetCollectionItemsParams) ([]CollectionItem, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getCollectionItems, arg.CollectionID, arg.SiteID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []CollectionItem
|
||||
for rows.Next() {
|
||||
var i CollectionItem
|
||||
if err := rows.Scan(
|
||||
&i.ItemID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.TemplateID,
|
||||
&i.HtmlContent,
|
||||
&i.Position,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getCollectionItemsWithTemplate = `-- name: GetCollectionItemsWithTemplate :many
|
||||
SELECT
|
||||
ci.item_id, ci.collection_id, ci.site_id, ci.template_id, ci.html_content, ci.position, ci.created_at, ci.updated_at, ci.last_edited_by,
|
||||
ct.name as template_name, ct.html_template, ct.is_default
|
||||
FROM collection_items ci
|
||||
JOIN collection_templates ct ON ci.template_id = ct.template_id
|
||||
WHERE ci.collection_id = ?1 AND ci.site_id = ?2
|
||||
ORDER BY ci.position ASC
|
||||
`
|
||||
|
||||
type GetCollectionItemsWithTemplateParams struct {
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
type GetCollectionItemsWithTemplateRow struct {
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
TemplateID int64 `json:"template_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
Position int64 `json:"position"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
TemplateName string `json:"template_name"`
|
||||
HtmlTemplate string `json:"html_template"`
|
||||
IsDefault int64 `json:"is_default"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetCollectionItemsWithTemplate(ctx context.Context, arg GetCollectionItemsWithTemplateParams) ([]GetCollectionItemsWithTemplateRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getCollectionItemsWithTemplate, arg.CollectionID, arg.SiteID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetCollectionItemsWithTemplateRow
|
||||
for rows.Next() {
|
||||
var i GetCollectionItemsWithTemplateRow
|
||||
if err := rows.Scan(
|
||||
&i.ItemID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.TemplateID,
|
||||
&i.HtmlContent,
|
||||
&i.Position,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
&i.TemplateName,
|
||||
&i.HtmlTemplate,
|
||||
&i.IsDefault,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getMaxPosition = `-- name: GetMaxPosition :one
|
||||
SELECT COALESCE(MAX(position), 0) as max_position
|
||||
FROM collection_items
|
||||
WHERE collection_id = ?1 AND site_id = ?2
|
||||
`
|
||||
|
||||
type GetMaxPositionParams struct {
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetMaxPosition(ctx context.Context, arg GetMaxPositionParams) (interface{}, error) {
|
||||
row := q.db.QueryRowContext(ctx, getMaxPosition, arg.CollectionID, arg.SiteID)
|
||||
var max_position interface{}
|
||||
err := row.Scan(&max_position)
|
||||
return max_position, err
|
||||
}
|
||||
|
||||
const reorderCollectionItems = `-- name: ReorderCollectionItems :exec
|
||||
UPDATE collection_items
|
||||
SET position = position + ?1
|
||||
WHERE collection_id = ?2 AND site_id = ?3
|
||||
AND position >= ?4
|
||||
`
|
||||
|
||||
type ReorderCollectionItemsParams struct {
|
||||
PositionDelta int64 `json:"position_delta"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
StartPosition int64 `json:"start_position"`
|
||||
}
|
||||
|
||||
func (q *Queries) ReorderCollectionItems(ctx context.Context, arg ReorderCollectionItemsParams) error {
|
||||
_, err := q.db.ExecContext(ctx, reorderCollectionItems,
|
||||
arg.PositionDelta,
|
||||
arg.CollectionID,
|
||||
arg.SiteID,
|
||||
arg.StartPosition,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateCollectionItem = `-- name: UpdateCollectionItem :one
|
||||
UPDATE collection_items
|
||||
SET html_content = ?1, last_edited_by = ?2
|
||||
WHERE item_id = ?3 AND collection_id = ?4 AND site_id = ?5
|
||||
RETURNING item_id, collection_id, site_id, template_id, html_content, position, created_at, updated_at, last_edited_by
|
||||
`
|
||||
|
||||
type UpdateCollectionItemParams struct {
|
||||
HtmlContent string `json:"html_content"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateCollectionItem(ctx context.Context, arg UpdateCollectionItemParams) (CollectionItem, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateCollectionItem,
|
||||
arg.HtmlContent,
|
||||
arg.LastEditedBy,
|
||||
arg.ItemID,
|
||||
arg.CollectionID,
|
||||
arg.SiteID,
|
||||
)
|
||||
var i CollectionItem
|
||||
err := row.Scan(
|
||||
&i.ItemID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.TemplateID,
|
||||
&i.HtmlContent,
|
||||
&i.Position,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateCollectionItemPosition = `-- name: UpdateCollectionItemPosition :exec
|
||||
UPDATE collection_items
|
||||
SET position = ?1
|
||||
WHERE item_id = ?2 AND collection_id = ?3 AND site_id = ?4
|
||||
`
|
||||
|
||||
type UpdateCollectionItemPositionParams struct {
|
||||
Position int64 `json:"position"`
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateCollectionItemPosition(ctx context.Context, arg UpdateCollectionItemPositionParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateCollectionItemPosition,
|
||||
arg.Position,
|
||||
arg.ItemID,
|
||||
arg.CollectionID,
|
||||
arg.SiteID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
217
internal/db/sqlite/collection_templates.sql.go
Normal file
217
internal/db/sqlite/collection_templates.sql.go
Normal file
@@ -0,0 +1,217 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
// source: collection_templates.sql
|
||||
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const createCollectionTemplate = `-- name: CreateCollectionTemplate :one
|
||||
INSERT INTO collection_templates (collection_id, site_id, name, html_template, is_default)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5)
|
||||
RETURNING template_id, collection_id, site_id, name, html_template, is_default, created_at
|
||||
`
|
||||
|
||||
type CreateCollectionTemplateParams struct {
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
Name string `json:"name"`
|
||||
HtmlTemplate string `json:"html_template"`
|
||||
IsDefault int64 `json:"is_default"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateCollectionTemplate(ctx context.Context, arg CreateCollectionTemplateParams) (CollectionTemplate, error) {
|
||||
row := q.db.QueryRowContext(ctx, createCollectionTemplate,
|
||||
arg.CollectionID,
|
||||
arg.SiteID,
|
||||
arg.Name,
|
||||
arg.HtmlTemplate,
|
||||
arg.IsDefault,
|
||||
)
|
||||
var i CollectionTemplate
|
||||
err := row.Scan(
|
||||
&i.TemplateID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.Name,
|
||||
&i.HtmlTemplate,
|
||||
&i.IsDefault,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteCollectionTemplate = `-- name: DeleteCollectionTemplate :exec
|
||||
DELETE FROM collection_templates
|
||||
WHERE template_id = ?1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteCollectionTemplate(ctx context.Context, templateID int64) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteCollectionTemplate, templateID)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteCollectionTemplates = `-- name: DeleteCollectionTemplates :exec
|
||||
DELETE FROM collection_templates
|
||||
WHERE collection_id = ?1 AND site_id = ?2
|
||||
`
|
||||
|
||||
type DeleteCollectionTemplatesParams struct {
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteCollectionTemplates(ctx context.Context, arg DeleteCollectionTemplatesParams) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteCollectionTemplates, arg.CollectionID, arg.SiteID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getCollectionTemplate = `-- name: GetCollectionTemplate :one
|
||||
|
||||
SELECT template_id, collection_id, site_id, name, html_template, is_default, created_at
|
||||
FROM collection_templates
|
||||
WHERE template_id = ?1
|
||||
`
|
||||
|
||||
// Collection templates table queries
|
||||
func (q *Queries) GetCollectionTemplate(ctx context.Context, templateID int64) (CollectionTemplate, error) {
|
||||
row := q.db.QueryRowContext(ctx, getCollectionTemplate, templateID)
|
||||
var i CollectionTemplate
|
||||
err := row.Scan(
|
||||
&i.TemplateID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.Name,
|
||||
&i.HtmlTemplate,
|
||||
&i.IsDefault,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getCollectionTemplates = `-- name: GetCollectionTemplates :many
|
||||
SELECT template_id, collection_id, site_id, name, html_template, is_default, created_at
|
||||
FROM collection_templates
|
||||
WHERE collection_id = ?1 AND site_id = ?2
|
||||
ORDER BY is_default DESC, created_at ASC
|
||||
`
|
||||
|
||||
type GetCollectionTemplatesParams struct {
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetCollectionTemplates(ctx context.Context, arg GetCollectionTemplatesParams) ([]CollectionTemplate, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getCollectionTemplates, arg.CollectionID, arg.SiteID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []CollectionTemplate
|
||||
for rows.Next() {
|
||||
var i CollectionTemplate
|
||||
if err := rows.Scan(
|
||||
&i.TemplateID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.Name,
|
||||
&i.HtmlTemplate,
|
||||
&i.IsDefault,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getDefaultTemplate = `-- name: GetDefaultTemplate :one
|
||||
SELECT template_id, collection_id, site_id, name, html_template, is_default, created_at
|
||||
FROM collection_templates
|
||||
WHERE collection_id = ?1 AND site_id = ?2 AND is_default = TRUE
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
type GetDefaultTemplateParams struct {
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetDefaultTemplate(ctx context.Context, arg GetDefaultTemplateParams) (CollectionTemplate, error) {
|
||||
row := q.db.QueryRowContext(ctx, getDefaultTemplate, arg.CollectionID, arg.SiteID)
|
||||
var i CollectionTemplate
|
||||
err := row.Scan(
|
||||
&i.TemplateID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.Name,
|
||||
&i.HtmlTemplate,
|
||||
&i.IsDefault,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const setTemplateAsDefault = `-- name: SetTemplateAsDefault :exec
|
||||
UPDATE collection_templates
|
||||
SET is_default = CASE
|
||||
WHEN template_id = ?1 THEN TRUE
|
||||
ELSE FALSE
|
||||
END
|
||||
WHERE collection_id = ?2 AND site_id = ?3
|
||||
`
|
||||
|
||||
type SetTemplateAsDefaultParams struct {
|
||||
TemplateID int64 `json:"template_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) SetTemplateAsDefault(ctx context.Context, arg SetTemplateAsDefaultParams) error {
|
||||
_, err := q.db.ExecContext(ctx, setTemplateAsDefault, arg.TemplateID, arg.CollectionID, arg.SiteID)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateCollectionTemplate = `-- name: UpdateCollectionTemplate :one
|
||||
UPDATE collection_templates
|
||||
SET name = ?1, html_template = ?2, is_default = ?3
|
||||
WHERE template_id = ?4
|
||||
RETURNING template_id, collection_id, site_id, name, html_template, is_default, created_at
|
||||
`
|
||||
|
||||
type UpdateCollectionTemplateParams struct {
|
||||
Name string `json:"name"`
|
||||
HtmlTemplate string `json:"html_template"`
|
||||
IsDefault int64 `json:"is_default"`
|
||||
TemplateID int64 `json:"template_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateCollectionTemplate(ctx context.Context, arg UpdateCollectionTemplateParams) (CollectionTemplate, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateCollectionTemplate,
|
||||
arg.Name,
|
||||
arg.HtmlTemplate,
|
||||
arg.IsDefault,
|
||||
arg.TemplateID,
|
||||
)
|
||||
var i CollectionTemplate
|
||||
err := row.Scan(
|
||||
&i.TemplateID,
|
||||
&i.CollectionID,
|
||||
&i.SiteID,
|
||||
&i.Name,
|
||||
&i.HtmlTemplate,
|
||||
&i.IsDefault,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
199
internal/db/sqlite/collections.sql.go
Normal file
199
internal/db/sqlite/collections.sql.go
Normal file
@@ -0,0 +1,199 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
// source: collections.sql
|
||||
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const createCollection = `-- name: CreateCollection :one
|
||||
INSERT INTO collections (id, site_id, container_html, last_edited_by)
|
||||
VALUES (?1, ?2, ?3, ?4)
|
||||
RETURNING id, site_id, container_html, created_at, updated_at, last_edited_by
|
||||
`
|
||||
|
||||
type CreateCollectionParams struct {
|
||||
ID string `json:"id"`
|
||||
SiteID string `json:"site_id"`
|
||||
ContainerHtml string `json:"container_html"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateCollection(ctx context.Context, arg CreateCollectionParams) (Collection, error) {
|
||||
row := q.db.QueryRowContext(ctx, createCollection,
|
||||
arg.ID,
|
||||
arg.SiteID,
|
||||
arg.ContainerHtml,
|
||||
arg.LastEditedBy,
|
||||
)
|
||||
var i Collection
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.SiteID,
|
||||
&i.ContainerHtml,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteAllSiteCollections = `-- name: DeleteAllSiteCollections :exec
|
||||
DELETE FROM collections
|
||||
WHERE site_id = ?1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteAllSiteCollections(ctx context.Context, siteID string) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteAllSiteCollections, siteID)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteCollection = `-- name: DeleteCollection :exec
|
||||
DELETE FROM collections
|
||||
WHERE id = ?1 AND site_id = ?2
|
||||
`
|
||||
|
||||
type DeleteCollectionParams struct {
|
||||
ID string `json:"id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteCollection(ctx context.Context, arg DeleteCollectionParams) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteCollection, arg.ID, arg.SiteID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getAllCollections = `-- name: GetAllCollections :many
|
||||
SELECT id, site_id, container_html, created_at, updated_at, last_edited_by
|
||||
FROM collections
|
||||
WHERE site_id = ?1
|
||||
ORDER BY updated_at DESC
|
||||
`
|
||||
|
||||
func (q *Queries) GetAllCollections(ctx context.Context, siteID string) ([]Collection, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getAllCollections, siteID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Collection
|
||||
for rows.Next() {
|
||||
var i Collection
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.SiteID,
|
||||
&i.ContainerHtml,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getCollection = `-- name: GetCollection :one
|
||||
|
||||
SELECT id, site_id, container_html, created_at, updated_at, last_edited_by
|
||||
FROM collections
|
||||
WHERE id = ?1 AND site_id = ?2
|
||||
`
|
||||
|
||||
type GetCollectionParams struct {
|
||||
ID string `json:"id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
// Collections table queries
|
||||
func (q *Queries) GetCollection(ctx context.Context, arg GetCollectionParams) (Collection, error) {
|
||||
row := q.db.QueryRowContext(ctx, getCollection, arg.ID, arg.SiteID)
|
||||
var i Collection
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.SiteID,
|
||||
&i.ContainerHtml,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateCollection = `-- name: UpdateCollection :one
|
||||
UPDATE collections
|
||||
SET container_html = ?1, last_edited_by = ?2
|
||||
WHERE id = ?3 AND site_id = ?4
|
||||
RETURNING id, site_id, container_html, created_at, updated_at, last_edited_by
|
||||
`
|
||||
|
||||
type UpdateCollectionParams struct {
|
||||
ContainerHtml string `json:"container_html"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
ID string `json:"id"`
|
||||
SiteID string `json:"site_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateCollection(ctx context.Context, arg UpdateCollectionParams) (Collection, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateCollection,
|
||||
arg.ContainerHtml,
|
||||
arg.LastEditedBy,
|
||||
arg.ID,
|
||||
arg.SiteID,
|
||||
)
|
||||
var i Collection
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.SiteID,
|
||||
&i.ContainerHtml,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const upsertCollection = `-- name: UpsertCollection :one
|
||||
INSERT INTO collections (id, site_id, container_html, last_edited_by)
|
||||
VALUES (?1, ?2, ?3, ?4)
|
||||
ON CONFLICT(id, site_id) DO UPDATE SET
|
||||
container_html = EXCLUDED.container_html,
|
||||
last_edited_by = EXCLUDED.last_edited_by
|
||||
RETURNING id, site_id, container_html, created_at, updated_at, last_edited_by
|
||||
`
|
||||
|
||||
type UpsertCollectionParams struct {
|
||||
ID string `json:"id"`
|
||||
SiteID string `json:"site_id"`
|
||||
ContainerHtml string `json:"container_html"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpsertCollection(ctx context.Context, arg UpsertCollectionParams) (Collection, error) {
|
||||
row := q.db.QueryRowContext(ctx, upsertCollection,
|
||||
arg.ID,
|
||||
arg.SiteID,
|
||||
arg.ContainerHtml,
|
||||
arg.LastEditedBy,
|
||||
)
|
||||
var i Collection
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.SiteID,
|
||||
&i.ContainerHtml,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.LastEditedBy,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -8,6 +8,49 @@ import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type Collection struct {
|
||||
ID string `json:"id"`
|
||||
SiteID string `json:"site_id"`
|
||||
ContainerHtml string `json:"container_html"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
}
|
||||
|
||||
type CollectionItem struct {
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
TemplateID int64 `json:"template_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
Position int64 `json:"position"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
}
|
||||
|
||||
type CollectionItemVersion struct {
|
||||
VersionID int64 `json:"version_id"`
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
HtmlContent string `json:"html_content"`
|
||||
TemplateID int64 `json:"template_id"`
|
||||
Position int64 `json:"position"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
}
|
||||
|
||||
type CollectionTemplate struct {
|
||||
TemplateID int64 `json:"template_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
Name string `json:"name"`
|
||||
HtmlTemplate string `json:"html_template"`
|
||||
IsDefault int64 `json:"is_default"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
}
|
||||
|
||||
type Content struct {
|
||||
ID string `json:"id"`
|
||||
SiteID string `json:"site_id"`
|
||||
|
||||
@@ -9,20 +9,58 @@ import (
|
||||
)
|
||||
|
||||
type Querier interface {
|
||||
CreateCollection(ctx context.Context, arg CreateCollectionParams) (Collection, error)
|
||||
CreateCollectionItem(ctx context.Context, arg CreateCollectionItemParams) (CollectionItem, error)
|
||||
// Collection item versions table queries
|
||||
CreateCollectionItemVersion(ctx context.Context, arg CreateCollectionItemVersionParams) error
|
||||
CreateCollectionTemplate(ctx context.Context, arg CreateCollectionTemplateParams) (CollectionTemplate, error)
|
||||
CreateContent(ctx context.Context, arg CreateContentParams) (Content, error)
|
||||
CreateContentVersion(ctx context.Context, arg CreateContentVersionParams) error
|
||||
DeleteAllSiteCollections(ctx context.Context, siteID string) error
|
||||
DeleteAllSiteContent(ctx context.Context, siteID string) error
|
||||
DeleteCollection(ctx context.Context, arg DeleteCollectionParams) error
|
||||
DeleteCollectionItem(ctx context.Context, arg DeleteCollectionItemParams) error
|
||||
DeleteCollectionItems(ctx context.Context, arg DeleteCollectionItemsParams) error
|
||||
DeleteCollectionTemplate(ctx context.Context, templateID int64) error
|
||||
DeleteCollectionTemplates(ctx context.Context, arg DeleteCollectionTemplatesParams) error
|
||||
DeleteContent(ctx context.Context, arg DeleteContentParams) error
|
||||
DeleteOldCollectionItemVersions(ctx context.Context, arg DeleteOldCollectionItemVersionsParams) error
|
||||
DeleteOldVersions(ctx context.Context, arg DeleteOldVersionsParams) error
|
||||
GetAllCollectionItemVersionsForSite(ctx context.Context, arg GetAllCollectionItemVersionsForSiteParams) ([]GetAllCollectionItemVersionsForSiteRow, error)
|
||||
GetAllCollections(ctx context.Context, siteID string) ([]Collection, error)
|
||||
GetAllContent(ctx context.Context, siteID string) ([]Content, error)
|
||||
GetAllVersionsForSite(ctx context.Context, arg GetAllVersionsForSiteParams) ([]GetAllVersionsForSiteRow, error)
|
||||
GetBulkContent(ctx context.Context, arg GetBulkContentParams) ([]Content, error)
|
||||
// Collections table queries
|
||||
GetCollection(ctx context.Context, arg GetCollectionParams) (Collection, error)
|
||||
// Collection items table queries
|
||||
GetCollectionItem(ctx context.Context, arg GetCollectionItemParams) (CollectionItem, error)
|
||||
GetCollectionItemVersion(ctx context.Context, versionID int64) (CollectionItemVersion, error)
|
||||
GetCollectionItemVersionHistory(ctx context.Context, arg GetCollectionItemVersionHistoryParams) ([]CollectionItemVersion, error)
|
||||
GetCollectionItems(ctx context.Context, arg GetCollectionItemsParams) ([]CollectionItem, error)
|
||||
GetCollectionItemsWithTemplate(ctx context.Context, arg GetCollectionItemsWithTemplateParams) ([]GetCollectionItemsWithTemplateRow, error)
|
||||
// Collection templates table queries
|
||||
GetCollectionTemplate(ctx context.Context, templateID int64) (CollectionTemplate, error)
|
||||
GetCollectionTemplates(ctx context.Context, arg GetCollectionTemplatesParams) ([]CollectionTemplate, error)
|
||||
GetContent(ctx context.Context, arg GetContentParams) (Content, error)
|
||||
GetContentVersion(ctx context.Context, versionID int64) (ContentVersion, error)
|
||||
GetContentVersionHistory(ctx context.Context, arg GetContentVersionHistoryParams) ([]ContentVersion, error)
|
||||
GetDefaultTemplate(ctx context.Context, arg GetDefaultTemplateParams) (CollectionTemplate, error)
|
||||
GetMaxPosition(ctx context.Context, arg GetMaxPositionParams) (interface{}, error)
|
||||
InitializeCollectionItemVersionsTable(ctx context.Context) error
|
||||
InitializeCollectionItemsTable(ctx context.Context) error
|
||||
InitializeCollectionTemplatesTable(ctx context.Context) error
|
||||
InitializeCollectionsTable(ctx context.Context) error
|
||||
InitializeSchema(ctx context.Context) error
|
||||
InitializeVersionsTable(ctx context.Context) error
|
||||
ReorderCollectionItems(ctx context.Context, arg ReorderCollectionItemsParams) error
|
||||
SetTemplateAsDefault(ctx context.Context, arg SetTemplateAsDefaultParams) error
|
||||
UpdateCollection(ctx context.Context, arg UpdateCollectionParams) (Collection, error)
|
||||
UpdateCollectionItem(ctx context.Context, arg UpdateCollectionItemParams) (CollectionItem, error)
|
||||
UpdateCollectionItemPosition(ctx context.Context, arg UpdateCollectionItemPositionParams) error
|
||||
UpdateCollectionTemplate(ctx context.Context, arg UpdateCollectionTemplateParams) (CollectionTemplate, error)
|
||||
UpdateContent(ctx context.Context, arg UpdateContentParams) (Content, error)
|
||||
UpsertCollection(ctx context.Context, arg UpsertCollectionParams) (Collection, error)
|
||||
UpsertContent(ctx context.Context, arg UpsertContentParams) (Content, error)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,15 +22,97 @@ CREATE TABLE content_versions (
|
||||
created_by TEXT DEFAULT 'system' NOT NULL
|
||||
);
|
||||
|
||||
-- Collections table - manages .insertr-add containers
|
||||
CREATE TABLE collections (
|
||||
id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
container_html TEXT NOT NULL,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
updated_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||
PRIMARY KEY (id, site_id)
|
||||
);
|
||||
|
||||
-- Collection templates - multiple template variants per collection
|
||||
CREATE TABLE collection_templates (
|
||||
template_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
collection_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
html_template TEXT NOT NULL,
|
||||
is_default INTEGER DEFAULT 0 NOT NULL,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
FOREIGN KEY (collection_id, site_id) REFERENCES collections(id, site_id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Collection items - individual items within collections
|
||||
CREATE TABLE collection_items (
|
||||
item_id TEXT NOT NULL,
|
||||
collection_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
template_id INTEGER NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
updated_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||
PRIMARY KEY (item_id, collection_id, site_id),
|
||||
FOREIGN KEY (collection_id, site_id) REFERENCES collections(id, site_id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (template_id) REFERENCES collection_templates(template_id) ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
-- Collection item version history
|
||||
CREATE TABLE collection_item_versions (
|
||||
version_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
item_id TEXT NOT NULL,
|
||||
collection_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
template_id INTEGER NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
created_by TEXT DEFAULT 'system' NOT NULL,
|
||||
FOREIGN KEY (item_id, collection_id, site_id) REFERENCES collection_items(item_id, collection_id, site_id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Indexes for performance
|
||||
CREATE INDEX IF NOT EXISTS idx_content_site_id ON content(site_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_content_updated_at ON content(updated_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_content_versions_lookup ON content_versions(content_id, site_id, created_at DESC);
|
||||
|
||||
-- Collection indexes for performance
|
||||
CREATE INDEX IF NOT EXISTS idx_collections_site_id ON collections(site_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_collections_updated_at ON collections(updated_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_templates_lookup ON collection_templates(collection_id, site_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_templates_default ON collection_templates(collection_id, site_id, is_default DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_items_lookup ON collection_items(collection_id, site_id, position);
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_items_template ON collection_items(template_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_item_versions_lookup ON collection_item_versions(item_id, collection_id, site_id, created_at DESC);
|
||||
|
||||
-- Constraint to ensure only one default template per collection
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_collection_templates_one_default
|
||||
ON collection_templates(collection_id, site_id)
|
||||
WHERE is_default = 1;
|
||||
|
||||
-- Trigger to automatically update updated_at timestamp
|
||||
CREATE TRIGGER IF NOT EXISTS update_content_updated_at
|
||||
AFTER UPDATE ON content
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE content SET updated_at = strftime('%s', 'now') WHERE id = NEW.id AND site_id = NEW.site_id;
|
||||
END;
|
||||
|
||||
-- Triggers for collection timestamps
|
||||
CREATE TRIGGER IF NOT EXISTS update_collections_updated_at
|
||||
AFTER UPDATE ON collections
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE collections SET updated_at = strftime('%s', 'now') WHERE id = NEW.id AND site_id = NEW.site_id;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS update_collection_items_updated_at
|
||||
AFTER UPDATE ON collection_items
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE collection_items SET updated_at = strftime('%s', 'now') WHERE item_id = NEW.item_id AND collection_id = NEW.collection_id AND site_id = NEW.site_id;
|
||||
END;
|
||||
@@ -21,6 +21,59 @@ CREATE TABLE IF NOT EXISTS content_versions (
|
||||
created_by TEXT DEFAULT 'system' NOT NULL
|
||||
);
|
||||
|
||||
-- name: InitializeCollectionsTable :exec
|
||||
CREATE TABLE IF NOT EXISTS collections (
|
||||
id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
container_html TEXT NOT NULL,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
updated_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||
PRIMARY KEY (id, site_id)
|
||||
);
|
||||
|
||||
-- name: InitializeCollectionTemplatesTable :exec
|
||||
CREATE TABLE IF NOT EXISTS collection_templates (
|
||||
template_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
collection_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
html_template TEXT NOT NULL,
|
||||
is_default INTEGER DEFAULT 0 NOT NULL,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
FOREIGN KEY (collection_id, site_id) REFERENCES collections(id, site_id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- name: InitializeCollectionItemsTable :exec
|
||||
CREATE TABLE IF NOT EXISTS collection_items (
|
||||
item_id TEXT NOT NULL,
|
||||
collection_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
template_id INTEGER NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
updated_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||
PRIMARY KEY (item_id, collection_id, site_id),
|
||||
FOREIGN KEY (collection_id, site_id) REFERENCES collections(id, site_id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (template_id) REFERENCES collection_templates(template_id) ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
-- name: InitializeCollectionItemVersionsTable :exec
|
||||
CREATE TABLE IF NOT EXISTS collection_item_versions (
|
||||
version_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
item_id TEXT NOT NULL,
|
||||
collection_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
template_id INTEGER NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
created_by TEXT DEFAULT 'system' NOT NULL,
|
||||
FOREIGN KEY (item_id, collection_id, site_id) REFERENCES collection_items(item_id, collection_id, site_id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- name: CreateContentSiteIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_content_site_id ON content(site_id);
|
||||
|
||||
@@ -30,10 +83,52 @@ CREATE INDEX IF NOT EXISTS idx_content_updated_at ON content(updated_at);
|
||||
-- name: CreateVersionsLookupIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_content_versions_lookup ON content_versions(content_id, site_id, created_at DESC);
|
||||
|
||||
-- name: CreateCollectionsSiteIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collections_site_id ON collections(site_id);
|
||||
|
||||
-- name: CreateCollectionsUpdatedAtIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collections_updated_at ON collections(updated_at);
|
||||
|
||||
-- name: CreateCollectionTemplatesLookupIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_templates_lookup ON collection_templates(collection_id, site_id);
|
||||
|
||||
-- name: CreateCollectionTemplatesDefaultIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_templates_default ON collection_templates(collection_id, site_id, is_default DESC);
|
||||
|
||||
-- name: CreateCollectionItemsLookupIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_items_lookup ON collection_items(collection_id, site_id, position);
|
||||
|
||||
-- name: CreateCollectionItemsTemplateIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_items_template ON collection_items(template_id);
|
||||
|
||||
-- name: CreateCollectionItemVersionsLookupIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_collection_item_versions_lookup ON collection_item_versions(item_id, collection_id, site_id, created_at DESC);
|
||||
|
||||
-- name: CreateCollectionTemplatesOneDefaultIndex :exec
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_collection_templates_one_default
|
||||
ON collection_templates(collection_id, site_id)
|
||||
WHERE is_default = 1;
|
||||
|
||||
-- name: CreateUpdateTrigger :exec
|
||||
CREATE TRIGGER IF NOT EXISTS update_content_updated_at
|
||||
AFTER UPDATE ON content
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE content SET updated_at = strftime('%s', 'now') WHERE id = NEW.id AND site_id = NEW.site_id;
|
||||
END;
|
||||
|
||||
-- name: CreateCollectionsUpdateTrigger :exec
|
||||
CREATE TRIGGER IF NOT EXISTS update_collections_updated_at
|
||||
AFTER UPDATE ON collections
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE collections SET updated_at = strftime('%s', 'now') WHERE id = NEW.id AND site_id = NEW.site_id;
|
||||
END;
|
||||
|
||||
-- name: CreateCollectionItemsUpdateTrigger :exec
|
||||
CREATE TRIGGER IF NOT EXISTS update_collection_items_updated_at
|
||||
AFTER UPDATE ON collection_items
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE collection_items SET updated_at = strftime('%s', 'now') WHERE item_id = NEW.item_id AND collection_id = NEW.collection_id AND site_id = NEW.site_id;
|
||||
END;
|
||||
@@ -9,6 +9,83 @@ import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const initializeCollectionItemVersionsTable = `-- name: InitializeCollectionItemVersionsTable :exec
|
||||
CREATE TABLE IF NOT EXISTS collection_item_versions (
|
||||
version_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
item_id TEXT NOT NULL,
|
||||
collection_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
template_id INTEGER NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
created_by TEXT DEFAULT 'system' NOT NULL,
|
||||
FOREIGN KEY (item_id, collection_id, site_id) REFERENCES collection_items(item_id, collection_id, site_id) ON DELETE CASCADE
|
||||
)
|
||||
`
|
||||
|
||||
func (q *Queries) InitializeCollectionItemVersionsTable(ctx context.Context) error {
|
||||
_, err := q.db.ExecContext(ctx, initializeCollectionItemVersionsTable)
|
||||
return err
|
||||
}
|
||||
|
||||
const initializeCollectionItemsTable = `-- name: InitializeCollectionItemsTable :exec
|
||||
CREATE TABLE IF NOT EXISTS collection_items (
|
||||
item_id TEXT NOT NULL,
|
||||
collection_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
template_id INTEGER NOT NULL,
|
||||
html_content TEXT NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
updated_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||
PRIMARY KEY (item_id, collection_id, site_id),
|
||||
FOREIGN KEY (collection_id, site_id) REFERENCES collections(id, site_id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (template_id) REFERENCES collection_templates(template_id) ON DELETE RESTRICT
|
||||
)
|
||||
`
|
||||
|
||||
func (q *Queries) InitializeCollectionItemsTable(ctx context.Context) error {
|
||||
_, err := q.db.ExecContext(ctx, initializeCollectionItemsTable)
|
||||
return err
|
||||
}
|
||||
|
||||
const initializeCollectionTemplatesTable = `-- name: InitializeCollectionTemplatesTable :exec
|
||||
CREATE TABLE IF NOT EXISTS collection_templates (
|
||||
template_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
collection_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
html_template TEXT NOT NULL,
|
||||
is_default INTEGER DEFAULT 0 NOT NULL,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
FOREIGN KEY (collection_id, site_id) REFERENCES collections(id, site_id) ON DELETE CASCADE
|
||||
)
|
||||
`
|
||||
|
||||
func (q *Queries) InitializeCollectionTemplatesTable(ctx context.Context) error {
|
||||
_, err := q.db.ExecContext(ctx, initializeCollectionTemplatesTable)
|
||||
return err
|
||||
}
|
||||
|
||||
const initializeCollectionsTable = `-- name: InitializeCollectionsTable :exec
|
||||
CREATE TABLE IF NOT EXISTS collections (
|
||||
id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
container_html TEXT NOT NULL,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
updated_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
last_edited_by TEXT DEFAULT 'system' NOT NULL,
|
||||
PRIMARY KEY (id, site_id)
|
||||
)
|
||||
`
|
||||
|
||||
func (q *Queries) InitializeCollectionsTable(ctx context.Context) error {
|
||||
_, err := q.db.ExecContext(ctx, initializeCollectionsTable)
|
||||
return err
|
||||
}
|
||||
|
||||
const initializeSchema = `-- name: InitializeSchema :exec
|
||||
CREATE TABLE IF NOT EXISTS content (
|
||||
id TEXT NOT NULL,
|
||||
|
||||
@@ -217,3 +217,196 @@ func toNullString(s string) sql.NullString {
|
||||
}
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,14 +52,14 @@ func (e *ContentEngine) ProcessContent(input ContentInput) (*ContentResult, erro
|
||||
return nil, fmt.Errorf("parsing HTML: %w", err)
|
||||
}
|
||||
|
||||
// 2. Find insertr elements
|
||||
elements := e.findInsertrElements(doc)
|
||||
// 2. Find insertr and collection elements
|
||||
insertrElements, collectionElements := e.findEditableElements(doc)
|
||||
|
||||
// 3. Generate IDs for elements
|
||||
// 3. Process regular .insertr elements
|
||||
generatedIDs := make(map[string]string)
|
||||
processedElements := make([]ProcessedElement, len(elements))
|
||||
processedElements := make([]ProcessedElement, len(insertrElements))
|
||||
|
||||
for i, elem := range elements {
|
||||
for i, elem := range insertrElements {
|
||||
// Generate structural ID (always deterministic)
|
||||
id := e.idGenerator.Generate(elem.Node, input.FilePath)
|
||||
|
||||
@@ -97,7 +97,26 @@ func (e *ContentEngine) ProcessContent(input ContentInput) (*ContentResult, erro
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Inject content if required by mode
|
||||
// 4. Process .insertr-add collection elements
|
||||
for _, collectionElem := range collectionElements {
|
||||
// Generate structural ID for the collection container
|
||||
collectionID := e.idGenerator.Generate(collectionElem.Node, input.FilePath)
|
||||
|
||||
// Add data-content-id attribute to the collection container
|
||||
e.setAttribute(collectionElem.Node, "data-content-id", collectionID)
|
||||
|
||||
// Process collection during enhancement or content injection
|
||||
if input.Mode == Enhancement || input.Mode == ContentInjection {
|
||||
err := e.processCollection(collectionElem.Node, collectionID, input.SiteID)
|
||||
if err != nil {
|
||||
fmt.Printf("⚠️ Failed to process collection %s: %v\n", collectionID, err)
|
||||
} else {
|
||||
fmt.Printf("✅ Processed collection: %s\n", collectionID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Inject content if required by mode
|
||||
if input.Mode == Enhancement || input.Mode == ContentInjection {
|
||||
err = e.injectContent(processedElements, input.SiteID)
|
||||
if err != nil {
|
||||
@@ -105,7 +124,9 @@ func (e *ContentEngine) ProcessContent(input ContentInput) (*ContentResult, erro
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Inject editor assets for enhancement mode (development)
|
||||
// TODO: Implement collection-specific content injection here if needed
|
||||
|
||||
// 6. Inject editor assets for enhancement mode (development)
|
||||
if input.Mode == Enhancement {
|
||||
injector := NewInjectorWithAuth(e.client, input.SiteID, e.authProvider)
|
||||
injector.InjectEditorAssets(doc, true, "")
|
||||
@@ -123,31 +144,40 @@ type InsertrElement struct {
|
||||
Node *html.Node
|
||||
}
|
||||
|
||||
// findInsertrElements finds all elements with class="insertr" and applies container transformation
|
||||
// This implements the "syntactic sugar transformation" from CLASSES.md:
|
||||
// - Containers with .insertr get their .insertr class removed
|
||||
// - Viable children of those containers get .insertr class added
|
||||
// - Regular elements with .insertr are kept as-is
|
||||
func (e *ContentEngine) findInsertrElements(doc *html.Node) []InsertrElement {
|
||||
var elements []InsertrElement
|
||||
// CollectionElement represents an insertr-add collection element found in HTML
|
||||
type CollectionElement struct {
|
||||
Node *html.Node
|
||||
}
|
||||
|
||||
// findEditableElements finds all editable elements (.insertr and .insertr-add)
|
||||
func (e *ContentEngine) findEditableElements(doc *html.Node) ([]InsertrElement, []CollectionElement) {
|
||||
var insertrElements []InsertrElement
|
||||
var collectionElements []CollectionElement
|
||||
var containersToTransform []*html.Node
|
||||
|
||||
// First pass: find all .insertr elements and identify containers
|
||||
// First pass: find all .insertr and .insertr-add elements
|
||||
e.walkNodes(doc, func(n *html.Node) {
|
||||
if n.Type == html.ElementNode && e.hasInsertrClass(n) {
|
||||
if isContainer(n) {
|
||||
// Container element - mark for transformation
|
||||
containersToTransform = append(containersToTransform, n)
|
||||
} else {
|
||||
// Regular element - add directly
|
||||
elements = append(elements, InsertrElement{
|
||||
if n.Type == html.ElementNode {
|
||||
if e.hasInsertrClass(n) {
|
||||
if isContainer(n) {
|
||||
// Container element - mark for transformation
|
||||
containersToTransform = append(containersToTransform, n)
|
||||
} else {
|
||||
// Regular element - add directly
|
||||
insertrElements = append(insertrElements, InsertrElement{
|
||||
Node: n,
|
||||
})
|
||||
}
|
||||
} else if e.hasInsertrAddClass(n) {
|
||||
// Collection element - add directly (no container transformation for collections)
|
||||
collectionElements = append(collectionElements, CollectionElement{
|
||||
Node: n,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Second pass: transform containers (remove .insertr from container, add to children)
|
||||
// Second pass: transform .insertr containers (remove .insertr from container, add to children)
|
||||
for _, container := range containersToTransform {
|
||||
// Remove .insertr class from container
|
||||
e.removeClass(container, "insertr")
|
||||
@@ -156,13 +186,23 @@ func (e *ContentEngine) findInsertrElements(doc *html.Node) []InsertrElement {
|
||||
viableChildren := FindViableChildren(container)
|
||||
for _, child := range viableChildren {
|
||||
e.addClass(child, "insertr")
|
||||
elements = append(elements, InsertrElement{
|
||||
insertrElements = append(insertrElements, InsertrElement{
|
||||
Node: child,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return elements
|
||||
return insertrElements, collectionElements
|
||||
}
|
||||
|
||||
// findInsertrElements finds all elements with class="insertr" and applies container transformation
|
||||
// This implements the "syntactic sugar transformation" from CLASSES.md:
|
||||
// - Containers with .insertr get their .insertr class removed
|
||||
// - Viable children of those containers get .insertr class added
|
||||
// - Regular elements with .insertr are kept as-is
|
||||
func (e *ContentEngine) findInsertrElements(doc *html.Node) []InsertrElement {
|
||||
insertrElements, _ := e.findEditableElements(doc)
|
||||
return insertrElements
|
||||
}
|
||||
|
||||
// walkNodes walks through all nodes in the document
|
||||
@@ -184,6 +224,17 @@ func (e *ContentEngine) hasInsertrClass(node *html.Node) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// hasInsertrAddClass checks if node has class="insertr-add" (collection)
|
||||
func (e *ContentEngine) hasInsertrAddClass(node *html.Node) bool {
|
||||
classes := GetClasses(node)
|
||||
for _, class := range classes {
|
||||
if class == "insertr-add" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// addContentAttributes adds data-content-id attribute only
|
||||
// HTML-first approach: no content-type attribute needed
|
||||
func (e *ContentEngine) addContentAttributes(node *html.Node, contentID string) {
|
||||
@@ -342,3 +393,124 @@ func (e *ContentEngine) extractOriginalTemplate(node *html.Node) string {
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// processCollection handles collection detection, persistence and reconstruction
|
||||
func (e *ContentEngine) processCollection(collectionNode *html.Node, collectionID, siteID string) error {
|
||||
// 1. Check if collection exists in database
|
||||
existingCollection, err := e.client.GetCollection(siteID, collectionID)
|
||||
collectionExists := (err == nil && existingCollection != nil)
|
||||
|
||||
if !collectionExists {
|
||||
// 2. New collection: extract container HTML and create collection record
|
||||
containerHTML := e.extractOriginalTemplate(collectionNode)
|
||||
|
||||
_, err := e.client.CreateCollection(siteID, collectionID, containerHTML, "system")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create collection %s: %w", collectionID, err)
|
||||
}
|
||||
|
||||
// 3. Extract templates from existing children
|
||||
err = e.extractAndStoreTemplates(collectionNode, collectionID, siteID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to extract templates for collection %s: %w", collectionID, err)
|
||||
}
|
||||
|
||||
fmt.Printf("✅ Created new collection: %s with templates\n", collectionID)
|
||||
} else {
|
||||
// 4. Existing collection: reconstruct items from database
|
||||
err = e.reconstructCollectionItems(collectionNode, collectionID, siteID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to reconstruct collection %s: %w", collectionID, err)
|
||||
}
|
||||
|
||||
fmt.Printf("✅ Reconstructed collection: %s from database\n", collectionID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// extractAndStoreTemplates extracts template patterns from existing collection children
|
||||
func (e *ContentEngine) extractAndStoreTemplates(collectionNode *html.Node, collectionID, siteID string) error {
|
||||
// Find existing children elements to use as templates
|
||||
var templateElements []*html.Node
|
||||
|
||||
// Walk through direct children of the collection
|
||||
for child := collectionNode.FirstChild; child != nil; child = child.NextSibling {
|
||||
if child.Type == html.ElementNode {
|
||||
templateElements = append(templateElements, child)
|
||||
}
|
||||
}
|
||||
|
||||
if len(templateElements) == 0 {
|
||||
// No existing children - create a default empty template
|
||||
_, err := e.client.CreateCollectionTemplate(siteID, collectionID, "default", "<div>New item</div>", true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create default template: %w", err)
|
||||
}
|
||||
fmt.Printf("✅ Created default template for collection %s\n", collectionID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Extract templates from existing children
|
||||
for i, templateElement := range templateElements {
|
||||
templateHTML := e.extractOriginalTemplate(templateElement)
|
||||
templateName := fmt.Sprintf("template-%d", i+1)
|
||||
isDefault := (i == 0) // First template is default
|
||||
|
||||
_, err := e.client.CreateCollectionTemplate(siteID, collectionID, templateName, templateHTML, isDefault)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create template %s: %w", templateName, err)
|
||||
}
|
||||
|
||||
fmt.Printf("✅ Created template '%s' for collection %s\n", templateName, collectionID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// reconstructCollectionItems rebuilds collection items from database and adds them to DOM
|
||||
func (e *ContentEngine) reconstructCollectionItems(collectionNode *html.Node, collectionID, siteID string) error {
|
||||
// Get all items for this collection from database
|
||||
items, err := e.client.GetCollectionItems(siteID, collectionID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get collection items: %w", err)
|
||||
}
|
||||
|
||||
// Clear existing children (they will be replaced with database items)
|
||||
for child := collectionNode.FirstChild; child != nil; {
|
||||
next := child.NextSibling
|
||||
collectionNode.RemoveChild(child)
|
||||
child = next
|
||||
}
|
||||
|
||||
// Add items from database in position order
|
||||
for _, item := range items {
|
||||
// Parse the item HTML content
|
||||
itemDoc, err := html.Parse(strings.NewReader(item.HTMLContent))
|
||||
if err != nil {
|
||||
fmt.Printf("⚠️ Failed to parse item HTML for %s: %v\n", item.ItemID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Find the body element and extract its children
|
||||
var bodyNode *html.Node
|
||||
e.walkNodes(itemDoc, func(n *html.Node) {
|
||||
if n.Type == html.ElementNode && n.Data == "body" {
|
||||
bodyNode = n
|
||||
}
|
||||
})
|
||||
|
||||
if bodyNode != nil {
|
||||
// Move all children from body to collection
|
||||
for bodyChild := bodyNode.FirstChild; bodyChild != nil; {
|
||||
next := bodyChild.NextSibling
|
||||
bodyNode.RemoveChild(bodyChild)
|
||||
collectionNode.AppendChild(bodyChild)
|
||||
bodyChild = next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("✅ Reconstructed %d items for collection %s\n", len(items), collectionID)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -48,6 +48,12 @@ type ContentClient interface {
|
||||
GetBulkContent(siteID string, contentIDs []string) (map[string]ContentItem, error)
|
||||
GetAllContent(siteID string) (map[string]ContentItem, error)
|
||||
CreateContent(siteID, contentID, htmlContent, originalTemplate, lastEditedBy string) (*ContentItem, error)
|
||||
|
||||
// Collection operations
|
||||
GetCollection(siteID, collectionID string) (*CollectionItem, error)
|
||||
CreateCollection(siteID, collectionID, containerHTML, lastEditedBy string) (*CollectionItem, error)
|
||||
GetCollectionItems(siteID, collectionID string) ([]CollectionItemWithTemplate, error)
|
||||
CreateCollectionTemplate(siteID, collectionID, name, htmlTemplate string, isDefault bool) (*CollectionTemplateItem, error)
|
||||
}
|
||||
|
||||
// ContentItem represents a piece of content from the database
|
||||
@@ -65,3 +71,39 @@ type ContentResponse struct {
|
||||
Content []ContentItem `json:"content"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// CollectionItem represents a collection container from the database
|
||||
type CollectionItem struct {
|
||||
ID string `json:"id"`
|
||||
SiteID string `json:"site_id"`
|
||||
ContainerHTML string `json:"container_html"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
LastEditedBy string `json:"last_edited_by,omitempty"`
|
||||
}
|
||||
|
||||
// CollectionTemplateItem represents a collection template from the database
|
||||
type CollectionTemplateItem struct {
|
||||
TemplateID int `json:"template_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
Name string `json:"name"`
|
||||
HTMLTemplate string `json:"html_template"`
|
||||
IsDefault bool `json:"is_default"`
|
||||
}
|
||||
|
||||
// CollectionItemWithTemplate represents a collection item with its template information
|
||||
type CollectionItemWithTemplate struct {
|
||||
ItemID string `json:"item_id"`
|
||||
CollectionID string `json:"collection_id"`
|
||||
SiteID string `json:"site_id"`
|
||||
TemplateID int `json:"template_id"`
|
||||
HTMLContent string `json:"html_content"`
|
||||
Position int `json:"position"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
LastEditedBy string `json:"last_edited_by"`
|
||||
|
||||
// Template information
|
||||
TemplateName string `json:"template_name"`
|
||||
HTMLTemplate string `json:"html_template"`
|
||||
IsDefault bool `json:"is_default"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user