132 lines
3.7 KiB
Go
132 lines
3.7 KiB
Go
// File Path: monorepo/cloud/maplepress-backend/internal/domain/page/page.go
|
|
package page
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/gocql/gocql"
|
|
)
|
|
|
|
// Page represents a WordPress page/post indexed in the system
|
|
type Page struct {
|
|
// Identity
|
|
SiteID gocql.UUID `json:"site_id"` // Partition key
|
|
PageID string `json:"page_id"` // Clustering key (WordPress page ID)
|
|
TenantID gocql.UUID `json:"tenant_id"` // For additional isolation
|
|
|
|
// Content
|
|
Title string `json:"title"`
|
|
Content string `json:"content"` // HTML stripped
|
|
Excerpt string `json:"excerpt"` // Summary
|
|
|
|
URL string `json:"url"` // Canonical URL
|
|
|
|
// Metadata
|
|
Status string `json:"status"` // publish, draft, trash
|
|
PostType string `json:"post_type"` // page, post
|
|
Author string `json:"author"`
|
|
|
|
// Timestamps
|
|
PublishedAt time.Time `json:"published_at"`
|
|
ModifiedAt time.Time `json:"modified_at"`
|
|
IndexedAt time.Time `json:"indexed_at"` // When we indexed it
|
|
|
|
// Search
|
|
MeilisearchDocID string `json:"meilisearch_doc_id"` // ID in Meilisearch index
|
|
|
|
// Audit
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
|
|
// CWE-359: IP address tracking for GDPR compliance (90-day expiration)
|
|
CreatedFromIPAddress string `json:"-"` // Encrypted IP address, never exposed in JSON
|
|
CreatedFromIPTimestamp time.Time `json:"-"` // For 90-day expiration tracking
|
|
ModifiedFromIPAddress string `json:"-"` // Encrypted IP address, never exposed in JSON
|
|
ModifiedFromIPTimestamp time.Time `json:"-"` // For 90-day expiration tracking
|
|
}
|
|
|
|
// Status constants
|
|
const (
|
|
StatusPublish = "publish"
|
|
StatusDraft = "draft"
|
|
StatusTrash = "trash"
|
|
)
|
|
|
|
// PostType constants
|
|
const (
|
|
PostTypePage = "page"
|
|
PostTypePost = "post"
|
|
)
|
|
|
|
// NewPage creates a new Page entity
|
|
func NewPage(siteID, tenantID gocql.UUID, pageID string, title, content, excerpt, url, status, postType, author string, publishedAt, modifiedAt time.Time, encryptedIP string) *Page {
|
|
now := time.Now()
|
|
|
|
return &Page{
|
|
SiteID: siteID,
|
|
PageID: pageID,
|
|
TenantID: tenantID,
|
|
Title: title,
|
|
Content: content,
|
|
Excerpt: excerpt,
|
|
URL: url,
|
|
Status: status,
|
|
PostType: postType,
|
|
Author: author,
|
|
PublishedAt: publishedAt,
|
|
ModifiedAt: modifiedAt,
|
|
IndexedAt: now,
|
|
MeilisearchDocID: "", // Set after indexing in Meilisearch
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
// CWE-359: Encrypted IP address tracking for GDPR compliance
|
|
CreatedFromIPAddress: encryptedIP,
|
|
CreatedFromIPTimestamp: now,
|
|
ModifiedFromIPAddress: encryptedIP,
|
|
ModifiedFromIPTimestamp: now,
|
|
}
|
|
}
|
|
|
|
// IsPublished checks if the page is published
|
|
func (p *Page) IsPublished() bool {
|
|
return p.Status == StatusPublish
|
|
}
|
|
|
|
// ShouldIndex checks if the page should be indexed in search
|
|
func (p *Page) ShouldIndex() bool {
|
|
// Only index published pages
|
|
return p.IsPublished()
|
|
}
|
|
|
|
// GetMeilisearchID returns the Meilisearch document ID
|
|
func (p *Page) GetMeilisearchID() string {
|
|
if p.MeilisearchDocID != "" {
|
|
return p.MeilisearchDocID
|
|
}
|
|
// Use page_id as fallback
|
|
return p.PageID
|
|
}
|
|
|
|
// SetMeilisearchID sets the Meilisearch document ID
|
|
func (p *Page) SetMeilisearchID(docID string) {
|
|
p.MeilisearchDocID = docID
|
|
p.UpdatedAt = time.Now()
|
|
}
|
|
|
|
// MarkIndexed updates the indexed timestamp
|
|
func (p *Page) MarkIndexed() {
|
|
p.IndexedAt = time.Now()
|
|
p.UpdatedAt = time.Now()
|
|
}
|
|
|
|
// Update updates the page content
|
|
func (p *Page) Update(title, content, excerpt, url, status, author string, modifiedAt time.Time) {
|
|
p.Title = title
|
|
p.Content = content
|
|
p.Excerpt = excerpt
|
|
p.URL = url
|
|
p.Status = status
|
|
p.Author = author
|
|
p.ModifiedAt = modifiedAt
|
|
p.UpdatedAt = time.Now()
|
|
}
|