279 lines
8 KiB
Go
279 lines
8 KiB
Go
// File Path: monorepo/cloud/maplepress-backend/internal/repo/page_repo.go
|
|
package repo
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/gocql/gocql"
|
|
"go.uber.org/zap"
|
|
|
|
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/domain/page"
|
|
)
|
|
|
|
type pageRepository struct {
|
|
session *gocql.Session
|
|
logger *zap.Logger
|
|
}
|
|
|
|
func NewPageRepository(session *gocql.Session, logger *zap.Logger) page.Repository {
|
|
return &pageRepository{
|
|
session: session,
|
|
logger: logger.Named("page-repo"),
|
|
}
|
|
}
|
|
|
|
// Create inserts a new page
|
|
func (r *pageRepository) Create(ctx context.Context, p *page.Page) error {
|
|
query := `
|
|
INSERT INTO maplepress.pages_by_site (
|
|
site_id, page_id, tenant_id,
|
|
title, content, excerpt, url,
|
|
status, post_type, author,
|
|
published_at, modified_at, indexed_at,
|
|
meilisearch_doc_id,
|
|
created_at, updated_at,
|
|
created_from_ip_address, created_from_ip_timestamp,
|
|
modified_from_ip_address, modified_from_ip_timestamp
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
`
|
|
|
|
return r.session.Query(query,
|
|
p.SiteID, p.PageID, p.TenantID,
|
|
p.Title, p.Content, p.Excerpt, p.URL,
|
|
p.Status, p.PostType, p.Author,
|
|
p.PublishedAt, p.ModifiedAt, p.IndexedAt,
|
|
p.MeilisearchDocID,
|
|
p.CreatedAt, p.UpdatedAt,
|
|
p.CreatedFromIPAddress, p.CreatedFromIPTimestamp,
|
|
p.ModifiedFromIPAddress, p.ModifiedFromIPTimestamp,
|
|
).WithContext(ctx).Exec()
|
|
}
|
|
|
|
// Update updates an existing page
|
|
func (r *pageRepository) Update(ctx context.Context, p *page.Page) error {
|
|
query := `
|
|
UPDATE maplepress.pages_by_site SET
|
|
title = ?,
|
|
content = ?,
|
|
excerpt = ?,
|
|
url = ?,
|
|
status = ?,
|
|
post_type = ?,
|
|
author = ?,
|
|
published_at = ?,
|
|
modified_at = ?,
|
|
indexed_at = ?,
|
|
meilisearch_doc_id = ?,
|
|
updated_at = ?,
|
|
modified_from_ip_address = ?,
|
|
modified_from_ip_timestamp = ?
|
|
WHERE site_id = ? AND page_id = ?
|
|
`
|
|
|
|
return r.session.Query(query,
|
|
p.Title, p.Content, p.Excerpt, p.URL,
|
|
p.Status, p.PostType, p.Author,
|
|
p.PublishedAt, p.ModifiedAt, p.IndexedAt,
|
|
p.MeilisearchDocID,
|
|
p.UpdatedAt,
|
|
p.ModifiedFromIPAddress, p.ModifiedFromIPTimestamp,
|
|
p.SiteID, p.PageID,
|
|
).WithContext(ctx).Exec()
|
|
}
|
|
|
|
// Upsert creates or updates a page
|
|
func (r *pageRepository) Upsert(ctx context.Context, p *page.Page) error {
|
|
// In Cassandra, INSERT acts as an upsert
|
|
return r.Create(ctx, p)
|
|
}
|
|
|
|
// GetByID retrieves a page by site_id and page_id
|
|
func (r *pageRepository) GetByID(ctx context.Context, siteID gocql.UUID, pageID string) (*page.Page, error) {
|
|
query := `
|
|
SELECT site_id, page_id, tenant_id,
|
|
title, content, excerpt, url,
|
|
status, post_type, author,
|
|
published_at, modified_at, indexed_at,
|
|
meilisearch_doc_id,
|
|
created_at, updated_at,
|
|
created_from_ip_address, created_from_ip_timestamp,
|
|
modified_from_ip_address, modified_from_ip_timestamp
|
|
FROM maplepress.pages_by_site
|
|
WHERE site_id = ? AND page_id = ?
|
|
`
|
|
|
|
p := &page.Page{}
|
|
err := r.session.Query(query, siteID, pageID).
|
|
WithContext(ctx).
|
|
Scan(
|
|
&p.SiteID, &p.PageID, &p.TenantID,
|
|
&p.Title, &p.Content, &p.Excerpt, &p.URL,
|
|
&p.Status, &p.PostType, &p.Author,
|
|
&p.PublishedAt, &p.ModifiedAt, &p.IndexedAt,
|
|
&p.MeilisearchDocID,
|
|
&p.CreatedAt, &p.UpdatedAt,
|
|
&p.CreatedFromIPAddress, &p.CreatedFromIPTimestamp,
|
|
&p.ModifiedFromIPAddress, &p.ModifiedFromIPTimestamp,
|
|
)
|
|
|
|
if err == gocql.ErrNotFound {
|
|
return nil, fmt.Errorf("page not found: site_id=%s, page_id=%s", siteID, pageID)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get page: %w", err)
|
|
}
|
|
|
|
return p, nil
|
|
}
|
|
|
|
// GetBySiteID retrieves all pages for a site
|
|
func (r *pageRepository) GetBySiteID(ctx context.Context, siteID gocql.UUID) ([]*page.Page, error) {
|
|
query := `
|
|
SELECT site_id, page_id, tenant_id,
|
|
title, content, excerpt, url,
|
|
status, post_type, author,
|
|
published_at, modified_at, indexed_at,
|
|
meilisearch_doc_id,
|
|
created_at, updated_at,
|
|
created_from_ip_address, created_from_ip_timestamp,
|
|
modified_from_ip_address, modified_from_ip_timestamp
|
|
FROM maplepress.pages_by_site
|
|
WHERE site_id = ?
|
|
`
|
|
|
|
iter := r.session.Query(query, siteID).WithContext(ctx).Iter()
|
|
defer iter.Close()
|
|
|
|
var pages []*page.Page
|
|
p := &page.Page{}
|
|
|
|
for iter.Scan(
|
|
&p.SiteID, &p.PageID, &p.TenantID,
|
|
&p.Title, &p.Content, &p.Excerpt, &p.URL,
|
|
&p.Status, &p.PostType, &p.Author,
|
|
&p.PublishedAt, &p.ModifiedAt, &p.IndexedAt,
|
|
&p.MeilisearchDocID,
|
|
&p.CreatedAt, &p.UpdatedAt,
|
|
&p.CreatedFromIPAddress, &p.CreatedFromIPTimestamp,
|
|
&p.ModifiedFromIPAddress, &p.ModifiedFromIPTimestamp,
|
|
) {
|
|
pages = append(pages, p)
|
|
p = &page.Page{} // Create new instance for next iteration
|
|
}
|
|
|
|
if err := iter.Close(); err != nil {
|
|
return nil, fmt.Errorf("failed to iterate pages: %w", err)
|
|
}
|
|
|
|
return pages, nil
|
|
}
|
|
|
|
// GetBySiteIDPaginated retrieves pages for a site with pagination
|
|
func (r *pageRepository) GetBySiteIDPaginated(ctx context.Context, siteID gocql.UUID, limit int, pageState []byte) ([]*page.Page, []byte, error) {
|
|
query := `
|
|
SELECT site_id, page_id, tenant_id,
|
|
title, content, excerpt, url,
|
|
status, post_type, author,
|
|
published_at, modified_at, indexed_at,
|
|
meilisearch_doc_id,
|
|
created_at, updated_at,
|
|
created_from_ip_address, created_from_ip_timestamp,
|
|
modified_from_ip_address, modified_from_ip_timestamp
|
|
FROM maplepress.pages_by_site
|
|
WHERE site_id = ?
|
|
`
|
|
|
|
q := r.session.Query(query, siteID).WithContext(ctx).PageSize(limit)
|
|
|
|
if len(pageState) > 0 {
|
|
q = q.PageState(pageState)
|
|
}
|
|
|
|
iter := q.Iter()
|
|
defer iter.Close()
|
|
|
|
var pages []*page.Page
|
|
p := &page.Page{}
|
|
|
|
for iter.Scan(
|
|
&p.SiteID, &p.PageID, &p.TenantID,
|
|
&p.Title, &p.Content, &p.Excerpt, &p.URL,
|
|
&p.Status, &p.PostType, &p.Author,
|
|
&p.PublishedAt, &p.ModifiedAt, &p.IndexedAt,
|
|
&p.MeilisearchDocID,
|
|
&p.CreatedAt, &p.UpdatedAt,
|
|
&p.CreatedFromIPAddress, &p.CreatedFromIPTimestamp,
|
|
&p.ModifiedFromIPAddress, &p.ModifiedFromIPTimestamp,
|
|
) {
|
|
pages = append(pages, p)
|
|
p = &page.Page{} // Create new instance for next iteration
|
|
}
|
|
|
|
if err := iter.Close(); err != nil {
|
|
return nil, nil, fmt.Errorf("failed to iterate pages: %w", err)
|
|
}
|
|
|
|
nextPageState := iter.PageState()
|
|
return pages, nextPageState, nil
|
|
}
|
|
|
|
// Delete deletes a page
|
|
func (r *pageRepository) Delete(ctx context.Context, siteID gocql.UUID, pageID string) error {
|
|
query := `DELETE FROM maplepress.pages_by_site WHERE site_id = ? AND page_id = ?`
|
|
return r.session.Query(query, siteID, pageID).WithContext(ctx).Exec()
|
|
}
|
|
|
|
// DeleteBySiteID deletes all pages for a site
|
|
func (r *pageRepository) DeleteBySiteID(ctx context.Context, siteID gocql.UUID) error {
|
|
// Note: This is an expensive operation in Cassandra
|
|
// Better to delete partition by partition if possible
|
|
query := `DELETE FROM maplepress.pages_by_site WHERE site_id = ?`
|
|
return r.session.Query(query, siteID).WithContext(ctx).Exec()
|
|
}
|
|
|
|
// DeleteMultiple deletes multiple pages by their IDs
|
|
func (r *pageRepository) DeleteMultiple(ctx context.Context, siteID gocql.UUID, pageIDs []string) error {
|
|
// Use batch for efficiency
|
|
batch := r.session.NewBatch(gocql.LoggedBatch).WithContext(ctx)
|
|
|
|
query := `DELETE FROM maplepress.pages_by_site WHERE site_id = ? AND page_id = ?`
|
|
|
|
for _, pageID := range pageIDs {
|
|
batch.Query(query, siteID, pageID)
|
|
}
|
|
|
|
return r.session.ExecuteBatch(batch)
|
|
}
|
|
|
|
// CountBySiteID counts pages for a site
|
|
func (r *pageRepository) CountBySiteID(ctx context.Context, siteID gocql.UUID) (int64, error) {
|
|
query := `SELECT COUNT(*) FROM maplepress.pages_by_site WHERE site_id = ?`
|
|
|
|
var count int64
|
|
err := r.session.Query(query, siteID).WithContext(ctx).Scan(&count)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to count pages: %w", err)
|
|
}
|
|
|
|
return count, nil
|
|
}
|
|
|
|
// Exists checks if a page exists
|
|
func (r *pageRepository) Exists(ctx context.Context, siteID gocql.UUID, pageID string) (bool, error) {
|
|
query := `SELECT page_id FROM maplepress.pages_by_site WHERE site_id = ? AND page_id = ?`
|
|
|
|
var id string
|
|
err := r.session.Query(query, siteID, pageID).WithContext(ctx).Scan(&id)
|
|
|
|
if err == gocql.ErrNotFound {
|
|
return false, nil
|
|
}
|
|
|
|
if err != nil {
|
|
return false, fmt.Errorf("failed to check page existence: %w", err)
|
|
}
|
|
|
|
return true, nil
|
|
}
|