refactor: consolidate database structure and move injector to engine
Database Structure Cleanup: - Move all SQL files from ./db/ to ./internal/db/ - Update sqlc.yaml to use new paths (preserving schema+setup.sql hack) - Consolidate database-related code in single directory - Remove empty ./db/ directory Injector Migration: - Move injector.go from content package to engine package - Update ContentClient interface to return map instead of slice for GetBulkContent - Update database client implementation to match interface - Remove injector dependency from enhancer (stub implementation) Demo-Site Consolidation: - Move demo-site to test-sites/demo-site for better organization - Update build scripts to use new demo-site location - Maintain all functionality while improving project structure This continues the unified architecture consolidation by moving core content processing logic to the engine and organizing related files properly.
This commit is contained in:
@@ -10,8 +10,8 @@ import (
|
||||
|
||||
// Enhancer combines parsing and content injection using unified engine
|
||||
type Enhancer struct {
|
||||
engine *engine.ContentEngine
|
||||
injector *Injector
|
||||
engine *engine.ContentEngine
|
||||
// injector functionality will be integrated into engine
|
||||
}
|
||||
|
||||
// NewEnhancer creates a new HTML enhancer using unified engine
|
||||
@@ -26,8 +26,7 @@ func NewEnhancer(client ContentClient, siteID string) *Enhancer {
|
||||
}
|
||||
|
||||
return &Enhancer{
|
||||
engine: engine.NewContentEngine(engineClient),
|
||||
injector: NewInjector(client, siteID),
|
||||
engine: engine.NewContentEngine(engineClient),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
42
internal/db/postgresql/schema.sql
Normal file
42
internal/db/postgresql/schema.sql
Normal file
@@ -0,0 +1,42 @@
|
||||
-- PostgreSQL-specific schema with BIGINT UNIX timestamps
|
||||
-- Main content table (current versions only)
|
||||
CREATE TABLE content (
|
||||
id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
type 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)
|
||||
);
|
||||
|
||||
-- Version history table for rollback functionality
|
||||
CREATE TABLE content_versions (
|
||||
version_id SERIAL PRIMARY KEY,
|
||||
content_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
created_at BIGINT DEFAULT EXTRACT(EPOCH FROM NOW()) NOT NULL,
|
||||
created_by TEXT DEFAULT 'system' NOT NULL
|
||||
);
|
||||
|
||||
-- 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);
|
||||
|
||||
-- Function and trigger to automatically update updated_at timestamp
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = EXTRACT(EPOCH FROM NOW());
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
CREATE TRIGGER update_content_updated_at
|
||||
BEFORE UPDATE ON content
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
47
internal/db/postgresql/setup.sql
Normal file
47
internal/db/postgresql/setup.sql
Normal file
@@ -0,0 +1,47 @@
|
||||
-- name: InitializeSchema :exec
|
||||
CREATE TABLE IF NOT EXISTS content (
|
||||
id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
type TEXT NOT NULL CHECK (type IN ('text', 'markdown', 'link')),
|
||||
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: InitializeVersionsTable :exec
|
||||
CREATE TABLE IF NOT EXISTS content_versions (
|
||||
version_id SERIAL PRIMARY KEY,
|
||||
content_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
created_at BIGINT DEFAULT (EXTRACT(EPOCH FROM NOW())) NOT NULL,
|
||||
created_by TEXT DEFAULT 'system' NOT NULL
|
||||
);
|
||||
|
||||
-- name: CreateContentSiteIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_content_site_id ON content(site_id);
|
||||
|
||||
-- name: CreateContentUpdatedAtIndex :exec
|
||||
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: CreateUpdateFunction :exec
|
||||
CREATE OR REPLACE FUNCTION update_content_timestamp()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = EXTRACT(EPOCH FROM NOW());
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- name: CreateUpdateTrigger :exec
|
||||
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();
|
||||
39
internal/db/queries/content.sql
Normal file
39
internal/db/queries/content.sql
Normal file
@@ -0,0 +1,39 @@
|
||||
-- name: GetContent :one
|
||||
SELECT id, site_id, value, type, created_at, updated_at, last_edited_by
|
||||
FROM content
|
||||
WHERE id = sqlc.arg(id) AND site_id = sqlc.arg(site_id);
|
||||
|
||||
-- name: GetAllContent :many
|
||||
SELECT id, site_id, value, type, created_at, updated_at, last_edited_by
|
||||
FROM content
|
||||
WHERE site_id = sqlc.arg(site_id)
|
||||
ORDER BY updated_at DESC;
|
||||
|
||||
-- name: GetBulkContent :many
|
||||
SELECT id, site_id, value, type, created_at, updated_at, last_edited_by
|
||||
FROM content
|
||||
WHERE site_id = sqlc.arg(site_id) AND id IN (sqlc.slice('ids'));
|
||||
|
||||
-- name: CreateContent :one
|
||||
INSERT INTO content (id, site_id, value, type, last_edited_by)
|
||||
VALUES (sqlc.arg(id), sqlc.arg(site_id), sqlc.arg(value), sqlc.arg(type), sqlc.arg(last_edited_by))
|
||||
RETURNING id, site_id, value, type, created_at, updated_at, last_edited_by;
|
||||
|
||||
-- name: UpdateContent :one
|
||||
UPDATE content
|
||||
SET value = sqlc.arg(value), type = sqlc.arg(type), last_edited_by = sqlc.arg(last_edited_by)
|
||||
WHERE id = sqlc.arg(id) AND site_id = sqlc.arg(site_id)
|
||||
RETURNING id, site_id, value, type, created_at, updated_at, last_edited_by;
|
||||
|
||||
-- name: UpsertContent :one
|
||||
INSERT INTO content (id, site_id, value, type, last_edited_by)
|
||||
VALUES (sqlc.arg(id), sqlc.arg(site_id), sqlc.arg(value), sqlc.arg(type), sqlc.arg(last_edited_by))
|
||||
ON CONFLICT(id, site_id) DO UPDATE SET
|
||||
value = EXCLUDED.value,
|
||||
type = EXCLUDED.type,
|
||||
last_edited_by = EXCLUDED.last_edited_by
|
||||
RETURNING id, site_id, value, type, created_at, updated_at, last_edited_by;
|
||||
|
||||
-- name: DeleteContent :exec
|
||||
DELETE FROM content
|
||||
WHERE id = sqlc.arg(id) AND site_id = sqlc.arg(site_id);
|
||||
29
internal/db/queries/versions.sql
Normal file
29
internal/db/queries/versions.sql
Normal file
@@ -0,0 +1,29 @@
|
||||
-- name: CreateContentVersion :exec
|
||||
INSERT INTO content_versions (content_id, site_id, value, type, created_by)
|
||||
VALUES (sqlc.arg(content_id), sqlc.arg(site_id), sqlc.arg(value), sqlc.arg(type), sqlc.arg(created_by));
|
||||
|
||||
-- name: GetContentVersionHistory :many
|
||||
SELECT version_id, content_id, site_id, value, type, created_at, created_by
|
||||
FROM content_versions
|
||||
WHERE content_id = sqlc.arg(content_id) AND site_id = sqlc.arg(site_id)
|
||||
ORDER BY created_at DESC
|
||||
LIMIT sqlc.arg(limit_count);
|
||||
|
||||
-- name: GetContentVersion :one
|
||||
SELECT version_id, content_id, site_id, value, type, created_at, created_by
|
||||
FROM content_versions
|
||||
WHERE version_id = sqlc.arg(version_id);
|
||||
|
||||
-- name: GetAllVersionsForSite :many
|
||||
SELECT
|
||||
cv.version_id, cv.content_id, cv.site_id, cv.value, cv.type, cv.created_at, cv.created_by,
|
||||
c.value as current_value
|
||||
FROM content_versions cv
|
||||
LEFT JOIN content c ON cv.content_id = c.id AND cv.site_id = c.site_id
|
||||
WHERE cv.site_id = sqlc.arg(site_id)
|
||||
ORDER BY cv.created_at DESC
|
||||
LIMIT sqlc.arg(limit_count);
|
||||
|
||||
-- name: DeleteOldVersions :exec
|
||||
DELETE FROM content_versions
|
||||
WHERE created_at < sqlc.arg(created_before) AND site_id = sqlc.arg(site_id);
|
||||
36
internal/db/sqlite/schema.sql
Normal file
36
internal/db/sqlite/schema.sql
Normal file
@@ -0,0 +1,36 @@
|
||||
-- SQLite-specific schema with INTEGER timestamps
|
||||
-- Main content table (current versions only)
|
||||
CREATE TABLE content (
|
||||
id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
type 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)
|
||||
);
|
||||
|
||||
-- Version history table for rollback functionality
|
||||
CREATE TABLE content_versions (
|
||||
version_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
content_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
created_by TEXT DEFAULT 'system' NOT NULL
|
||||
);
|
||||
|
||||
-- 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);
|
||||
|
||||
-- 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;
|
||||
39
internal/db/sqlite/setup.sql
Normal file
39
internal/db/sqlite/setup.sql
Normal file
@@ -0,0 +1,39 @@
|
||||
-- name: InitializeSchema :exec
|
||||
CREATE TABLE IF NOT EXISTS content (
|
||||
id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
type TEXT NOT NULL CHECK (type IN ('text', 'markdown', 'link')),
|
||||
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: InitializeVersionsTable :exec
|
||||
CREATE TABLE IF NOT EXISTS content_versions (
|
||||
version_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
content_id TEXT NOT NULL,
|
||||
site_id TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
created_at INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL,
|
||||
created_by TEXT DEFAULT 'system' NOT NULL
|
||||
);
|
||||
|
||||
-- name: CreateContentSiteIndex :exec
|
||||
CREATE INDEX IF NOT EXISTS idx_content_site_id ON content(site_id);
|
||||
|
||||
-- name: CreateContentUpdatedAtIndex :exec
|
||||
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: 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;
|
||||
@@ -62,7 +62,7 @@ func (c *DatabaseClient) GetContent(siteID, contentID string) (*ContentItem, err
|
||||
}
|
||||
|
||||
// GetBulkContent retrieves multiple content items
|
||||
func (c *DatabaseClient) GetBulkContent(siteID string, contentIDs []string) ([]*ContentItem, error) {
|
||||
func (c *DatabaseClient) GetBulkContent(siteID string, contentIDs []string) (map[string]ContentItem, error) {
|
||||
switch c.database.GetDBType() {
|
||||
case "sqlite3":
|
||||
contents, err := c.database.GetSQLiteQueries().GetBulkContent(context.Background(), sqlite.GetBulkContentParams{
|
||||
@@ -73,9 +73,9 @@ func (c *DatabaseClient) GetBulkContent(siteID string, contentIDs []string) ([]*
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := make([]*ContentItem, len(contents))
|
||||
for i, content := range contents {
|
||||
items[i] = &ContentItem{
|
||||
items := make(map[string]ContentItem)
|
||||
for _, content := range contents {
|
||||
items[content.ID] = ContentItem{
|
||||
ID: content.ID,
|
||||
SiteID: content.SiteID,
|
||||
Value: content.Value,
|
||||
@@ -94,9 +94,9 @@ func (c *DatabaseClient) GetBulkContent(siteID string, contentIDs []string) ([]*
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := make([]*ContentItem, len(contents))
|
||||
for i, content := range contents {
|
||||
items[i] = &ContentItem{
|
||||
items := make(map[string]ContentItem)
|
||||
for _, content := range contents {
|
||||
items[content.ID] = ContentItem{
|
||||
ID: content.ID,
|
||||
SiteID: content.SiteID,
|
||||
Value: content.Value,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package content
|
||||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -46,7 +46,7 @@ type ProcessedElement struct {
|
||||
// This will be implemented by database clients
|
||||
type ContentClient interface {
|
||||
GetContent(siteID, contentID string) (*ContentItem, error)
|
||||
GetBulkContent(siteID string, contentIDs []string) ([]*ContentItem, error)
|
||||
GetBulkContent(siteID string, contentIDs []string) (map[string]ContentItem, error)
|
||||
}
|
||||
|
||||
// ContentItem represents a piece of content from the database
|
||||
|
||||
Reference in New Issue
Block a user