content

package
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 8, 2025 License: MIT Imports: 18 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrContentTypeRequired             = errors.New("content: content type does not exist")
	ErrSlugRequired                    = errors.New("content: slug is required")
	ErrSlugInvalid                     = errors.New("content: slug contains invalid characters")
	ErrSlugExists                      = errors.New("content: slug already exists")
	ErrNoTranslations                  = errors.New("content: at least one translation is required")
	ErrDuplicateLocale                 = errors.New("content: duplicate locale provided")
	ErrUnknownLocale                   = errors.New("content: unknown locale")
	ErrContentSoftDeleteUnsupported    = errors.New("content: soft delete not supported")
	ErrContentIDRequired               = errors.New("content: content id required")
	ErrVersioningDisabled              = errors.New("content: versioning feature disabled")
	ErrContentVersionRequired          = errors.New("content: version identifier required")
	ErrContentVersionConflict          = errors.New("content: base version mismatch")
	ErrContentVersionAlreadyPublished  = errors.New("content: version already published")
	ErrContentVersionRetentionExceeded = errors.New("content: version retention limit reached")
	ErrSchedulingDisabled              = errors.New("content: scheduling feature disabled")
	ErrScheduleWindowInvalid           = errors.New("content: publish_at must be before unpublish_at")
	ErrScheduleTimestampInvalid        = errors.New("content: schedule timestamp is invalid")
	ErrContentTranslationsDisabled     = errors.New("content: translations feature disabled")
	ErrContentTranslationNotFound      = errors.New("content: translation not found")
)
View Source
var ContentVersionSnapshotSchema = map[string]any{
	"type": "object",
	"properties": map[string]any{
		"fields": map[string]any{
			"type":                 "object",
			"additionalProperties": true,
		},
		"translations": map[string]any{
			"type": "array",
			"items": map[string]any{
				"type":     "object",
				"required": []string{"locale", "title", "content"},
				"properties": map[string]any{
					"locale": map[string]any{"type": "string"},
					"title":  map[string]any{"type": "string"},
					"summary": map[string]any{
						"type": []any{"string", "null"},
					},
					"content": map[string]any{
						"type":                 "object",
						"additionalProperties": true,
					},
				},
			},
		},
		"metadata": map[string]any{
			"type":                 "object",
			"additionalProperties": true,
		},
	},
}

ContentVersionSnapshotSchema captures the JSON schema used to validate snapshots.

Functions

func NewContentRepository

func NewContentRepository(db *bun.DB) repository.Repository[*Content]

func NewContentTranslationRepository

func NewContentTranslationRepository(db *bun.DB) repository.Repository[*ContentTranslation]

func NewContentTypeRepository

func NewContentTypeRepository(db *bun.DB) repository.Repository[*ContentType]

func NewContentVersionRepository

func NewContentVersionRepository(db *bun.DB) repository.Repository[*ContentVersion]

NewContentVersionRepository creates a repository for ContentVersion entities.

func NewLocaleRepository

func NewLocaleRepository(db *bun.DB) repository.Repository[*Locale]

Types

type BunContentRepository

type BunContentRepository struct {
	// contains filtered or unexported fields
}

func NewBunContentRepository

func NewBunContentRepository(db *bun.DB) *BunContentRepository

func NewBunContentRepositoryWithCache

func NewBunContentRepositoryWithCache(db *bun.DB, cacheService cache.CacheService, keySerializer cache.KeySerializer) *BunContentRepository

func (*BunContentRepository) Create

func (r *BunContentRepository) Create(ctx context.Context, record *Content) (*Content, error)

func (*BunContentRepository) CreateVersion

func (r *BunContentRepository) CreateVersion(ctx context.Context, version *ContentVersion) (*ContentVersion, error)

func (*BunContentRepository) Delete

func (r *BunContentRepository) Delete(ctx context.Context, id uuid.UUID, hardDelete bool) error

func (*BunContentRepository) GetByID

func (r *BunContentRepository) GetByID(ctx context.Context, id uuid.UUID) (*Content, error)

func (*BunContentRepository) GetBySlug

func (r *BunContentRepository) GetBySlug(ctx context.Context, slug string) (*Content, error)

func (*BunContentRepository) GetLatestVersion

func (r *BunContentRepository) GetLatestVersion(ctx context.Context, contentID uuid.UUID) (*ContentVersion, error)

func (*BunContentRepository) GetVersion

func (r *BunContentRepository) GetVersion(ctx context.Context, contentID uuid.UUID, number int) (*ContentVersion, error)

func (*BunContentRepository) List

func (r *BunContentRepository) List(ctx context.Context) ([]*Content, error)

func (*BunContentRepository) ListVersions

func (r *BunContentRepository) ListVersions(ctx context.Context, contentID uuid.UUID) ([]*ContentVersion, error)

func (*BunContentRepository) ReplaceTranslations

func (r *BunContentRepository) ReplaceTranslations(ctx context.Context, contentID uuid.UUID, translations []*ContentTranslation) error

func (*BunContentRepository) Update

func (r *BunContentRepository) Update(ctx context.Context, record *Content) (*Content, error)

func (*BunContentRepository) UpdateVersion

func (r *BunContentRepository) UpdateVersion(ctx context.Context, version *ContentVersion) (*ContentVersion, error)

type BunContentTypeRepository

type BunContentTypeRepository struct {
	// contains filtered or unexported fields
}

func NewBunContentTypeRepository

func NewBunContentTypeRepository(db *bun.DB) *BunContentTypeRepository

func NewBunContentTypeRepositoryWithCache

func NewBunContentTypeRepositoryWithCache(db *bun.DB, cacheService cache.CacheService, keySerializer cache.KeySerializer) *BunContentTypeRepository

func (*BunContentTypeRepository) GetByID

type BunLocaleRepository

type BunLocaleRepository struct {
	// contains filtered or unexported fields
}

func NewBunLocaleRepository

func NewBunLocaleRepository(db *bun.DB) *BunLocaleRepository

func NewBunLocaleRepositoryWithCache

func NewBunLocaleRepositoryWithCache(db *bun.DB, cacheService cache.CacheService, keySerializer cache.KeySerializer) *BunLocaleRepository

NewBunLocaleRepositoryWithCache constructs a LocaleRepository with optional caching.

func (*BunLocaleRepository) GetByCode

func (r *BunLocaleRepository) GetByCode(ctx context.Context, code string) (*Locale, error)

type Content

type Content struct {
	bun.BaseModel `bun:"table:contents,alias:c"`

	ID               uuid.UUID             `bun:",pk,type:uuid" json:"id"`
	ContentTypeID    uuid.UUID             `bun:"content_type_id,notnull,type:uuid" json:"content_type_id"`
	CurrentVersion   int                   `bun:"current_version,notnull,default:1" json:"current_version"`
	PublishedVersion *int                  `bun:"published_version" json:"published_version,omitempty"`
	Status           string                `bun:"status,notnull,default:'draft'" json:"status"`
	Slug             string                `bun:"slug,notnull" json:"slug"`
	PublishAt        *time.Time            `bun:"publish_at,nullzero" json:"publish_at,omitempty"`
	UnpublishAt      *time.Time            `bun:"unpublish_at,nullzero" json:"unpublish_at,omitempty"`
	PublishedAt      *time.Time            `bun:"published_at,nullzero" json:"published_at,omitempty"`
	PublishedBy      *uuid.UUID            `bun:"published_by,type:uuid" json:"published_by,omitempty"`
	CreatedBy        uuid.UUID             `bun:"created_by,notnull,type:uuid" json:"created_by"`
	UpdatedBy        uuid.UUID             `bun:"updated_by,notnull,type:uuid" json:"updated_by"`
	DeletedAt        *time.Time            `bun:"deleted_at,nullzero" json:"deleted_at,omitempty"`
	CreatedAt        time.Time             `bun:"created_at,nullzero,default:current_timestamp" json:"created_at"`
	UpdatedAt        time.Time             `bun:"updated_at,nullzero,default:current_timestamp" json:"updated_at"`
	Type             *ContentType          `bun:"rel:belongs-to,join:content_type_id=id" json:"content_type,omitempty"`
	Translations     []*ContentTranslation `bun:"rel:has-many,join:id=content_id"        json:"translations,omitempty"`
	Versions         []*ContentVersion     `bun:"rel:has-many,join:id=content_id"        json:"versions,omitempty"`
	EffectiveStatus  domain.Status         `bun:"-" json:"effective_status"`
	IsVisible        bool                  `bun:"-" json:"is_visible"`
}

Content is the canonical record for translatable entries.

type ContentRepository

type ContentRepository interface {
	Create(ctx context.Context, record *Content) (*Content, error)
	GetByID(ctx context.Context, id uuid.UUID) (*Content, error)
	GetBySlug(ctx context.Context, slug string) (*Content, error)
	List(ctx context.Context) ([]*Content, error)
	Update(ctx context.Context, record *Content) (*Content, error)
	ReplaceTranslations(ctx context.Context, contentID uuid.UUID, translations []*ContentTranslation) error
	Delete(ctx context.Context, id uuid.UUID, hardDelete bool) error
	CreateVersion(ctx context.Context, version *ContentVersion) (*ContentVersion, error)
	ListVersions(ctx context.Context, contentID uuid.UUID) ([]*ContentVersion, error)
	GetVersion(ctx context.Context, contentID uuid.UUID, number int) (*ContentVersion, error)
	GetLatestVersion(ctx context.Context, contentID uuid.UUID) (*ContentVersion, error)
	UpdateVersion(ctx context.Context, version *ContentVersion) (*ContentVersion, error)
}

ContentRepository abstracts storage operations for content entities.

type ContentTranslation

type ContentTranslation struct {
	bun.BaseModel `bun:"table:content_translations,alias:ctn"`

	ID        uuid.UUID      `bun:",pk,type:uuid" json:"id"`
	ContentID uuid.UUID      `bun:"content_id,notnull,type:uuid" json:"content_id"`
	LocaleID  uuid.UUID      `bun:"locale_id,notnull,type:uuid" json:"locale_id"`
	Title     string         `bun:"title,notnull" json:"title"`
	Summary   *string        `bun:"summary" json:"summary,omitempty"`
	Content   map[string]any `bun:"content,type:jsonb,notnull" json:"content"`
	DeletedAt *time.Time     `bun:"deleted_at,nullzero" json:"deleted_at,omitempty"`
	CreatedAt time.Time      `bun:"created_at,nullzero,default:current_timestamp" json:"created_at"`
	UpdatedAt time.Time      `bun:"updated_at,nullzero,default:current_timestamp" json:"updated_at"`

	Locale *Locale `bun:"rel:belongs-to,join:locale_id=id" json:"locale,omitempty"`
}

ContentTranslation stores localized variants of a content entry.

type ContentTranslationInput

type ContentTranslationInput struct {
	Locale  string
	Title   string
	Summary *string
	Content map[string]any
}

ContentTranslationInput represents localized fields supplied during create.

type ContentType

type ContentType struct {
	bun.BaseModel `bun:"table:content_types,alias:ct"`

	ID           uuid.UUID      `bun:",pk,type:uuid"                json:"id"`
	Name         string         `bun:"name,notnull"                 json:"name"`
	Description  *string        `bun:"description"                  json:"description,omitempty"`
	Schema       map[string]any `bun:"schema,type:jsonb,notnull"    json:"schema"`
	Capabilities map[string]any `bun:"capabilities,type:jsonb"      json:"capabilities,omitempty"`
	Icon         *string        `bun:"icon"                         json:"icon,omitempty"`
	CreatedAt    time.Time      `bun:"created_at,nullzero,default:current_timestamp" json:"created_at"`
	UpdatedAt    time.Time      `bun:"updated_at,nullzero,default:current_timestamp" json:"updated_at"`
}

ContentType defines available content schemas.

type ContentTypeRepository

type ContentTypeRepository interface {
	GetByID(ctx context.Context, id uuid.UUID) (*ContentType, error)
}

ContentTypeRepository resolves content types.

type ContentVersion

type ContentVersion struct {
	bun.BaseModel `bun:"table:content_versions,alias:cv"`

	ID          uuid.UUID              `bun:",pk,type:uuid" json:"id"`
	ContentID   uuid.UUID              `bun:"content_id,notnull,type:uuid" json:"content_id"`
	Version     int                    `bun:"version,notnull" json:"version"`
	Status      domain.Status          `bun:"status,notnull,default:'draft'" json:"status"`
	Snapshot    ContentVersionSnapshot `bun:"snapshot,type:jsonb,notnull" json:"snapshot"`
	CreatedBy   uuid.UUID              `bun:"created_by,notnull,type:uuid" json:"created_by"`
	CreatedAt   time.Time              `bun:"created_at,nullzero,default:current_timestamp" json:"created_at"`
	PublishedAt *time.Time             `bun:"published_at,nullzero" json:"published_at,omitempty"`
	PublishedBy *uuid.UUID             `bun:"published_by,type:uuid" json:"published_by,omitempty"`
	Content     *Content               `bun:"rel:belongs-to,join:content_id=id" json:"content,omitempty"`
}

ContentVersion captures immutable snapshots of content payloads.

type ContentVersionSnapshot

type ContentVersionSnapshot struct {
	Fields       map[string]any                      `json:"fields,omitempty"`
	Translations []ContentVersionTranslationSnapshot `json:"translations,omitempty"`
	Metadata     map[string]any                      `json:"metadata,omitempty"`
}

ContentVersionSnapshot describes the persisted JSON snapshot for version history.

type ContentVersionTranslationSnapshot

type ContentVersionTranslationSnapshot struct {
	Locale  string         `json:"locale"`
	Title   string         `json:"title"`
	Summary *string        `json:"summary,omitempty"`
	Content map[string]any `json:"content"`
}

ContentVersionTranslationSnapshot encodes a localized payload captured in a version.

type CreateContentDraftRequest

type CreateContentDraftRequest struct {
	ContentID   uuid.UUID
	Snapshot    ContentVersionSnapshot
	CreatedBy   uuid.UUID
	UpdatedBy   uuid.UUID
	BaseVersion *int
}

CreateContentDraftRequest captures the payload needed to record a draft snapshot.

type CreateContentRequest

type CreateContentRequest struct {
	ContentTypeID            uuid.UUID
	Slug                     string
	Status                   string
	CreatedBy                uuid.UUID
	UpdatedBy                uuid.UUID
	Translations             []ContentTranslationInput
	AllowMissingTranslations bool
}

CreateContentRequest captures the information required to create content.

type DeleteContentRequest

type DeleteContentRequest struct {
	ID         uuid.UUID
	DeletedBy  uuid.UUID
	HardDelete bool
}

DeleteContentRequest captures the information required to remove a content entry. When HardDelete is false the record should be soft-deleted if the implementation supports it; otherwise the request should fail fast.

type DeleteContentTranslationRequest added in v0.2.0

type DeleteContentTranslationRequest struct {
	ContentID uuid.UUID
	Locale    string
	DeletedBy uuid.UUID
}

DeleteContentTranslationRequest captures the payload required to drop a translation.

type IDGenerator

type IDGenerator func() uuid.UUID

IDGenerator returns the identifier used for newly created records.

type Locale

type Locale struct {
	bun.BaseModel `bun:"table:locales,alias:l"`

	ID         uuid.UUID      `bun:",pk,type:uuid"         json:"id"`
	Code       string         `bun:"code,notnull"          json:"code"`
	Display    string         `bun:"display_name,notnull"  json:"display_name"`
	NativeName *string        `bun:"native_name"           json:"native_name,omitempty"`
	IsActive   bool           `bun:"is_active,notnull,default:true"  json:"is_active"`
	IsDefault  bool           `bun:"is_default,notnull,default:false" json:"is_default"`
	Metadata   map[string]any `bun:"metadata,type:jsonb"   json:"metadata,omitempty"`
	DeletedAt  *time.Time     `bun:"deleted_at,nullzero"   json:"deleted_at,omitempty"`
	CreatedAt  time.Time      `bun:"created_at,nullzero,default:current_timestamp" json:"created_at"`
}

Locale represents supported languages for the CMS.

type LocaleRepository

type LocaleRepository interface {
	GetByCode(ctx context.Context, code string) (*Locale, error)
}

LocaleRepository resolves locales by code.

type MemoryContentRepository

type MemoryContentRepository struct {
	// contains filtered or unexported fields
}

MemoryContentRepository is an "in memory" implementation for scaffolding and tests.

func NewMemoryContentRepository

func NewMemoryContentRepository() *MemoryContentRepository

NewMemoryContentRepository creates an empty "in memory" content repository.

func (*MemoryContentRepository) Create

func (m *MemoryContentRepository) Create(_ context.Context, record *Content) (*Content, error)

Create inserts the supplied content.

func (*MemoryContentRepository) CreateVersion

func (m *MemoryContentRepository) CreateVersion(_ context.Context, version *ContentVersion) (*ContentVersion, error)

CreateVersion appends a new version snapshot for the supplied content entity.

func (*MemoryContentRepository) Delete

func (m *MemoryContentRepository) Delete(_ context.Context, id uuid.UUID, hardDelete bool) error

Delete removes the content record and its associated versions when hard delete is requested.

func (*MemoryContentRepository) GetByID

GetByID retrieves content by identifier.

func (*MemoryContentRepository) GetBySlug

func (m *MemoryContentRepository) GetBySlug(_ context.Context, slug string) (*Content, error)

GetBySlug retrieves content by slug, returning NotFoundError when absent.

func (*MemoryContentRepository) GetLatestVersion

func (m *MemoryContentRepository) GetLatestVersion(_ context.Context, contentID uuid.UUID) (*ContentVersion, error)

GetLatestVersion retrieves the most recent version for a content entity.

func (*MemoryContentRepository) GetVersion

func (m *MemoryContentRepository) GetVersion(_ context.Context, contentID uuid.UUID, number int) (*ContentVersion, error)

GetVersion retrieves a specific content version by number.

func (*MemoryContentRepository) List

List returns all content entries.

func (*MemoryContentRepository) ListVersions

func (m *MemoryContentRepository) ListVersions(_ context.Context, contentID uuid.UUID) ([]*ContentVersion, error)

ListVersions returns every stored version for a content entity.

func (*MemoryContentRepository) ReplaceTranslations

func (m *MemoryContentRepository) ReplaceTranslations(_ context.Context, contentID uuid.UUID, translations []*ContentTranslation) error

ReplaceTranslations swaps the translations associated with a content record.

func (*MemoryContentRepository) Update

func (m *MemoryContentRepository) Update(_ context.Context, record *Content) (*Content, error)

Update persists metadata changes for content records.

func (*MemoryContentRepository) UpdateVersion

func (m *MemoryContentRepository) UpdateVersion(_ context.Context, version *ContentVersion) (*ContentVersion, error)

UpdateVersion mutates metadata for a stored content version.

type MemoryContentTypeRepository

type MemoryContentTypeRepository struct {
	// contains filtered or unexported fields
}

MemoryContentTypeRepository stores content types "in memory".

func NewMemoryContentTypeRepository

func NewMemoryContentTypeRepository() *MemoryContentTypeRepository

NewMemoryContentTypeRepository constructs the repository.

func (*MemoryContentTypeRepository) GetByID

GetByID fetches a content type.

func (*MemoryContentTypeRepository) Put

Put inserts or replaces a content type.

type MemoryLocaleRepository

type MemoryLocaleRepository struct {
	// contains filtered or unexported fields
}

MemoryLocaleRepository stores locales by code.

func NewMemoryLocaleRepository

func NewMemoryLocaleRepository() *MemoryLocaleRepository

NewMemoryLocaleRepository constructs the repository.

func (*MemoryLocaleRepository) GetByCode

func (m *MemoryLocaleRepository) GetByCode(_ context.Context, code string) (*Locale, error)

GetByCode resolves a locale by code (case-insensitive).

func (*MemoryLocaleRepository) Put

func (m *MemoryLocaleRepository) Put(locale *Locale)

Put inserts or replaces a locale.

type NotFoundError

type NotFoundError struct {
	Resource string
	Key      string
}

NotFoundError represents missing records from repository lookups.

func (*NotFoundError) Error

func (e *NotFoundError) Error() string

type PublishContentDraftRequest

type PublishContentDraftRequest struct {
	ContentID   uuid.UUID
	Version     int
	PublishedBy uuid.UUID
	PublishedAt *time.Time
}

PublishContentDraftRequest captures the information required to publish a content draft.

type RestoreContentVersionRequest

type RestoreContentVersionRequest struct {
	ContentID  uuid.UUID
	Version    int
	RestoredBy uuid.UUID
}

RestoreContentVersionRequest captures the request to restore a prior content version.

type ScheduleContentRequest

type ScheduleContentRequest struct {
	ContentID   uuid.UUID
	PublishAt   *time.Time
	UnpublishAt *time.Time
	ScheduledBy uuid.UUID
}

ScheduleContentRequest captures details to schedule publish/unpublish events.

type Service

Service exposes content management use cases.

func NewService

func NewService(contents ContentRepository, types ContentTypeRepository, locales LocaleRepository, opts ...ServiceOption) Service

NewService constructs a content service with the required dependencies.

type ServiceOption

type ServiceOption func(*service)

ServiceOption configures the service at construction time.

func WithActivityEmitter added in v0.5.0

func WithActivityEmitter(emitter *activity.Emitter) ServiceOption

WithActivityEmitter wires the activity emitter used for activity records.

func WithClock

func WithClock(clock func() time.Time) ServiceOption

WithClock overrides the clock used to stamp records.

func WithIDGenerator

func WithIDGenerator(generator IDGenerator) ServiceOption

WithIDGenerator overrides the generator used to create identifiers.

func WithLogger

func WithLogger(logger interfaces.Logger) ServiceOption

WithLogger assigns the logger used by the service. When omitted, a no-op logger is used.

func WithRequireTranslations added in v0.2.0

func WithRequireTranslations(required bool) ServiceOption

WithRequireTranslations controls whether translations are mandatory.

func WithScheduler

func WithScheduler(scheduler interfaces.Scheduler) ServiceOption

WithScheduler overrides the scheduler used to register publish/unpublish jobs.

func WithSchedulingEnabled

func WithSchedulingEnabled(enabled bool) ServiceOption

WithSchedulingEnabled toggles scheduling-related workflows.

func WithTranslationsEnabled added in v0.2.0

func WithTranslationsEnabled(enabled bool) ServiceOption

WithTranslationsEnabled toggles translation handling.

func WithVersionRetentionLimit

func WithVersionRetentionLimit(limit int) ServiceOption

WithVersionRetentionLimit constrains how many versions are retained per content entity.

func WithVersioningEnabled

func WithVersioningEnabled(enabled bool) ServiceOption

WithVersioningEnabled toggles the versioning workflow for the service.

type UpdateContentRequest

type UpdateContentRequest struct {
	ID                       uuid.UUID
	Status                   string
	UpdatedBy                uuid.UUID
	Translations             []ContentTranslationInput
	Metadata                 map[string]any
	AllowMissingTranslations bool
}

UpdateContentRequest captures mutable fields for an existing content entry. Slug and content type remain immutable and are inferred from the existing record.

type UpdateContentTranslationRequest added in v0.2.0

type UpdateContentTranslationRequest struct {
	ContentID uuid.UUID
	Locale    string
	Title     string
	Summary   *string
	Content   map[string]any
	UpdatedBy uuid.UUID
}

UpdateContentTranslationRequest captures the payload required to mutate a single translation.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL