Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
187
cloud/maplepress-backend/internal/domain/site/site.go
Normal file
187
cloud/maplepress-backend/internal/domain/site/site.go
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
// File Path: monorepo/cloud/maplepress-backend/internal/domain/site/site.go
|
||||
package site
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gocql/gocql"
|
||||
)
|
||||
|
||||
// Site represents a WordPress site registered in the system
|
||||
type Site struct {
|
||||
// Core Identity
|
||||
ID gocql.UUID `json:"id"`
|
||||
TenantID gocql.UUID `json:"tenant_id"`
|
||||
|
||||
// Site Information
|
||||
SiteURL string `json:"site_url"` // Full URL: https://example.com
|
||||
Domain string `json:"domain"` // Extracted: example.com
|
||||
|
||||
// Authentication
|
||||
APIKeyHash string `json:"-"` // SHA-256 hash, never exposed in JSON
|
||||
APIKeyPrefix string `json:"api_key_prefix"` // "live_sk_a1b2" for display
|
||||
APIKeyLastFour string `json:"api_key_last_four"` // Last 4 chars for display
|
||||
|
||||
// Status & Verification
|
||||
Status string `json:"status"` // active, inactive, pending, suspended, archived
|
||||
IsVerified bool `json:"is_verified"`
|
||||
VerificationToken string `json:"-"` // Never exposed
|
||||
|
||||
// Search & Indexing
|
||||
SearchIndexName string `json:"search_index_name"`
|
||||
TotalPagesIndexed int64 `json:"total_pages_indexed"` // All-time total for stats
|
||||
LastIndexedAt time.Time `json:"last_indexed_at,omitempty"`
|
||||
|
||||
// Plugin Info
|
||||
PluginVersion string `json:"plugin_version,omitempty"`
|
||||
|
||||
// Usage Tracking (for billing) - no quotas/limits
|
||||
StorageUsedBytes int64 `json:"storage_used_bytes"` // Current storage usage
|
||||
SearchRequestsCount int64 `json:"search_requests_count"` // Monthly search count
|
||||
MonthlyPagesIndexed int64 `json:"monthly_pages_indexed"` // Monthly indexing count
|
||||
LastResetAt time.Time `json:"last_reset_at"` // Last monthly reset
|
||||
|
||||
// Metadata (optional fields)
|
||||
Language string `json:"language,omitempty"` // ISO 639-1
|
||||
Timezone string `json:"timezone,omitempty"` // IANA timezone
|
||||
Notes string `json:"notes,omitempty"`
|
||||
|
||||
// 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 (
|
||||
StatusPending = "pending" // Site created, awaiting verification
|
||||
StatusActive = "active" // Site verified and operational
|
||||
StatusInactive = "inactive" // User temporarily disabled
|
||||
StatusSuspended = "suspended" // Suspended due to violation or non-payment
|
||||
StatusArchived = "archived" // Soft deleted
|
||||
)
|
||||
|
||||
|
||||
// NewSite creates a new Site entity with defaults
|
||||
func NewSite(tenantID gocql.UUID, domain, siteURL string, apiKeyHash, apiKeyPrefix, apiKeyLastFour string, encryptedIP string) *Site {
|
||||
now := time.Now()
|
||||
siteID := gocql.TimeUUID()
|
||||
|
||||
return &Site{
|
||||
ID: siteID,
|
||||
TenantID: tenantID,
|
||||
Domain: domain,
|
||||
SiteURL: siteURL,
|
||||
APIKeyHash: apiKeyHash,
|
||||
APIKeyPrefix: apiKeyPrefix,
|
||||
APIKeyLastFour: apiKeyLastFour,
|
||||
Status: StatusPending,
|
||||
IsVerified: false,
|
||||
VerificationToken: "", // Set by caller
|
||||
SearchIndexName: "site_" + siteID.String(),
|
||||
TotalPagesIndexed: 0,
|
||||
PluginVersion: "",
|
||||
|
||||
// Usage tracking (no quotas/limits)
|
||||
StorageUsedBytes: 0,
|
||||
SearchRequestsCount: 0,
|
||||
MonthlyPagesIndexed: 0,
|
||||
LastResetAt: now,
|
||||
|
||||
Language: "",
|
||||
Timezone: "",
|
||||
Notes: "",
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
|
||||
// CWE-359: Encrypted IP address tracking for GDPR compliance
|
||||
CreatedFromIPAddress: encryptedIP,
|
||||
CreatedFromIPTimestamp: now,
|
||||
ModifiedFromIPAddress: encryptedIP,
|
||||
ModifiedFromIPTimestamp: now,
|
||||
}
|
||||
}
|
||||
|
||||
// IsActive checks if the site is active and verified
|
||||
func (s *Site) IsActive() bool {
|
||||
return s.Status == StatusActive && s.IsVerified
|
||||
}
|
||||
|
||||
// IsTestMode checks if the site is using a test API key
|
||||
func (s *Site) IsTestMode() bool {
|
||||
// Check if API key prefix starts with "test_sk_"
|
||||
return len(s.APIKeyPrefix) >= 7 && s.APIKeyPrefix[:7] == "test_sk"
|
||||
}
|
||||
|
||||
// RequiresVerification checks if the site requires verification
|
||||
// Test mode sites skip verification for development
|
||||
func (s *Site) RequiresVerification() bool {
|
||||
return !s.IsTestMode()
|
||||
}
|
||||
|
||||
// CanAccessAPI checks if the site can access the API
|
||||
// More lenient than IsActive - allows pending sites for initial setup
|
||||
func (s *Site) CanAccessAPI() bool {
|
||||
// Allow active sites (fully verified)
|
||||
if s.Status == StatusActive {
|
||||
return true
|
||||
}
|
||||
// Allow pending sites (waiting for verification) for initial setup
|
||||
if s.Status == StatusPending {
|
||||
return true
|
||||
}
|
||||
// Block inactive, suspended, or archived sites
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// IncrementSearchCount increments the search request counter
|
||||
func (s *Site) IncrementSearchCount() {
|
||||
s.SearchRequestsCount++
|
||||
s.UpdatedAt = time.Now()
|
||||
}
|
||||
|
||||
// IncrementPageCount increments the indexed page counter (lifetime total)
|
||||
func (s *Site) IncrementPageCount() {
|
||||
s.TotalPagesIndexed++
|
||||
s.UpdatedAt = time.Now()
|
||||
}
|
||||
|
||||
// IncrementMonthlyPageCount increments both lifetime and monthly page counters
|
||||
func (s *Site) IncrementMonthlyPageCount(count int64) {
|
||||
s.TotalPagesIndexed += count
|
||||
s.MonthlyPagesIndexed += count
|
||||
s.LastIndexedAt = time.Now()
|
||||
s.UpdatedAt = time.Now()
|
||||
}
|
||||
|
||||
// UpdateStorageUsed updates the storage usage
|
||||
func (s *Site) UpdateStorageUsed(bytes int64) {
|
||||
s.StorageUsedBytes = bytes
|
||||
s.UpdatedAt = time.Now()
|
||||
}
|
||||
|
||||
// Verify marks the site as verified
|
||||
func (s *Site) Verify() {
|
||||
s.IsVerified = true
|
||||
s.Status = StatusActive
|
||||
s.VerificationToken = "" // Clear token after verification
|
||||
s.UpdatedAt = time.Now()
|
||||
}
|
||||
|
||||
// ResetMonthlyUsage resets monthly usage counters for billing cycles
|
||||
func (s *Site) ResetMonthlyUsage() {
|
||||
now := time.Now()
|
||||
|
||||
// Reset usage counters (no quotas)
|
||||
s.SearchRequestsCount = 0
|
||||
s.MonthlyPagesIndexed = 0
|
||||
s.LastResetAt = now
|
||||
|
||||
s.UpdatedAt = now
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue